0

Given a list of unique-valued sublists (that is to say two different sublists cannot share elements of the same value) - eg:

List[List[1, 1, 1], List[2], List[4, 4], List[7]]

how would this be transformed into a Map with a (value, size) key-value pairing?

This would result in:

{
  1 : 3
  2 : 1
  4 : 2
  7 : 1
}

Defining our List as values, I would assume that one could use streams and collect as a Map as such:

values.stream().collect(Collectors.toMap(Integer::intValue, ? ));

Currently unsure what to put in for the second parameter as it requires a value mapper but does not allow for .size() to be called upon any of the sublists.

4
  • What is the expected output when the input is List[List[1, 1, 1, 6], List[2], List[4, 4], List[7, 6]]? Is it expected to be {1:4,2:1,4:2,7:2} or would it be {1:3,2:1,4:2,6:2,7:1}? Commented Sep 25, 2019 at 4:14
  • @Naman we're assuming that the input consists only of Lists with elements of equal value within their respective List. List[1, 1, 1, 6] does not fit this description. Commented Sep 25, 2019 at 16:53
  • Alright, then consider the input List[List[1, 1, 1], List[2], List[4, 4], List[7], List[1, 1], what do you expect as an output now? Commented Sep 25, 2019 at 16:58
  • You raise good edge cases - I'll update my question to note uniqueness between elements from any two sublists not including itself. Commented Sep 25, 2019 at 17:10

3 Answers 3

7

When using Collectors.toMap, you need to specify how to get the key and the value from each element of the stream. Integer::intValue won't work here, because your stream elements are lists, not integers.

For the key, get the first element of the list. (This assumes that the inner lists are all non-empty.) For the value, pass the size method reference.

values.stream()
    .collect(Collectors.toMap(list -> list.get(0), List::size));
Sign up to request clarification or add additional context in comments.

1 Comment

Based on another answer, what becomes unclear is the expected output for a different input.. Might be worth a mention.
1

You could flatten the list and then use something like Collectors.groupingBy(Function.identity(), Collectors.counting()). However in this case i would say a good old (nested) for loop might be simpler to write and read.

List<List<Integer>> lst = Arrays.asList(Arrays.asList(1,1,1),Arrays.asList(2),Arrays.asList(4,4),Arrays.asList(7));

Map<Integer,Integer> result= new HashMap<Integer,Integer>();
System.out.println(lst);
//[[1, 1, 1], [2], [4, 4], [7]]

for(List<Integer> sub:lst){
  for(int n:sub){
    Integer last=result.get(n);
    int newCount=(last==null?0:last)+1;
    result.put(n, newCount);
  }
}
System.out.println(result);
//{1=3, 2=1, 4=2, 7=1}

Comments

1

I think @rgettman's answer is the most elegant. However, it makes the presumption that all lists are non-empty. Of course this is easily fixed by simply adding .filter(list -> !list.isEmpty()) before the collect reduction operation.

This is another approach, which no longer considers the list to be 'two-dimensional', that is, List<List<Integer>>, but instead flattens it to just a stream of Integers.

Map<Integer, Long> map = lists.stream()
    .flatMap(Collection::stream)
    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

2 Comments

It really matters what is the expected output when the input is List[List[1, 1, 1, 6], List[2], List[4, 4], List[7, 6]]. The output upon such case would differ in the two answers.
This yields, indeed, a whole different result. At the moment, all posted answers suffer from such an anomaly.

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.