4

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)*/
 }
}
Tagir Valeev
101k19 gold badges233 silver badges346 bronze badges
asked Sep 9, 2015 at 8:14
1
  • 1
    I edited your question to clarify it. Also I fixed the expected output. Please check whether my edit meets your intentions. Commented Sep 9, 2015 at 9:36

4 Answers 4

4

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());
answered Sep 9, 2015 at 9:32

1 Comment

As your solution is the most elegant one, this is my accepted answer.
2

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>>.

answered Sep 9, 2015 at 8:35

1 Comment

Works perfectly, exactly what I wanted and appreciate the explanation
-1

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/ ...));
answered Sep 9, 2015 at 8:28

Comments

-2

using Java 8 stream API:

List<Restaurant> sortedList = mylist.stream()
.sorted((restaurant1, restaurant2) -> Long.compare(restaurant1.getRating(), restaurant2.getRating()))
.collect(Collectors.toList());
answered Sep 9, 2015 at 8:34

3 Comments

can someone explain to me why the downvote? imo, the code fragment gives a complete answer to the question
I didnt downvote, but in general your solution is not correct, as it is comparing just the ratings and do not take care if the city and type is same or not. e.g. it produces list as follows: City1, Tier1, 1 City1, Tier2, 1 City2, Tier1, 1 City2, Tier3, 1
@sharonbn, downvote is issued by me. You did not read the question to the end and your solution is incorrect. This seems enough reason to downvote.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.