Search code examples
javajava-8hashmap

How to filter map and return list of values


I am trying to create a function that filters a map that holds a String as a key and a list of Flight objects as values and returns a list of strings. The map represents flight paths and the task is to find the shortest path from the origin to the destination. The flight class has two fields, origin and destination. So if I send Vienna, New York, I should get a list with fligth1 and flight2 in it. The funcition takes in two parameters (String origin, String destination)

The key in the map represents a city, while the values are locations from where flights arrive, like so:

Map<String, List<Flight>> paths = new HashMap<>();

List<Flight> flightsToBerlin = new ArrayList<>();
List<Flight> flightsToVienna = new ArrayList<>();

Flight flight1 = new Flight("Vienna", "Berlin");
Flight flight2 = new Flight("Berlin", "New York");

flightsToBerlin.add(flight1);
flightsToVienna.add(flight2);


paths.put("Vienna", flightsToVienna);
paths.put("Berlin", flightsToBerlin);

The trick is that the requirement is for it to be done in one line. And this is the part that drives me crazy. I have tried using streams but I get kind of confused after filtering the map and finding the destination, like so:

public List<Flight> findPath(String origin, String destination) {
    return (List<Flight>) this.paths.entrySet().stream()
            .filter(x -> x.getKey().equals(destination))..
}

How do I proceed from here?


Solution

  • You can do it like this:

    return Stream.of(
            paths.values()
                    .stream()
                    .flatMap(Collection::stream)
                    .collect(Collectors.groupingBy(Flight::getStartingLocation))
    ).flatMap(flights ->
            Stream.of(
                    new HashMap<>(Map.of(origin, new Flight(origin, origin)))
            ).peek(back ->
                    Stream.iterate(
                            List.of(origin),
                            list -> list.stream().flatMap(
                                    now -> flights.getOrDefault(now, Collections.emptyList()).stream()
                                            .filter(flight -> back.putIfAbsent(flight.getDestination(), flight) == null)
                                            .map(Flight::getDestination)
                            ).collect(Collectors.toList())
                    ).filter(list -> list.contains(destination)).findFirst()
            ).map(back ->
                    Stream.iterate(
                            new Flight(destination, null),
                            now -> back.get(now.getStartingLocation())
                    )
                            .skip(1)
                            .takeWhile(flight -> !flight.getDestination().equals(origin))
                            .collect(Collectors.toList())
            )
    )
            .map(ArrayList::new)
            .peek(Collections::reverse)
            .findFirst().get();