Convert a List of objects to a Map using Streams - java

I have a list of objects of class A:
List<A> list;
class A {
String name;
String lastname;
//Getter and Setter methods
}
I want to convert this list to a map from name to a set of lastnames:
Map<String, Set<String>> map;
For example, for the following list:
John Archer, John Agate, Tom Keinanen, Tom Barren, Cindy King
The map would be:
John -> {Archer, Agate}, Tom -> {Keinanen, Barren}, Cindy -> {King}
I tried the following code, but it returns a map from name to objects of class A:
list.stream.collect(groupingBy(A::getFirstName, toSet()));

Map< String, Set<String>> map = list.stream()
.collect(
Collectors.groupingBy(
A::getFirstName, Collectors.mapping(
A::getLastName, Collectors.toSet())));
You were on the right track you need to use:
Collectors.groupingBy to group by the firstName.
And then use a downstream collector like Collectors.mappping as a second parameter of Collectors.groupingBy to map to the lastName .
And then finally collect that in a Set<String> by invoking Collectors.toSet:

You never told the collector to extract last names.
I suppose you need something like
list.stream
.collect(groupingBy(
A::getFirstName, // The key is extracted.
mapping( // Map the stream of grouped values.
A::getLastName, // Extract last names.
toSet() // Collect them into a set.
)));

Related

Return city in which most people live - stream()

I have a class called Person (name,surname,city,age) and I added to it persons.
I have to find the city that lives the most people - in my case is "Meerdonk". I tried using stream(), but I cannot figure out how.
This is my code:
public static Optional<Person> getMostPopulateCity(List<Person> personList) {
return personList.stream()
.filter(person -> person.getCity()
// here
.max(Comparator.comparing(Person::getCity));
}
At // here, I don't know what I should do to get my most populated city, and if max. is OK to be used, as I want to get the max (most populated city).
Can someone explain me please what I should use to get out the most populated city? Or just to let me know what I have wrong?
You can use Collectors.groupingBy to group persons by city, then extract the map entry with the most people like so (assuming cities are strings):
return personList.stream()
.collect(Collectors.groupingBy(Person::getCity)) // Map<String, List<Person>>
.entrySet().stream()
.max(Comparator.comparing(e -> e.getValue().size())) // Optional<Map.Entry<String, List<Person>>
.map(Entry::getKey);
As suggested by #Thomas above, use a grouping collector with counting collector to collect the number of persons by city and then look for the city with the higest count value:
Optional<String> result = persons.stream()
.collect(Collectors.groupingBy(Person::getCity, Collectors.counting())) // Map<String, Long>: Key -> city, Value -> count of persons with such city
.entrySet().stream()
.max(Map.Entry.comparingByValue()) // looking for highest count value
.map(Map.Entry::getKey);

Java 8 Need advice with stream

I have a List:
class DummyClass {
List<String> rname;
String name;
}
The values in my List look like this:
list.add(DummyClass(Array.asList("a","b"),"apple"))
list.add(DummyClass(Array.asList("a","b"),"banana"))
list.add(DummyClass(Array.asList("a","c"),"orange"))
list.add(DummyClass(null,"apple"))
I want to convert the above List into a Map<String, Set>, where key is rname and value is Set of name field.
{
"a"-> ["apple", "orange", "banana"],
"b"-> ["apple", "banana"]
"c" -> ["orange"]
}
I am trying to use java stream and facing null pointer exception . Can someone please guide
Map<String, Set<String>> map =
list.stream()
.collect(Collectors.groupingBy(DummyClass::rname,
Collectors.mapping(DummyClass::getName,
Collectors.toSet())));
I am not able to process {(Array.asList("a","b"))}each element of list in stream.
There is some flaw here :
Collectors.groupingBy(DummyClass::rname,
Collectors.mapping(DummyClass::getName,
Collectors.toSet())))
where I am processing the entire list together, rather than each element . Shall I use another stream
You need to do a filter - many of the util classes to construct collections no longer allow null e.g. Map.of or the groupingBy you have above.
You can filter or first map, replace null with a string and then group by.
Map<String, Set<String>> map =
list.stream().filter(v-> v.getName() != null)
.collect(Collectors.groupingBy(DummyClass::rname,
Collectors.mapping(DummyClass::getName,
Collectors.toSet())));
Or if you don't want to drop null values, do a map and produce a key that all null names can be grouped under something like:
Map<String, Set<String>> map =
list.stream().map(v-> Map.entry(v.getName() == null? "null": v.getName(), v))
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getKey,
Collectors.toSet())));
The groupingBy that I have above needs to be changed as it now has a Map.Entry rather than your desired type.
I'm writing this on a mobile...without an editor so will leave that part to you :)

How to convert a nested map to a list<Object>

