1

I have a Tag class which contains a list of Items

class Tag {

private String tagName
private List<String> items

}

I have a list of Tags in which each tag a list of items

List<Tag> tagList =   [
                       {"tagName": "popular", "items": ["Item1","Item2","Item3","Item4"]},
                       {"tagName": "expensive" , "items":  ["Item2","Item4","Item5"]},
                       {"tagName": "mostwanted", "items":  ["Item1","Item2","Item5"]}
                      ]

I wan to convert this to a map which have the items as key and tagName as values.

expected output :

{
    "Item1" : ["popular","mostwanted"],
    "Item2" : ["popular","expensive","mostwanted"],
    "Item3" : ["popular","mostwanted"],
    "Item4" : ["popular","expensive"],
    "Item5" : ["expensive","mostwanted"]
}

I tried various combination of stream/faltmap/groupingBy, but I didnt get the expected output. Can you please help. Thanks

2 Answers 2

2

You can flat the items using flatMap then create pair of item and tagName using SimpleEntry. Then grouping by item using groupingBy and map tagName to get list of tagName

Map<String, List<String>> tagMap = tagList.stream()
        .flatMap(t -> t.getItems()
                       .stream()
                       .map(item -> new AbstractMap.SimpleEntry<>(item, t.getTagName())))
        .collect(Collectors.groupingBy(m -> m.getKey(),
            Collectors.mapping(m -> m.getValue(), Collectors.toList())));
Sign up to request clarification or add additional context in comments.

2 Comments

It's a good one! Stream API here looks very clumsy :) Moreover, I struggled to compose it.
@Nikolas When you love Stream API more then it looks nice also :)
1

Here is a procedural way to go using new features from such as Map::computeIfPresent and Map::computeIfAbsent, but without :

Map<String, List<String>> map = new HashMap<>();
    tagList.forEach(tag -> {
        String tagName = tag.getTagName();
        tag.getItems().forEach(item -> {
            map.computeIfPresent(item, (k, v) -> { v.add(tagName); return v; });
            map.computeIfAbsent(item, k -> new ArrayList<>(Arrays.asList(tagName)));
   });
});

map.forEach((k, v) -> System.out.println(k + " " + v));

If you want to sort these items from Item1 to Item5, use the different implementation:

Map<String, List<String>> map = new TreeMap<>();

Moreover, your expected output doesn't match as long as there is only one occurence of Item3:

Item1 [popular, mostwanted]
Item2 [popular, expensive, mostwanted]
Item3 [popular]
Item4 [popular, expensive]
Item5 [expensive, mostwanted]

2 Comments

You might have meant something simpler as tagList.forEach(tag -> tag.getItems().forEach(item -> map.computeIfAbsent(item, k -> new ArrayList<>()).add(tag.getTagName())));...though I would have preferred for (Tag tag : tagList) { for (String item : tag.getItems()) { map.computeIfAbsent(item, k -> new ArrayList<>()) .add(tag.getTagName()); } }
@Naman: You are right, I have forgotten about the return types of these methods (IMHO, the word "compute" is somehow associated with void in my mind). Feel free to post your answer.

Your Answer

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.