I have the list that contains data that looks like this
game_id | user_id | status
1 1 STARTED
2 1 FINISHED
1 2 STARTED
2 2 FINISHED
I want to collect this list into two maps (or in a single map if possible)
One map should keep mapping of user_id
to number of the games that user played - basically games with finished
statuses
user_id | finished_games
1 1
2 1
Another map should store user_id
to all games count. It would look like this
user_id | all_games
1 2
2 2
I do it like this (I removed game_id
field as it is that not important in fact)
import java.util.*;
import java.util.stream.*;
public class MyClass {
public static void main(String args[]) {
List<Game> games = Arrays.asList(
create("user-1", "STARTED"),
create("user-2", "STARTED"),
create("user-1", "FINISHED"),
create("user-2", "FINISHED"),
create("user-1", "FINISHED"),
create("user-2", "FINISHED")
);
// expect user-1: all_games (3), finished_games (2)
Map<String, Long> allGames = games
.stream()
.collect(Collectors.groupingBy(Game::getUserId, Collectors.counting()));
System.out.println(allGames);
Map<String, Long> finishedGames = games
.stream()
.filter(game -> game.battleStatus.equals("FINISHED"))
.collect(Collectors.groupingBy(Game::getUserId, Collectors.counting()));
System.out.println(finishedGames);
}
private static Game create(String id, String status) {
Game game = new Game();
game.userId = id;
game.battleStatus = status;
return game;
}
private static class Game {
String userId;
String battleStatus;
public String getUserId() {
return userId;
}
}
}
It seems to work fine. But when I calculate games with status, I filter in order to leave only items with FINISHED
status.
Map<String, Long> finishedGames = games
.stream()
.filter(game -> game.battleStatus.equals("FINISHED"))
.collect(Collectors.groupingBy(Game::getUserId, Collectors.counting()));
Is there a way to achieve that inside collect
block instead of using filter
?
If you need to count both finished and unfinished games, then another groupingBy
can help you:
Function<Game, Boolean> isFinished = game -> "FINISHED".equals(game.battleStatus);
Map<String, Map<Boolean, Long>> groupedGames =
games.stream()
.collect(groupingBy(Game::getUserId,
groupingBy(isFinished, counting())));
Or you may want to group by battleStatus
itself:
Map<String, Map<String, Long>> groupedGames =
games.stream()
.collect(groupingBy(Game::getUserId,
groupingBy(game -> game.battleStatus, counting())));
I would even create a getter getBattleStatus()
and then replace game -> game.battleStatus
with method reference Game::getBattleStatus
:
Map<String, Map<String, Long>> groupedGames =
games.stream()
.collect(groupingBy(Game::getUserId,
groupingBy(Game::getBattleStatus, counting())));