Java 8 Stream - operations on List<List<?>> operations on duplicate ids of a list

80 Views Asked by At

I have been trying to figure out a way to do this Hopefuly you'll understand my problem.

So I have a List of Lists of different data types

List<List<?>> a = [["1",100],["2",200],["5",300],["1",10],["2",20],["5",40]]

I need a list out of this list by substracting the integer values of same ID(the first element of every list) for example the result of stream should be

[["1",90],["2",80],["5",240]]

few things to be notes here 1.there will always be only 2 occurrence of every ID(index 0 (string part)). 2.the order should be maintained the id(index 0) found second should be substracted from the first.

is there any way to achieve this using stream kindly let me know

I tried this but it is not working

List<List<?>> b = a.parallelStream().map(each1 -> a.parallelStream().forEach(each2->{each1.got(0).equals(each2.get(0))? true:false;})? Arrays.asList(each2.get(0),each1.get(1)-each1.get(1)):null).collect(Collectors.toList())
4

There are 4 best solutions below

2
Jorn On

As always with this type of question, the imperative approach is much easier to read and reason about:

// LinkedHashMap to preserve insertion order
Map<String, Integer> results = new LinkedHashMap<>(); 
for (List<Object> list : lists) {
  String id = (String) list.get(0);
  int value = (Integer) list.get(1);
  if (results.containsKey(id)) {
    results.put(id, results.get(id) - value);
  } else {
    results.put(id, value);
  }
} 

I don't have a compiler handy so I can't test, but this should get you started.

After completing and understanding this example, you may even be able to turn it into a functional pipeline. But if you want this code to be maintainable, you probably shouldn't.

0
Andy Turner On

The first thing you need to do is to group the list elements by key (the first element of your sublist):

a.stream().collect(Collectors.groupingBy(e -> e.get(0)));

This will give you a Map<Object, List<Object>>, like:

{"1": [["1",100],["1",10]], "2": [["2",200],["2",20]], "5": [["5",300]["5",40]]}

Now, you need to apply a reduction to the values in the map to drop the keys from the sublists in the value. You can do this explicitly on the Map; or you can apply a downstream collector:

a.stream().collect(
    Collectors.groupingBy(
        e -> e.get(0),
        e -> e.stream().map(ee -> ee.get(1)).collect(toList())))

This will give you a Map<Object, List<Object>>, like:

{"1": [100, 10], "2": [200, 20], "5": [300, 40]}

Now you need to apply a further reduction to the map values in order to do the "subtraction".

I'm not going to provide code for that, because it's not clear exactly how you'd handle the case where that list has anything other than 2 values. But, it goes where I indicate:

a.stream().collect(
    Collectors.groupingBy(
        e -> e.get(0),
        e -> e.stream().map(ee -> ee.get(1)).reduce(....)))

Or, do it without streams. It's likely going to be more readable.

0
Azaan Wani On
List<List<?>> result = a.stream()
                        .collect(Collectors.toMap(list -> list.get(0),
                                                  list -> (int) list.get(1),
                                                  Integer::subtractExact))
                        .entrySet()
                        .stream()
                        .map(entry -> Arrays.asList(entry.getKey(),
                                                    entry.getValue()))
                        .collect(Collectors.toList());
System.out.print(result);
0
Sagar On

You could delay collecting/reducing object and get the result

import static java.util.stream.Collectors.*;
class Scratch {
    public static void main(String[] args) {
        List<List<?>> a = null;//[["1",100],["2",200],["5",300],["1",10],["2",20],["5",40]]
        Map<?, Optional<Integer>> map = a.stream().collect(groupingBy(e -> e.get(0),
                                                            mapping(e -> (Integer) e.get(1)
                                                                    , reducing((e1, e2) -> e1 - e2))));
    }
}