3

I am trying to Flatten a ArrayList with N-Depth. For this I tried using flapMap method of Stream API. I am able to get it. But I have to use flatMap() method repeatedly as per the number of list of lists. If I am using one more flatMap() method, it shows compile time error. Is there any way to dynamically get it done.

This is the code that I used:

List<Integer> list1 = Arrays.asList(4,5,6);
List<Integer> list2 = Arrays.asList(7,8,9);

List<List<Integer>> listOfLists = Arrays.asList(list1, list2);

List<List<List<Integer>>> listA = Arrays.asList(listOfLists);
List<List<List<List<Integer>>>> listB = Arrays.asList(listA);

List<Integer> listFinal = listB.stream()
    .flatMap(x -> x.stream())
    .flatMap(x -> x.stream())
    .flatMap(x -> x.stream())
    .collect(Collectors.toList());
//In the above line, If I use listA instead of listB, it is showing error.

listFinal.forEach(x-> System.out.println(x));
Bentaye
  • 9,403
  • 5
  • 32
  • 45
Klaus
  • 59
  • 1
  • 5
  • Does this answer your question: https://stackoverflow.com/questions/52670243/recursively-flatten-a-list-with-streams ? – Ortomala Lokni Mar 23 '22 at 15:46
  • No, Here they are passing flatten(final List nodes), But in my case, the input will be dynamic. So I used List> list as parameter. But I am unable to convert it to List of Lists inside the method. – Klaus Mar 24 '22 at 02:51
  • https://stackoverflow.com/q/32656888/1876620 there's a way to do something similar, maybe it serves as a source of inspiration – fps Apr 13 '22 at 00:43

4 Answers4

2

This seems to work for me, using recursion. When given a list

  • check if it is a list of Integer, in which case return it.
  • otherwise it is a list of lists. FlatMap it (to remove 1 level of nesting), and flatten the resulting list.
private static List<Integer> flatten(List<?> list) {
    if (list.get(0) instanceof Integer) {
        return (List<Integer>) list;
    }

    List<List<?>> listOfLists = (List<List<?>>) list;
    return flatten(listOfLists.stream()
            .flatMap(Collection::stream)
            .collect(Collectors.toList()));
}

then

public static void main(String[] args) {
    List<List<List<List<List<Integer>>>>> listC = Arrays.asList(
            Arrays.asList(
                    Arrays.asList(
                            Arrays.asList(
                                    Arrays.asList(0, 1),
                                    Arrays.asList(2, 3, 4)
                            ),
                            Arrays.asList(
                                    Arrays.asList(5),
                                    Arrays.asList(6, 7),
                                    Arrays.asList(8, 9)
                            )
                    ),
                    Arrays.asList(
                            Arrays.asList(
                                    Arrays.asList(10, 11),
                                    Arrays.asList(12, 13, 14)
                            ),
                            Arrays.asList(
                                    Arrays.asList(15),
                                    Arrays.asList(16, 17),
                                    Arrays.asList(18, 19)
                            )
                    )
            ),
            Arrays.asList(
                    Arrays.asList(
                            Arrays.asList(
                                    Arrays.asList(20, 21),
                                    Arrays.asList(22, 23, 24)
                            ),
                            Arrays.asList(
                                    Arrays.asList(25),
                                    Arrays.asList(26, 27),
                                    Arrays.asList(28, 29)
                            )
                    ),
                    Arrays.asList(
                            Arrays.asList(
                                    Arrays.asList(30, 31),
                                    Arrays.asList(32, 33, 34)
                            ),
                            Arrays.asList(
                                    Arrays.asList(35),
                                    Arrays.asList(36, 37),
                                    Arrays.asList(38, 39)
                            )
                    )
            )
    );

    List<Integer> result = flatten(listC);

    System.out.println(listC);
    System.out.println(result);

}

prints

[[[[[0, 1], [2, 3, 4]], [[5], [6, 7], [8, 9]]], [[[10, 11], [12, 13, 14]], [[15], [16, 17], [18, 19]]]], [[[[20, 21], [22, 23, 24]], [[25], [26, 27], [28, 29]]], [[[30, 31], [32, 33, 34]], [[35], [36, 37], [38, 39]]]]]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39]

There is a bit of Unchecked casting going on, but not sure how to do without

Bentaye
  • 9,403
  • 5
  • 32
  • 45
0

For List<List<List<List<Integer>>>> listB, .stream().flatMap(.stream()).flatMap(.stream()).flatMap(.stream()).collect()

But for List<List<List<Integer>>> listA, .stream().flatMap(.stream()).flatMap(.stream()).collect().

See flatMap() count is just one less than the Generic depth.

Bentaye
  • 9,403
  • 5
  • 32
  • 45
Deep
  • 153
  • 11
0

I hope you know that deeply nested collections isn't the best way to represent the data and must be avoided (it's an almost certain indicator of faulty design). So I'll treat this question as a cryptic puzzle rather than a practical task.

You can achieve that without using recursion. But caution this approach is vicious as well recursion because as well recursive approach it requires to relinquish the type safety provided by generics (I've warned that you shouldn't do that in the first place).

To do that, you need to perform intanceof checks in a loop. And populate the resulting list of row type with elements of a nested list.

Note :

  • contrary to commonly used generic collections like List<Integer> which characterized as covariant (i.e. you can assign only collection of the same type to it) you're allowed and to assign anything to the list of row type and to modify it as well. Which is an unsafe combination, and hence usage of row type collections is highly discouraged.

The loop exits if the first element isn't a list.

public static void main(String[] args) {
    List<List<List<List<Integer>>>> source =
            List.of(List.of(List.of(List.of(4,5,6), List.of(7,8,9))));

    List<Integer> result = source.stream()
            .flatMap(list -> deepFlatten(list).stream())
            .collect(Collectors.toList());
    
    System.out.println(source);
    System.out.println(result);
}

public static List<Integer> deepFlatten(List<?> nestedList) {
    if (nestedList.isEmpty()) {
        return (List<Integer>) nestedList;
    }

    List result = new ArrayList<>();
    List current = nestedList;
    while (current.get(0) instanceof List<?>) {
        for (Object next: current) {
            result.addAll((List) next);
        }
        current = result;
        result = new ArrayList<>();
    }
    return (List<Integer>) current;
}

Output

[[[[4, 5, 6], [7, 8, 9]]]]
[4, 5, 6, 7, 8, 9]
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
0

The below snippet worked for me. Thanks for the response all.

List list1 = Arrays.asList(4,5,6); List list2 = Arrays.asList(7,8,9);

    List<List<Integer>> listOfLists = Arrays.asList(list1, list2);

    List< List< List<Integer> > > list = Arrays.asList(listOfLists);
    List< List< List<List<Integer>> > > listB = Arrays.asList(list);
    
    
    //List<Integer> listF = listB.stream().flatMap(x -> x.stream()).flatMap(x -> x.stream()).flatMap(x -> x.stream()).collect(Collectors.toList());
    
    List<Integer> flattenList = (List<Integer>) getList(listB);
    flattenList.forEach(x-> System.out.println(x));
    
    
}
public static List<?> getList(List<? > s) {
    
    if( s.isEmpty() || !( s.get(0) instanceof List) ) {
        return s;
    }
    
    return getList( (List<?>) s.stream().flatMap(x-> ((List)x).stream()).collect(Collectors.toList()));
}
Klaus
  • 59
  • 1
  • 5
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 13 '22 at 03:02