Reputation: 3898
I'm struggling to reduce a list :
Let's say I have a List<Item> listItems
with an Item
class defined such as :
public class Item {
private String effect;
private String value;
public String getEffect() {
return effect;
}
public void setEffect(String effect) {
this.effect = effect;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
Now, in my listItems
list, I have some elements with the same effect
property.
I want to remove all element from my listItems
list with the same effect
property except the one with the higher value
property. (value
is number represented as a String
).
Also, I wanna keep all the element with an unique effect
property.
How can I achieve that ? I guess I'm gonna have to deal with Comparator
.
Upvotes: 2
Views: 906
Reputation: 34460
It seems that you want to group the items of your list by effect
, using max(value)
as an aggregation. You can do this as follows:
Map<String, Item> byEffectHavingMaxValue = listItems.stream()
.collect(Collectors.toMap(
Item::getEffect, // group by effect
Function.identity(), // keep items as values
BinaryOperator.maxBy( // when effect exists, keep the item with max value
Comparator.comparingInt(i -> Integer.parseInt(i.getValue())))));
Collection<Item> result = byEffectHavingMaxValue.values();
The solution above collects elements of the stream to a Map
. For this, it uses an overload of Collectors.toMap
that requires 3 arguments:
Function
that transforms elements of the stream to keysFunction
that transforms elements of the stream to valuesBinaryOperator
that is used to resolve collisions between values associated with the same keyIn this case, the Item::getEffect
method reference is being used as the keyMapper function, which transforms an Item
instance to its effect
. Then, Function.identity()
is being used as the valueMapper function, which does nothing, i.e. it leaves each Item
instance untouched. Finally, BinaryOperator.maxBy(Comparator.comparingInt(i -> Integer.parseInt(i.getValue())))
is being used as the mergeFunction
, which is a BinaryOperator
that receives two values of the map that have the same key (i.e. two Item
instances with the same effect
), and resolves the collision in the map by selecting the Item
instance that has the max value
(value
is first converted to Integer
, so that we compare numeric values instead of strings).
Upvotes: 4
Reputation: 18792
"I wanna keep all the element with an unique effect property": assuming all Item
objects have valid effect
and value
:
//group list entries to sub - lists having the same effect value
Map<String, List<Item>> map = itemList.stream()
.collect(Collectors.groupingBy(Item::getEffect)) ;
List<Item> uniqueEfectMaxValue = new ArrayList<>();
//get item having max value and add to uniqueEfectMaxValue
map.forEach((key,list)->{
Item item = list.stream().
collect(Collectors.maxBy(Comparator.
comparing(Item::getValue))).get();
uniqueEfectMaxValue.add(item);
}
);
Upvotes: 1
Reputation: 1239
Add elements to list like shown below
List<Item> itemList = new ArrayList<>();
itemList.add(new Item("e1", "10"));
itemList.add(new Item("e1", "20"));
itemList.add(new Item("e1", "30"));
itemList.add(new Item("e2", "10"));
itemList.add(new Item("e3", "10"));
itemList.add(new Item("e3", "-10"));
Logic to eliminate duplicates and keep items with highest value
Map<String, Item> effectMap = new HashMap<>();
for (Item item : itemList) {
Item itemWithHigherValue = effectMap.get(item.getEffect());
if (itemWithHigherValue == null ||
(Integer.parseInt(item.getValue()) >
Integer.parseInt(itemWithHigherValue.getValue())))
{
effectMap.put(item.getEffect(), item);
}
}
Printing the results
List<Item> uniqueItemList = new ArrayList<Item>(effectMap.values());
for (Item item : uniqueItemList) {
System.out.println(item);
}
Upvotes: 1