Rachit Jain
Rachit Jain

Reputation: 302

Parsing a list using java streams

My usecase is I have list of Restaurants objects with attributes as city, type, rating and I want to print the list by highest rating for given combination of (city, type). As I created this example to mess with java stream, please reply how it can be achieved using streams:

import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.Map;
import java.util.Optional;
import static java.util.stream.Collectors.maxBy;
import static java.util.stream.Collectors.groupingBy;
import static java.util.Comparator.comparingLong;

class Restaurant {
  public String city; // Any city
  public String type; // Tier1, Tier2, etc. 
  public Long rating; // for simplicity sake, assume its a number from 1 to 5
  public Restaurant(String city, String type, Long rating) { 
    this.city = city;
    this.type = type;
    this.rating = rating;
  }
  public String getCity() { return city; }
  public String getType() { return type; }
  public Long getRating() { return rating; }
}

public class HelloWorld
{
  public static void main(String[] args)
  {
    List<Restaurant> mylist = new ArrayList<Restaurant>();
    mylist.add(new Restaurant("City1", "Tier1", 1L));
    mylist.add(new Restaurant("City1", "Tier1", 2L));
    mylist.add(new Restaurant("City1", "Tier2", 1L));
    mylist.add(new Restaurant("City2", "Tier1", 1L));
    mylist.add(new Restaurant("City2", "Tier1", 3L));
    mylist.add(new Restaurant("City2", "Tier3", 1L));
    // expected outcome: (City1, Tier1, 2), (City1, Tier2, 1), (City2, Tier1, 3), (City2, Tier3, 1)

    Map<String, Map<String, Optional<Restaurant>>> mymap =
   mylist.stream().collect(groupingBy(Restaurant::getCity, groupingBy(Restaurant::getType, maxBy(comparingLong(Restaurant::getRating)))));
   /* Upto here everything is fine, but I want to have everything stored back into a list. Tried flatMap(Map::values).collect(Collectors.toList()); but it fails*/
  /*My output list should have 4 objects of Restaurant*/
  /*(City1, Tier1, 2), (City1, Tier2, 1), (City2, Tier1, 3), (City2, Tier3, 1)*/
  }
}

Upvotes: 4

Views: 1564

Answers (4)

Tagir Valeev
Tagir Valeev

Reputation: 100209

First, you can create composite key to get rid of nested maps. The Arrays.asList(r.getCity(), r.getType()) is what you need. Next, groupingBy is not well suitable in your case, as you should handle those nasty optionals. Instead use toMap collector with custom merging function:

Map<List<String>, Restaurant> mymap = mylist.stream().collect(
        toMap(r -> Arrays.asList(r.getCity(), r.getType()), r -> r,
                BinaryOperator.maxBy(comparingLong(Restaurant::getRating))));

Now mymap.values() is the flat collection of restaurants as you want. If you really want the List, you can dump it into one:

List<Restaurant> list = new ArrayList<>(mymap.values());

Upvotes: 4

iBlocksShaun
iBlocksShaun

Reputation: 113

The code snippet below should produce the result you are after:

    List<Optional<Restaurant>> resultList = mymap.values()
                                                 .stream()
                                                 .flatMap(map -> map.values().stream())
                                                 .collect(Collectors.toList());

mymap.values().stream() produces a Stream<Map<String, Optional<Restaurant>>>. flatMap() then maps that stream to a Stream<Optional<Restaurant>>, and collect(Collectors.toList()) collects that stream into a List<Optional<Restaurant>>.

Upvotes: 2

Sharon Ben Asher
Sharon Ben Asher

Reputation: 14328

using Java 8 stream API:

List<Restaurant> sortedList = mylist.stream()
.sorted((restaurant1, restaurant2) -> Long.compare(restaurant1.getRating(), restaurant2.getRating()))
.collect(Collectors.toList());

Upvotes: -2

ikos23
ikos23

Reputation: 5354

I haven't worked with Java 8 yet but my guess is

mylist.stream().sorted(new Comparator<Restaurant>() {
     public int compare(Restaurant r1, Restaurant r2) {
        // impl here how your restaurant should be compared
        // (by rating or smth. else)
     }
}).collect(groupingBy( .../ al the rest stuff here/ ...));

Upvotes: -1

Related Questions