简体   繁体   中英

How to create nested maps from flat sql select result set

Having a SQL query similar to:

SELECT DISTINCT REGION, COUNTRY, CITY

Yielding this result set

REGION COUNTRY CITY
EUROPE FRANCE  PARIS
EUROPE FRANCE  LYON 
EUROPE FRANCE  NICE
EUROPE GERMANY BERLIN
EUROPE GERMANY DORTMUND 
EUROPE GERMANY HANNOVER

Is there a way to use google's ListMultimap so that I end up with a key -> value -> value structure?

Eg

{EUROPE
    {GERMANY
       {BERLIN, DORTMUND, HANNOVER}, 
     FRANCE
       {PARIS, LYON, NICE}
     }
}

Or would another package be better suited for this?

EDIT:

Tried implementing @Prog_G's solution, but got out of memory with the below approach. I reckon that I have to transform outer to yet another data structure at the end. Would I create a Map out of this in some way? PS. the real example is one more level deep, but wanted to keep it simpler for the sake of the question.

private static class GeoRowMapper implements RowCallbackHandler {

    ListMultimap<String, ListMultimap<String, ListMultimap<String, String>>> outer = ArrayListMultimap.create();
    ListMultimap<String, ListMultimap<String, String>> middle = ArrayListMultimap.create();
    ListMultimap<String, String> inner = ArrayListMultimap.create();

    @Override
    public void processRow(ResultSet rs) throws SQLException {

        String region = rs.getString("region");
        String country = rs.getString("country");
        String city = rs.getString("city");
        String address = rs.getString("address");
        inner.put(city, address);
        middle.put(country, inner);
        outer.put(region, middle);
    }
    public ListMultimap<String, ListMultimap<String, ListMultimap<String, List<String>>>> get() {
        return Multimaps.asMap(outer);
    }
}

You could use Table , as per Andy Turner's comment. In your case it would be something like:

ImmutableTable<Region, Country, ImmutableList<City>> immutableTable = RECORDS.stream()
        .collect(toImmutableTable(
                r -> r.getRegion(),
                r -> r.getCountry(),
                r -> ImmutableList.of(r.getCity()),
                (l, l2) -> ImmutableList.<City>builder().addAll(l).addAll(l2).build()
        ));

Or if you want mutable table as a result:

Table<Region, Country, List<City>> table = RECORDS.stream()
        .collect(toTable(
                r -> r.getRegion(),
                r -> r.getCountry(),
                r -> Lists.newArrayList(r.getCity()),
                (l, l2) -> {
                    l.addAll(l2);
                    return l;
                },
                HashBasedTable::create
        ));

Works with stream() and Collectors.groupingBy (credits go here , see also this ). Set instead of List guarantees unique elements.

Map<String, Map<String, Set<String>>> grouped = 
    records.stream()
                .collect(Collectors.groupingBy(r -> r.region,
                         Collectors.groupingBy(r -> r.country,
                         Collectors.mapping(r -> r.city, Collectors.toSet()))));

given

class Record {
    String city;
    String region;
    String country;
}

Currently this will give you actually HashMap s and a HashSet . Their issue is a lack of guaranteed order when iterating their entries. Also you have no reordering or "insert at a index" operation available, if the structure should back a tree displayed in a user interface. Eg LinkedHashMap and TreeSet have a predictable iteration order. Still this approach may be ok as an intermediary step when creating the final tree data structure.

Depending on your taste the usage of Map.computeIfAbsent may be an option, which was introduced with an eye on multimaps .

public Map<String, Map<String, Set<String>>> mumap(List<Record> records) {
    Map<String, Map<String, Set<String>>> result = new LinkedHashMap<>();
    for (Record r : records) {
        result.computeIfAbsent(r.region, region -> new LinkedHashMap<>())
              .computeIfAbsent(r.country, country -> new TreeSet<>())
              .add(r.city);
    }
    return result;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM