How to convert a list of objects containing other objects into HashMap - java-8

hope you are doing well.
I have some list of other object which again contains two different objects. I have tried couple of ways using Collectors.groupingBy() but didn't get expected output. So help me to figure out solution to this.
Suppose I following three entities:
class FruitFlower {
Fruit fruit;
Flower flower;
}
class Fruit {
String name;
}
class Flower {
String name;
}
And I have a list like this
List<FruitFlower> fruitFlowers = new ArrayList<>();
fruitFlowers.addAll(
new FruitFlower(
new Fruit("Apple"),
new Flower("Rose")
),
new FruitFlower(
new Fruit("Banana"),
new Flower("Lily")
),
new FruitFlower(
new Fruit("Apple"),
new Flower("Sunflower")
),
new FruitFlower(
new Fruit("Banana"),
new Flower("Purple")
),
new FruitFlower(
new Fruit("Banana"),
new Flower("Rose")
)
);
Now, I want to order this list by some filter such that it returns HashMap like this
HashMap<Fruit,List<Flower>> hashMap=new HashMap<>();
Resultant Object:
{
"Apple":["Rose","Sunflower"]
"Banana":["Lily","Purple","Rose"]
}
Where
Expected output is a HadhMap that contains only unique values and a list of other values connected to that particular object. You can consider Objects of String for simplicity.
This is Java question and expected answer should be in Java 8 using stream API.
Thank you in advance.

As you suspected, groupingBy is the right approach.
fruitFlowers.stream().collect(Collectors.groupingBy(
i -> i.fruit,
Collectors.mapping(i -> i.flower, Collectors.toList())
));
The first parameter to groupingBy defines how you want to group them. In this case, we want to group by fruits.
The second parameter describes what the value of the resulting map should be. In this case, we want the values to be the list of flowers.
Note that this approach only works if groupingBy can deduce that two fruits are equivalent. This requires that the Fruit class contains an appropriate implementation of equals and hashCode.
If the Fruit class cannot be extended to add those methods, one can change the first lambda to i -> i.fruit.name, but that would make the resulting Map to have String keys instead of Fruit.

Related

How to GroupBy objects from a list by some common catalog of properties in Java 8

I've been struggling with a problem with one of my lists of data because one of the requirements after generating it is to group some of them by some common parameters (more than 1)
What I should get at the end is a map where the value is a list of common objects. For example.
List<Cause> listToGroup = new ArrayList<>();
listToGroup.add(Similar);
listToGroup.add(Common);
listToGroup.add(Similar);
listToGroup.add(Similar);
listToGroup.add(Common);
In a weird way to represent one group (Similar) and the other (Common), those should be separated into two different lists (that list is generated by a request to other methods, in that case, I just added manually to show what could be the contained data in the list). My main problem is the criteria to group them because is based on a group of parameters that are shared, but not all (if the required parameters are equal, should belong to the same list) In the class shown below, that behaviour is seen because there are some parameters that are not being considered.
public class Cause extends GeneralDomain {
//parameters which must be equals between objects
private Long id;
private Date creationDate;
private Part origin;
private Part destination;
//parameters which are not required to be equal
private BigDecimal value
private Stage stageEvent
//omitted getters and setters
}
I've been seeing the comparator method and the groupingBy method provided in Java 8, but at the moment I just know how to perform that task considering just one parameter (for example grouping them by id) And I have no idea about how to group them using more than one parameter.
//this should be the code if the requirement would be just one parameter to groupby, but in my case are more than one.
Map<Long, List<Cause>> result = request.getList(criteria)
.stream()
.map(p -> parsin.createDto(p))
.collect(groupingBy(Cause ::getId));
I would be really glad for any suggestion. If my explanation is not clear, I'm so sorry. That became so complicated that even is hard for me to explain

Pass comparator into map with collectors [duplicate]

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.

How to find the duplicates values in a Map with in a stream of list?

Say I have this array list:
List<Map<String,String>> fileDataList= new ArrayList<>();
fileDataList.stream().forEach(t->{
//find duplicate map values
});
The list may contain duplicate values like these:
[
{age:12,name:"john"},
{age:11,name:"Mary"},
{age:12,name:"john"}
]
Now,I would like to find the duplicate map values which match both name and age inside the stream without removing them.
I tried with HashSet, But I couldn't understand.
Holger in comments brings a point I missed. If your Map contains only of those name and age properties, you could simply do:
fileDataList.stream()
.distinct()
.collect(Collectors.toList())
And this will be enough. If, on the other hand, you have more properties and
what to filter by only some of them, you could use this utility:
fileDataList.stream()
.filter(distinctByKey(x -> Arrays.asList(x.get("name"), x.get("age")))
.collect(Collectors.toList());
You can transfre your list if Map to list of EntrySet.
List<Map.Entry<String, String>> entries = fileDataList.stream()
.flatMap(e -> e.entrySet().stream())
.collect(toList());

java8 stream - filter map and aggregate

Lets say I have a list of rooms
List<Room> rooms;
And each room has a list of persons.
Using java8 streams I want to iterate the list of rooms, get all persons, execute some method on each node (doSomething()) and get a list of all the filtered objects.
Is this the best practive using java 8?
List<Asset> pesonsList= new ArrayList<>();
for (Room room : rooms)
room.getPersonsList().stream()
.filter(person -> person.isTall())
.forEach(person -> {
doSomething(person);
pesonsList.add(person);
});
You'd probably better do it like this:
List<Person> persons =
rooms.stream()
.flatMap(room -> room.getPersonsList().stream())
.filter(Person::isTall)
.peek(this::doSomething)
.collect(Collectors.toList());
In addition to the #JBNizet answer I'd suggest to replace getPersonsList() in Room class with persons() method: instead of
List<Person> getPersonsList() {...}
Create this method:
Stream<Person> persons() {...}
The first advantage is that streaming operations will become shorter:
List<Person> persons = rooms.stream()
.flatMap(Room::persons)
.filter(Person::isTall)
.peek(this::doSomething)
.collect(Collectors.toList());
The second advantage is that it might be more implementation independent, thus more efficient. Suppose that internally you store persons in the Set or in array. When having getPersonsList() method you will need to copy all the persons to the new list first. When having persons() method, you can easily create a stream directly from your internal data structure whatever it is. I believe, it's Java 8 way to return the stream of internal objects instead of some specific data structure were it List or array or whatever.

Apache Cayenne Get Collection Expression

Using Apache Cayenne I am trying to figure out how to avoid doing iterative calls to the DB when I have a Collection of attribute values.
Say we have a persistent object Person with an attribute name of type String. If I have a List containing names of the Person I would like to generate an expression that allows a single DB call rather than iterating over the list and getting each Person in turn.
This doesn't actually exist but I would like something like this:
List<String> names = ...;
ExpressionFactory.orLikeExp(Person.NAME_PROPERTY, names);
You can use ExpressionFactory.join(..):
List<Expression> pairs = new ArrayList<>(names.size());
for(String name : names) {
// use an expression appropriate for comparison...
// "like" in this example, but can be "equals", etc.
pairs.add(ExpressionFactory.likeExp(Person.NAME_PROPERTY, name));
}
Expression e = ExpressionFactory.joinExp(Expression.OR, pairs);

Resources