How to convert a nested map to a list:
the map is:
Map<Integer, Map<Integer, Map<String, Double>>> list
the Object class is:
public class employee {
private Integer id;
private Integer number;
private String name;
private Double salary;
How to convert the nested map to the List?
Iterate over the map entries. For each inner map, also iterate over its entries, etc. For each entry in the innermost map, create an Employee and add it to your list.
The standard way to iterate over a map is to iterate over its entry set. vefthym’s answer shows you how to do this with a for loop. You may eloborate that code into what you need.
You may also do it with streams, provided you can use Java 8. I am assuming that your outer map maps from ID to an intermediate map (I would expect that intermediate map to hold exactly one entry; but my code will also work with more or fewer). The next map maps from number to a map from name to salary.
List<Employee> empls = list.entrySet()
.stream()
.flatMap(oe -> oe.getValue()
.entrySet()
.stream()
.flatMap((Map.Entry<Integer, Map<String, Double>> me) -> me.getValue()
.entrySet()
.stream()
.map((Map.Entry<String, Double> ie)
-> new Employee(oe.getKey(), me.getKey(), ie.getKey(), ie.getValue()))))
.collect(Collectors.toList());
That was meant to be oe for outer entry, that is, entry in the outer map. Similarly me for middle entry and ie for inner entry. I have renamed your class to begin with a capital E to follow Java naming conventions, and I have assumed a convenient constructor.
EDIT: vefthym, where did your answer go now that I was referring to it? I know you were not too happy about it yourself, it’s fair enough. In any case, the standard way to iterate over a map with a for loop is:
for (Map.Entry<Integer, String> currentEntry : yourMap.entrySet()) {
// do your stuff here
// use currentEntry.getKey() and currentEntry.getValue() to get the key and value from the current entry
}
You need to repeat the type arguments from your map declaration in the <> after Map.Entry.

groupingBy returns Object for key as Map when it should use String

Let's say I have a list of Brand objects. The POJO contains a getName() that returns a string. I want to build a
Map<String, Brand>
out of this with the String being the name... but I want the key to be case insensitive.
How do I make this work using Java streams? Trying:
brands.stream().collect(Collectors.groupingBy(brand -> brand.getName().toLowerCase()));
doesn't work, which I think is because I'm not using groupBy correctly.
Collect the results into a case insensitive map
Map<String, Brand> map = brands
.stream()
.collect(
Collectors.toMap(
Brand::getName, // the key
Function.identity(), // the value
(first, second) -> first, // how to handle duplicates
() -> new TreeMap<String, Brand>(String.CASE_INSENSITIVE_ORDER))); // supply the map implementation
Collectors#groupBy won't work here because it returns a Map<KeyType, List<ValueType>>, but you don't want a List as a value, you just want a Brand, from what I've understood.

Java 8 streams: iterate over Map of Lists

I have the following Object and a Map:
MyObject
String name;
Long priority;
foo bar;
Map<String, List<MyObject>> anotherHashMap;
I want to convert the Map in another Map. The Key of the result map is the key of the input map. The value of the result map ist the Property "name" of My object, ordered by priority.
The ordering and extracting the name is not the problem, but I could not put it into the result map. I do it the old Java 7 way, but it would be nice it is possible to use the streaming API.
Map<String, List<String>> result = new HashMap<>();
for (String identifier : anotherHashMap.keySet()) {
List<String> generatedList = anotherHashMap.get(identifier).stream()...;
teaserPerPage.put(identifier, generatedList);
}
Has anyone an idea? I tried this, but got stuck:
anotherHashMap.entrySet().stream().collect(Collectors.asMap(..., ...));
Map<String, List<String>> result = anotherHashMap
.entrySet().stream() // Stream over entry set
.collect(Collectors.toMap( // Collect final result map
Map.Entry::getKey, // Key mapping is the same
e -> e.getValue().stream() // Stream over list
.sorted(Comparator.comparingLong(MyObject::getPriority)) // Sort by priority
.map(MyObject::getName) // Apply mapping to MyObject
.collect(Collectors.toList())) // Collect mapping into list
);
Essentially, you stream over each entry set and collect it into a new map. To compute the value in the new map, you stream over the List<MyOjbect> from the old map, sort, and apply a mapping and collection function to it. In this case I used MyObject::getName as the mapping and collected the resulting names into a list.
For generating another map, we can have something like following:
HashMap<String, List<String>> result = anotherHashMap.entrySet().stream().collect(Collectors.toMap(elem -> elem.getKey(), elem -> elem.getValue() // can further process it);
Above I am recreating the map again, but you can process the key or the value according to your needs.
Map<String, List<String>> result = anotherHashMap.entrySet().stream().collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().stream()
.sorted(comparing(MyObject::getPriority))
.map(MyObject::getName)
.collect(Collectors.toList())));
Similar to answer of Mike Kobit, but sorting is applied in the correct place (i.e. value is sorted, not map entries) and more concise static method Comparator.comparing is used to get Comparator for sorting.

Categories