Ellone
Ellone

Reputation: 3898

Remove element with duplicate property based on another property from list using java 8

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

Answers (3)

fps
fps

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:

  • keyMapper: a Function that transforms elements of the stream to keys
  • valueMapper: a Function that transforms elements of the stream to values
  • mergeFunction: a BinaryOperator that is used to resolve collisions between values associated with the same key

In 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

c0der
c0der

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

Vinoth A
Vinoth A

Reputation: 1239

  1. As explained by nullpointer, you could have a map with effect as key ,
    and item as value. Please refer the code snippets below

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

Related Questions