Home > Software engineering >  Java - Stream API: custom reduce function to find a minimum
Java - Stream API: custom reduce function to find a minimum

Time:08-28

Small question regarding Java and its Stream API, especially the reduce() function please.

I have as input some Stream of integers.

The integers goes from -1 included, to a positive integer.

The mathematical notation is I think [-1 .. n], it is important the smallest number possible is -1, minus 1. It cannot be -2, -3 etc, the smallest is -1.

I would like to write a reduce function which does:

case a - if the stream contains number from 0 to n, basically, there is no -1 in the stream, return the minimum number of the stream.

Stream<Integer> casea = Stream.of(2, 0, 4);

for above example case a, to return 0.

case b - if the stream contains only -1, there is nothing else other than -1, return -1.

Stream<Integer> caseb = Stream.of(-1, -1, -1);

for above example case b, to return -1.

case c - if the stream contains any -1, and any non -1, return the minimum number that is not -1.

Stream<Integer> casec = Stream.of(-1, 2, 5);

for above example case b, to return 2 (and not -1).

I am having trouble crafting this reduce function.

What I have tried:

So far, I tried .reduce(Integer.MAX_VALUE, Integer::min))

Unfortunately, it is not correct. It is returning the correct result for case a and b.

However, it will return -1 for the case c, where I would like to return the smallest non -1 integer, since it has something other than -1 in the stream.

May I ask what is the correct reduce function please?

Thank you

CodePudding user response:

To find the smallest element that is different from -1, firstly, apply filter() to retain only non-minus-one elements in the stream and then apply min().

As a default value, which would be returned if the stream appears to be empty, provide -1.

public static int getSmallestNonMinusOne(List<Integer> list) {
    
    return list.stream()
        .mapToInt(Integer::intValue)
        .filter(i -> i != -1)
        .min()
        .orElse(-1);
}

Alternatively, if you want to use reduce() you can re-write the code like this:

public static int getSmallestNonMinusOne(List<Integer> list) {
    
    return list.stream()
        .mapToInt(Integer::intValue)
        .filter(i -> i != -1)
        .reduce(Integer::min)
        .orElse(-1);
}

Note: that min is basically a specialized form of reduction, and from the perspective of readability the first version is preferred one.

main()

public static void main(String[] args) {
    System.out.println(getSmallestNonMinusOne(List.of(-1, 0, 8)));
    System.out.println(getSmallestNonMinusOne(List.of(-1, -1, -1)));
    System.out.println(getSmallestNonMinusOne(List.of(-1, 2, 5)));
    System.out.println(getSmallestNonMinusOne(List.of()));
}

Output:

0    // -1, 0, 8
-1   // only -1 elements in the source
2    // -1, 2, 5
-1   // source is empty

CodePudding user response:

If you want to specifically use the reduce operator:

numbers.stream().reduce(-1, (submin, element) ->
    (submin == -1 && element != -1) 
    ? element // here, I have seen the first non-minus-one number
    : element != -1 ? Math.min(submin, element) : submin;  // here, I would keep updating the minimum without considering any minus ones 

It starts with a minus one as the minimum, changes the minimum if it encounters a non-minus-one element, and keeps updating the minimum the normal way, ignoring any next -1s too.

  • Related