Reputation: 302
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
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
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
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
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