Thrax
Thrax

Reputation: 1964

Calculate sum of object's attribute in List of List (with stream)

I'm using Java 8, and I want to resolve my problem using List.stream() functionnalities.

Here's my problem :

This is the Score class (Player class does not matter) :

public class Score {

    private Player player;
    private int score;

    public Player getPlayer() {
        return player;
    }

    public void setPlayer(Player player) {
        this.player = player;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }
}

At the start of my program, I have a list of players populated :

List<Player> players; // Populated elsewhere

My program then does what it has to do, and I get a variable of this type :

List<List<Score>> scores; // Populated elsewhere

Now i want to print a formatted output such as this for each players :

Player.getName() + " : " + averagedScore;

where averagedScore is the sum of the score.getScore() value for one Player divided by the number of occurences of a Score for this Player in my scores variables.


Now, what I've done so far :

for (Players player : players) {
    scores.stream().filter(x -> x.stream().anyMatch(y -> y.getPlayer().equals(player)));
}

But I don't see how to calculate the sum of element.getScore() contained in sublists and filtered by element.getPlayer()

Upvotes: 2

Views: 2562

Answers (1)

assylias
assylias

Reputation: 328618

You could use a combination of the groupingBy and averagingInt collectors after having "flatMapped" your list of lists:

Map<Player, Double> scoreByPlayer = scores.stream()
                .flatMap(List::stream)
                .collect(groupingBy(Score::getPlayer, averagingInt(Score::getScore)));

scoreByPlayer.forEach((p, s) -> System.out.println(p + " : " + s));

Note: requires the following static imports:

import static java.util.stream.Collectors.averagingInt;
import static java.util.stream.Collectors.groupingBy;


For reference, full example:

public static void main(String[] args) {
  Player adam = new Player("Adam");
  Player pat = new Player("Pat");
  Player susie = new Player("Susie");
  List<List<Score>> scores = new ArrayList<> ();
  scores.add(Arrays.asList(s(adam, 1), s(pat, 2), s(susie, 3)));
  scores.add(Arrays.asList(s(adam, 2), s(pat, 4), s(susie, 6)));
  scores.add(Arrays.asList(s(adam, 3), s(pat, 6), s(susie, 9)));

  Map<Player, Double> scoreByPlayer = scores.stream()
                  .flatMap(List::stream)
                  .collect(groupingBy(Score::getPlayer, averagingInt(Score::getScore)));

  scoreByPlayer.forEach((p, s) -> System.out.println(p + " : " + s));
}

private static Score s(Player p, int score) { return new Score(p, score); }

public static class Score {
  private final Player player;
  private final int score;
  public Score(Player player, int score) {
    this.player = player;
    this.score = score;
  }

  public Player getPlayer() { return player; }
  public int getScore() { return score; }
}

public static class Player {
  private final String name;
  public Player(String name) { this.name = name; }
  @Override public String toString() { return name; }
}

Upvotes: 6

Related Questions