# Stream operation with two dimensional array

I am trying to find the best average score from below two dimensional array:

String[][] scores = { { "Amit", "70" }, { "Arthit", "60" }, { "Peter", "60" }, { "Arthit", "100" } };


The output is: 80 (Arthit's score (60+100)/2)

Till now I solved this problem with below approach, however I am looking for elegant solution with stream:

public static void main(String[] args) {
String[][] scores = { { "Amit", "70" }, { "Arthit", "60" }, { "Peter", "60" }, { "Arthit", "100" } };

int highestAvg = Integer.MIN_VALUE;
Function<String[], Integer> function = new Function<String[], Integer>() {
@Override
public Integer apply(String[] t) {
int sum = 0, count = 0;
for (int i = 0; i < scores.length; i++) {
if (t[0].equals(scores[i][0])) {
count++;
sum += Integer.parseInt(scores[i][1]);
}
}
int avg = sum / count;
return highestAvg < avg ? avg : highestAvg;
}
};
System.out.println(Arrays.stream(scores).map(function).max((o1, o2) -> o1.compareTo(o2)).get());
}


Would you please suggest, what's the better approach to handle two dimensional array using stream?

Note: I am not looking the exact solution, just looking your valuable suggestion.

Apologies for providing a full solution, I know you didn't ask for that, it was just easier for me to work through it by writing the code in full.

I'm initially approaching about by thinking about how would I solve this in a database using SQL, and then applying a similar approach in Java.

First thing to do here is group all the scores together by person. I'm going to end up with Map of some kind, keyed by the person. That means using the groupingBy collector. I need to provide a separate function for the key and the value. The value mapping is the interesting bit here, I was initially thinking I could just provide a list of the scores and average them in the next step, but then I stumbled across the averagingInt collector which saves me a step.

So now I have a map with each person's average score in. I don't need the names anymore, as I only need to return the score, so I'll just take the values() from the map and find the biggest. Keeping it streamy means using the max() method to get the result.

This seems to do the trick, and isn't too ugly.

public static void main(String[] args) {

String[][] scores = { { "Amit", "70" }, { "Arthit", "60" }, { "Peter", "60" }, { "Arthit", "100" } };

double highestAvg = Arrays.stream(scores)
.collect(groupingBy(s -> s[0],
averagingInt(s -> parseInt(s[1]))))
.values()
.stream()
.max(naturalOrder())
.get();

System.out.println(highestAvg)
}


It uses these static imports to keep things uncluttered

import static java.lang.Integer.parseInt;
import static java.util.Comparator.naturalOrder;
import static java.util.stream.Collectors.averagingInt;
import static java.util.stream.Collectors.groupingBy;


Note that I changed the average to a double because it saves another conversion and the result might need the decimals.

Something to think about - how could you change the data structure to clean this up further?

• Welcome to Code Review. You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process.
– Zeta
Oct 20, 2018 at 8:27
• Updated to add more explanation about the approach. Amazingly I just got asked this exact question in an online interview. What a stroke of luck. Oct 22, 2018 at 10:21