5

I am trying to create a map using groupingBy(...) function in lambda. Now the problem I am facing is I am unable to convert list to map for particular condition

Code:

List<Integer> list = IntStream
                       .range(0,120)
                       .mapToObj(Integer::new)
                       .collect(Collectors.toList());

Expected functionality:

I need to collect a map whose key is key value like keys from 0 to 120 and values will increase like

[]
[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
.
.
.
//so on upto 120


Map<Integer, List<Integer>> myMapOne = new HashMap<>();
        for (Integer inteter : list) {
            myMapOne.put(inteter, list.subList(0, list.indexOf(inteter)));
        }

What I am trying (idea):

Map<Integer, Integer> myMap = list
                              .stream()
                             .collect(Collectors.groupingBy(Integer::new, list.subList(0, Integer::new)));

Error: not able to convert to List inside groupingBy() or not sure if I am missing something

How to solve this? Any help is welcome

Edit:

do I need to use method reference here?

Below is method

public static List<Integer> getList(Integer index, List<Integer> list) {
    return list.subList(0, list.indexOf(index));
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • 1
    Does this help ? http://stackoverflow.com/questions/23057549/lambda-expression-to-convert-array-list-of-string-to-array-list-of-integers – Sam Orozco Sep 03 '16 at 05:57
  • @SamOrozco thanks, but it returns `List` and I need `map` –  Sep 03 '16 at 05:59
  • The values of your Map are Integer from 0 to 120, but what should be the keys? – Journeycorner Sep 03 '16 at 06:23
  • @Journeycorner , first thanks for reply, the integer value that I'm getting from `List`, for reference I have added code under **Expected Functionality** –  Sep 03 '16 at 06:26
  • Btw. you should prefer static factory methods Integer:valueOf or boxed() (which also makes it clear it is just boxing and calls Integer::valueOf) to Integer::new . – Journeycorner Sep 03 '16 at 06:27
  • @HelloWorld I still don't get it please give an example like: Map: 1 -> List from 1 to 10, 2 -> List from 11 to 20 ... – Journeycorner Sep 03 '16 at 06:29
  • but then `collect(..)` gets complicated and since I am new to lambda so still struggling to figure out what to do –  Sep 03 '16 at 06:30
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/122557/discussion-between-journeycorner-and-helloworld). – Journeycorner Sep 03 '16 at 06:30
  • 2
    Speaking as someone who loves streams/lambdas, I don't think this is a good use case for them. – Rogue Sep 03 '16 at 06:36
  • @Rogue, actually I am trying to hit my head against `groupingBy(..),mapping(..) and entire lambda wall` so i m just trying to put things in my head :) –  Sep 03 '16 at 06:52

2 Answers2

7

You could do:

IntStream.range(0, 120)
    .boxed()
    .collect(Collectors.toMap(
        x -> x, 
        y -> IntStream.range(0, y).boxed().collect(Collectors.toList())));

Instead of .mapToObj(Integer::new), you can use boxed(). Then you can use toMap() to map the values to a key and a value. The key is just the identity, so you can use just x->x, the value is a new list, which can be created with IntStream.range(0, y).boxed().collect(Collectors.toList())

user140547
  • 7,750
  • 3
  • 28
  • 80
  • 1
    Dammit, exactly my answer, you was 2 minutes faster :D – Journeycorner Sep 03 '16 at 06:42
  • @user140547 cant we involve `groupingBy(..)` just asking –  Sep 03 '16 at 06:50
  • I am trying, but this code outputs 0=0, 120=120: Map> foos = IntStream.of(0, 120) .boxed() .collect(groupingBy((x) -> x, mapping((y) -> y, toList()))); – Journeycorner Sep 03 '16 at 06:59
  • @Journeycorner `Stream.of(..)` will create stream of only value 0 &120 –  Sep 03 '16 at 07:08
  • @user140547 I'm not sure, how can we use `groupingBy(..)` but thanks for teaching about the simplicity of `lambda`, and also thanks for this answer :) –  Sep 03 '16 at 07:17
  • 4
    Well the problem with `groupingBy(..)` is that it, well, groups its elements by some condition. So while it may be possible to solve it with `groupingBy()`, it is not a "natural" solution since you don't want to do any grouping. Just because it can be used to create a Map it does not mean it is a good fit for this kind of problem. – user140547 Sep 03 '16 at 07:17
1

Your original approach of using subList was interesting, as it doesn’t waste space nor CPU time for copies of the same data. You can do the same with the Stream API:

List<Integer> all = IntStream.range(0, 120).boxed().collect(Collectors.toList());
Map<Integer, List<Integer>> myMapOne = IntStream.range(0, all.size()).boxed()
    .collect(Collectors.toMap(Function.identity(), i -> all.subList(0, i)));

Note that this doesn’t use indexOf as it’s not necessary, given the predictable contents of the List. You can elide it in your loop variant as well.


But, of course, you actually don’t need real storage for this List at all, so, if you are willing to write a bit more code, you can use:

final class NaturalNumbers extends AbstractList<Integer> implements RandomAccess {
    final int limit;
    NaturalNumbers(int limit) {
        this.limit = limit;
    }
    public int size() { return limit; }
    public Integer get(int index) { return index; }
    // the next two are not required, but nice to have
    @Override public void forEach(Consumer<? super Integer> action) {
        spliterator().forEachRemaining(action);
    }
    @Override public Spliterator<Integer> spliterator() {
        return IntStream.range(0, limit).spliterator();
    }
}
Map<Integer, List<Integer>> myMapOne = IntStream.range(0, 120).boxed()
    .collect(Collectors.toMap(Function.identity(), NaturalNumbers::new));

Just for completeness, using groupingBy here, would be possible when using the flatMapping collector shown at the end of this answer:

Map<Integer, List<Integer>> myMapOne = IntStream.range(0, 120).boxed()
    .collect(Collectors.groupingBy(Function.identity(),
        flatMapping(i -> IntStream.range(0, i).boxed(), Collectors.toList())));

But for this task, it’s no win compared to the other solutions.

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
  • Thank you for teaching me this (especially `function.Identity` that can be very handy :) –  Sep 05 '16 at 11:53