Flatten nested arrays in java

Java 8’s Stream API offers a compact and flexible solution. Using the method

private static Stream<Object> flatten(Object[] array) {
    return Arrays.stream(array)
                 .flatMap(o -> o instanceof Object[]? flatten((Object[])o): Stream.of(o));
}

you can perform the operation as

Object[] array = { 1, 2, new Object[]{ 3, 4, new Object[]{ 5 }, 6, 7 }, 8, 9, 10 };
System.out.println("original: "+Arrays.deepToString(array));

Object[] flat = flatten(array).toArray();
System.out.println("flat:     "+Arrays.toString(flat));

or when you assume the leaf objects to be of a specific type:

int[] flatInt = flatten(array).mapToInt(Integer.class::cast).toArray();
System.out.println("flat int: "+Arrays.toString(flat));

I created a class to solve this using Java, the code is also shown below.

Solution:

package com.conorgriffin.flattener;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Flattens an array of arbitrarily nested arrays of integers into a flat array of integers.
 * <p/>
 * @author conorgriffin
 */
public class IntegerArrayFlattener {

    /**
     * Flatten an array of arbitrarily nested arrays of integers into a flat array of integers. e.g. [[1,2,[3]],4] -> [1,2,3,4].
     *
     * @param inputArray an array of Integers or nested arrays of Integers
     * @return flattened array of Integers or null if input is null
     * @throws IllegalArgumentException
     */
    public static Integer[] flatten(Object[] inputArray) throws IllegalArgumentException {

        if (inputArray == null) return null;

        List<Integer> flatList = new ArrayList<Integer>();

        for (Object element : inputArray) {
            if (element instanceof Integer) {
                flatList.add((Integer) element);
            } else if (element instanceof Object[]) {
                flatList.addAll(Arrays.asList(flatten((Object[]) element)));
            } else {
                throw new IllegalArgumentException("Input must be an array of Integers or nested arrays of Integers");
            }
        }
        return flatList.toArray(new Integer[flatList.size()]);
    }
}

Unit Tests:

package com.conorgriffin.flattener;

import org.junit.Assert;
import org.junit.Test;

/**
 * Tests IntegerArrayFlattener
 */
public class IntegerArrayFlattenerTest {

    Integer[] expectedArray = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    @Test
    public void testNullReturnsNull() throws IllegalArgumentException {
        Assert.assertNull(
                "Testing a null argument",
                IntegerArrayFlattener.flatten(null)
        );
    }

    @Test
    public void testEmptyArray() throws IllegalArgumentException {
        Assert.assertArrayEquals(
                "Testing an empty array",
                new Integer[]{},
                IntegerArrayFlattener.flatten(new Object[]{})
        );
    }

    @Test
    public void testFlatArray() throws IllegalArgumentException {
        Assert.assertArrayEquals(
                "Testing a flat array",
                expectedArray,
                IntegerArrayFlattener.flatten(new Object[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
        );
    }

    @Test
    public void testNestedArray() throws IllegalArgumentException {
        Assert.assertArrayEquals(
                "Testing nested array",
                expectedArray,
                IntegerArrayFlattener.flatten(new Object[]{1, 2, 3, 4, new Object[]{5, 6, 7, 8}, 9, 10})
        );
    }

    @Test
    public void testMultipleNestedArrays() throws IllegalArgumentException {
        Assert.assertArrayEquals(
                "Testing multiple nested arrays",
                expectedArray,
                IntegerArrayFlattener.flatten(new Object[]{1, 2, new Object[]{3, 4, new Object[]{5}, 6, 7}, 8, 9, 10})
        );
    }

    @Test(expected = IllegalArgumentException.class)
    public void throwsExceptionForObjectInArray() throws IllegalArgumentException {
        IntegerArrayFlattener.flatten(
                new Object[]{new Object()}
        );
    }

    @Test(expected = IllegalArgumentException.class)
    public void throwsExceptionForObjectInNestedArray() throws IllegalArgumentException {
        IntegerArrayFlattener.flatten(
                new Object[]{1, 2, new Object[]{3, new Object()}}
        );
    }

    @Test(expected = IllegalArgumentException.class)
    public void throwsExceptionForNullInArray() throws IllegalArgumentException {
        IntegerArrayFlattener.flatten(
                new Object[]{null}
        );
    }

    @Test(expected = IllegalArgumentException.class)
    public void throwsExceptionForNullInNestedArray() throws IllegalArgumentException {
        IntegerArrayFlattener.flatten(
                new Object[]{1, 2, new Object[]{3, null}}
        );
    }

}

If it's a primitive array with only two levels, you could do:

Arrays.stream(array)
  .flatMapToInt(o -> Arrays.stream(o))
  .toArray()

to get the corresponding boxed array (which you can unbox if necessary)


That's the way I would solve it. Don't know which kind of efficiency you are looking for. But yeah. that does the job in JavaScript.

arr.toString().split(',').filter((item) => item).map((item) => Number(item))

A probably more efficient way to do this would be to use reduce and concat method from arr and recursion.

function flattenDeep(arr1) {
   return arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
}