Home > Back-end >  How to combine multiple Lists in Java based on a condition using the Stream API?
How to combine multiple Lists in Java based on a condition using the Stream API?

Time:01-11

Let's say I have 4 Lists of equal size (N):

A = [22, -1, -10]
B = [3, -2, 54]
C = [-12, 3, 2]
D = [40, 4, -3]

I would like to combine them into another list of size N, by picking one number from each list at every index, based on a condition. Let's say we take the smallest positive integer, to keep in simple.

The result would look like this:

Result = [3, 3, 2]

What would be the most elegant way to do this using the Java streams API?

CodePudding user response:

I am confused by your example result because it does not match your question.
You said result should be size N = 4, but your example is size 3. You said build Result by picking one number from each list, then you picked 2 numbers from list C (3, 2).

I have given two solutions since I do not know which one you want.

public static void main(String[]args) {
        List<Integer> A = Arrays.asList(22, -1, -10);
        List<Integer> B = Arrays.asList(3, -2, 54);
        List<Integer> C = Arrays.asList(-12, 3, 2);
        List<Integer> D = Arrays.asList(40, 4, -3);
        
        List<List<Integer>> listOfLists = Arrays.asList(A, B, C ,D);
        
        // get the min positive integers by picking one from each lsit
        List<Integer> positiveMinsPickOne = listOfLists.stream()
                .map((list -> list.stream().filter(num -> num >= 0).min(Integer::compareTo).get()))
                .collect(Collectors.toList());
        
        System.out.println(positiveMinsPickOne);
        
        // get the min positive integers by picking over all integers
        List<Integer> positiveMinsPickAll = listOfLists.stream()
        .flatMap(List::stream)
        .filter(num -> num > 0)
        .sorted()
        .limit(listOfLists.size())
        .collect(Collectors.toList());
        
        System.out.println(positiveMinsPickAll);
    }

Output:
[22, 3, 2, 4]
[2, 3, 3, 4]

CodePudding user response:

If this is a one-off, you can stream over the indexes and reduce them like this:

IntStream.range(0, a.size())
        .mapToInt(col -> Stream.of(a, b, c, d)
                .mapToInt(list -> list.get(col))
                .filter(i -> i > 0)
                .min()
                .orElse(0))
        .toArray()

If you need an abstraction for different type of reduction, you can create a method that handles the outer iteration and accepts a function to reduce each column:

int[] aggregate(List<List<Integer>> lists, ToIntFunction<IntStream> function) {
    return IntStream.range(0, lists.get(0).size())
            .mapToObj(col -> lists.stream().mapToInt(list -> list.get(col)))
            .mapToInt(function)
            .toArray();
}

You could call it like this to get the lowest positive value per column:

aggregate(Arrays.asList(a, b, c, d), col -> col.filter(i -> i > 0).min().orElse(0))
  • Related