5

I have a generic function which accepts Collection<? extends T> ts.

I'm also passing:

Function<? extends T, ? extends K> classifier which maps each item T to a key K (possible to have duplicates)

Function<? extends T, Integer> evaluator which gives an integer value for the item.

The function itself has a built-in calculation ("int to int") for every produced Integer (could be something like squaring for our example)

Finally, I'd like to sum all of the values for each key.

So the end result is: Map<K, Integer>.

For example,
Let's say we have the list ["a","a", "bb"] and we use Function.identity to classify, String::length to evaluate and squaring as the built-in function. Then the returned map will be: {"a": 2, "b": 4}

How can I do that? (I guess that preferably using Collectors.groupingBy)

4
  • Because we're also squaring Commented Apr 1, 2019 at 7:37
  • 1
    What did your search and research bring up? Have you tried something? Commented Apr 1, 2019 at 7:43
  • @OleV.V., yeah actually tried to integrate it with the Collectors.groupingBy. It didn't work out Commented Apr 1, 2019 at 7:45
  • 2
    @yaseco Show, don't tell. Commented Apr 1, 2019 at 7:48

2 Answers 2

4

Here's one way to do it:

public static <T,K> Map<K,Integer> mapper (
    Collection<T> ts,
    Function<T, K> classifier,
    Function<T, Integer> evaluator,
    Function<Integer,Integer> calculator) 
{
     return
        ts.stream()
          .collect(Collectors.groupingBy(classifier,
                                         Collectors.summingInt(t->evaluator.andThen(calculator).apply(t))));
}

The output for:

System.out.println (mapper(Arrays.asList("a","a","bb"),Function.identity(),String::length,i->i*i));

is

{bb=4, a=2}
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks so much Eran! (Apparently, I was quite close to the answer but you closed the gap for me - Cheers!)
I do need to understand the downstream part better
@yaseco Collectors.summingInt() allows you to map each of the T elements of a given group to an int and then sum all these ints. t->evaluator.andThen(calculator).apply(t) takes a T instance, applies the evaluator function on it and then applies the calculator function of the result.
t->evaluator.andThen(calculator).apply(t) will repeatedly invoke andThen, constructing a new Function instance for every evaluation of this lambda expression. The straight-forward way to apply a method to the result of another would be a nested invocation like t -> calculator.apply(evaluator.apply(t)). Or, if it really has to be a new functional construct, evaluator.andThen(calculator)::apply will invoke andThen only once and capture the result.
2

Or another approach:

private static <K, T> Map<K, Integer> map(Collection<? extends T> ts,
                                          Function<? super T, ? extends K> classifier,
                                          Function<? super T, Integer> evaluator,
                                          Function<Integer, Integer> andThen) {

    return ts.stream()
             .collect(Collectors.groupingBy(
                 classifier,
                 Collectors.mapping(evaluator.andThen(andThen),
                                    Collectors.reducing(0, Integer::sum))
             ));

}

And use it with:

public static void main(String[] args) {

    System.out.println(map(
        Arrays.asList("a", "a", "bb"),
        Function.identity(),
        String::length,
        x -> x * x));

}

1 Comment

It’s funny, how far people go, just to use method references. Here, using evaluator::apply and andThen::apply to convert a Function to a Function. Since these are already Function instances, a simple Collectors.mapping(evaluator, Collectors.mapping(andThen, …)) would do and once you realize that neither, lambda expression nor method reference is required, you can use a single combined Collectors.mapping(evaluator.andThen(andThen), …), unlike the other answer, this will not repeatedly invoke andThen

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.