фильтровать поток, используя collect () вместо filter () - PullRequest
0 голосов
/ 29 января 2019

У меня есть список, который содержит данные, которые выглядят следующим образом

game_id | user_id | status
   1         1       STARTED
   2         1       FINISHED
   1         2       STARTED
   2         2       FINISHED

Я хочу собрать этот список в две карты (или, если возможно, в одну карту)

Одна карта должнасохраняйте привязку user_id к числу игр, в которые играл пользователь - в основном, игры с finished статусами

user_id | finished_games
   1            1
   2            1

Другая карта должна хранить user_id для всех игр.Это выглядело бы так

user_id  | all_games
   1            2
   2            2

Я делаю это так (я удалил поле game_id, поскольку оно на самом деле не важно)

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;
        }
    }
}

Кажется, все работает нормально.Но когда я вычисляю игры со статусом, я фильтрую, чтобы оставить только элементы со статусом FINISHED.

Map<String, Long> finishedGames = games
    .stream()
    .filter(game -> game.battleStatus.equals("FINISHED"))
    .collect(Collectors.groupingBy(Game::getUserId, Collectors.counting()));

Есть ли способ достичь этого внутри блока collect вместо использования filter?

Ответы [ 2 ]

0 голосов
/ 29 января 2019

Вы можете использовать несколько groupingBy, чтобы получить это, например:

Map<String, Map<String, Long>> gameStatuses = games
   .stream()
   .collect(Collectors.groupingBy(Game::getUserId, 
   Collectors.groupingBy(Game::getBattleStatus, Collectors.counting())));
System.out.println(gameStatuses);

Для этого вам нужно добавить геттер в класс Game для battleStatus, например:

private static class Game {
    String userId;
    String battleStatus;

    public String getUserId() {
        return userId;
    }

    public String getBattleStatus() {
        return battleStatus;
    }
}

Это даст вам следующий вывод:

{user-1={STARTED=1, FINISHED=2}, user-2={STARTED=1, FINISHED=2}}

Вы можете использовать вложенный map.get, чтобы получить значения для любого пользователя и любого статуса.

0 голосов
/ 29 января 2019

Если вам нужно сосчитать как законченные, так и незаконченные игры, вам может помочь еще одна groupingBy:

Function<Game, Boolean> isFinished = game -> "FINISHED".equals(game.battleStatus);

Map<String, Map<Boolean, Long>> groupedGames = 
    games.stream()
         .collect(groupingBy(Game::getUserId, 
                      groupingBy(isFinished, counting())));

Или вы можете сгруппировать по battleStatus:

Map<String, Map<String, Long>> groupedGames = 
    games.stream()
         .collect(groupingBy(Game::getUserId, 
                      groupingBy(game -> game.battleStatus, counting())));

Я бы даже создал геттер getBattleStatus(), а затем заменил game -> game.battleStatus ссылкой на метод Game::getBattleStatus:

Map<String, Map<String, Long>> groupedGames = 
    games.stream()
         .collect(groupingBy(Game::getUserId, 
                      groupingBy(Game::getBattleStatus, counting())));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...