Is there any way to convert a 2D List to 1D List using only `map` not using `flatMap`?
Asked Answered
I

5

5

I'm a Java beginner, I just got learn about map and flatMap.

When 2d List should be converted to 1d List, it was implemented like below.

List<List<Integer>> list_2d = List.of(List.of(1, 2), List.of(3, 4));

List<Integer> lst1 = list_2d
    .stream()
    .flatMap(arr -> arr.stream())
    .collect(Collectors.toList());
printAll(lst1); // [1, 2, 3, 4]

But, I think it looks that it can be implemented not using flatMap.

Are there any way to make the code with same logic, just using map, not using flatMap?

Just asking because if map can replace all of flatMap, there is no reason to memorize flatMap. I always pursue simple and basic things.

Impassion answered 20/12, 2020 at 10:14 Comment(3)
I don't think you can convert a single Stream element to multiple Stream elements without using flatMap. map only converts a single element into a single element of different type.Threescore
@OleV.V. Just because, if map can replaced all of flatMap, there are no reason to memorize flatMap. I always pursue simple and basic thing.Impassion
@Impassion - I always pursue simple and basic thing. - The flatMap is indeed a simple and basic thing. One of the reasons why Java became so popular is because of these simple constructs/types/operations; otherwise, in C/C++ we had to deal with many complexities just because there were no such simple and basic things available e.g. think of character and strings in Java and C/C++.Armoured
B
9

To answer the question as asked, there are other ways, but I wouldn’t recommend any that I can think of. For example you may use reduction:

    List<List<Integer>> list2d = List.of(List.of(1, 2), List.of(3, 4));

    List<Integer> lst1 = list2d
        .stream()
        .reduce((l1, l2) -> {
            ArrayList<Integer> concatenated = new ArrayList<>(l1);
            concatenated.addAll(l2);
            return concatenated;
        })
        .orElse(List.of()); // or else empty list
    
    System.out.println(lst1);

Output is the same as yours:

[1, 2, 3, 4]

But your code is much easier to understand than mine. I suggest you stick to it.

Can’t map() and flatMap() be interchanged?

Just because, if map can replaced all of flatMap, there are no reason to memorize flatMap. I always pursue simple and basic thing.

You already got the simplest and most basic thing there is. Also to get the full potential from streams, there are many method calls that you will need to use now and then. After having used streams for years, I still find myself looking them up in the Javadoc sometimes. I had to look up the details of reduce() for this answer. Don’t expect to have everything in your head. I do know flatMap() by heart, though, since it is often practical.

Edit: Only out of academic interest: Typically you can’t replace flatMap() with map(). Or you would have used map() from the outset. But the other way around: You can always replace map() with flatMap(). You just wouldn’t want to. For example, if we had:

    List<String> strings = List.of("short", "somewhat longer");
    List<Integer> lengths = strings.stream()
            .map(String::length)
            .collect(Collectors.toList());
    System.out.println(lengths);

[5, 15]

If for some strange reason we could only remember flatMap(), not map(), we could do:

    List<Integer> lengths = strings.stream()
            .flatMap(s -> Stream.of(s.length()))
            .collect(Collectors.toList());

I think it’s clear to see that all it brings us is needless complication, though. It’s better to remember both of map() and flatMap() at least well enough to be able to look them up when we need them.

Buna answered 20/12, 2020 at 10:32 Comment(0)
T
6

You can remove the flatMap if you perform the flat-mapping during collect:

List<Integer> lst1 = list_2d
    .stream()
    .collect(Collectors.flatMapping (List::stream, Collectors.toList()));

However, I don't see the advantage of using Collectors.flatMapping instead of flatMap.

Threescore answered 20/12, 2020 at 10:19 Comment(1)
You can also get rid of Collectors.flatMapping by using reduce directly.Roadhouse
C
5

May be this is not what you are looking for, but if the goal is to add all sublists to a one resulting list, using for each might also be an option in terms of simple and basic:

List<List<Integer>> list_2d = List.of(List.of(1, 2), List.of(3, 4));
List<Integer> result = new ArrayList<>();
list_2d.forEach(list -> result.addAll(list));
System.out.println(result);
Chibouk answered 20/12, 2020 at 10:49 Comment(0)
S
2

As there are already several answers providing different implementations, I will try to show the difference between map and flatMap.

The map operation is used for changing every element of a stream to exactly one other element. In your example, possible use-cases could be either doing some aggregation on the list (e. g. summing all elements), yielding a List<Integer> containing all the sums of the individual lists. Or you could change the data structure, maybe to a set, yielding a List<Set<Integer>>. The number of elements in your resulting list, will always be 2 as the original list had 2 elements.

The flatMap operation on the other hand is used to convert one element to zero or more elements. Consider a class Family consisting of a number of Humans. If you get a list of Families from somewhere, but you just care about the individual people, you can flatMap the Family list to a list of Humans.

The difference between flatMap and the implementations given in the other answers is the type of operation: flatMap is an intermediate operation, meaning the result is a Stream. collect and reduce are terminal operations, meaning the result is something different (in your example: a List<Integer>. If you want to perform more stream-operations (e. g. filter or another map) you would have to create another Stream of the resulting list.

To illustrate this, consider having the list given in your question and as a result you want to have all even numbers from the list. I am using the collect solution from this answer. As you can see, using flatMap saves an unnecessary creation of a List as well as some lines of code.

public static void usingFlatMap() {
    List<List<Integer>> originalList = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4));

    List<Integer> result = originalList
            .stream()
            .flatMap(list -> list.stream())
            .filter(number -> number % 2 == 0)
            .collect(Collectors.toList());

    System.out.println(result); // [2, 4]
}

public static void usingCollect() {
    List<List<Integer>> originalList = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4));

    List<Integer> intermediateList = originalList
            .stream()
            .collect(Collectors.flatMapping(List::stream, Collectors.toList()));
    List<Integer> result = intermediateList
            .stream()
            .filter(number -> number % 2 == 0)
            .collect(Collectors.toList());

    System.out.println(result); // [2, 4]
}
Susian answered 20/12, 2020 at 12:19 Comment(0)
M
0

You can iterate over this 2d-list and add elements (integers) from it to another 1d-list:


  1. List<List<Integer>> list_2d = List.of(List.of(1, 2), List.of(3, 4));
    
    List<Integer> list_1d = new ArrayList<>();
    
    IntStream.range(0, list_2d.size()).forEach(i ->
            IntStream.range(0, list_2d.get(i).size()).forEach(j ->
                    list_1d.add(list_2d.get(i).get(j))));
    
    System.out.println(list_1d); // [1, 2, 3, 4]
    

  1. List<List<Integer>> list_2d = List.of(List.of(1, 2), List.of(3, 4));
    
    List<Integer> list_1d = new ArrayList<>();
    
    list_2d.forEach(list ->
            list.forEach(element ->
                    list_1d.add(element)));
    
    System.out.println(list_1d); // [1, 2, 3, 4]
    
Muttonhead answered 22/12, 2020 at 18:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.