13

I want to flatten nested arrays like:

[[[1],2],[3]],4] -> [1,2,3,4] 

manually in java I can't find a clue ! :S

I have tried a manual java script guide but it doesn't get a solution

public static void main(String[] args) {

  Object arr[] = { 1, 2, new Object[] { 4, new int[] { 5, 6 }, 7 }, 10 };
  String deepToString = Arrays.deepToString(arr);
  String replace = deepToString.replace("[", "").replace("]", "");
  String array[] = replace.split(",");
  int temp[] = new int[array.length];
  for (int i = 0; i < array.length; i++) {
    temp[i] = Integer.parseInt(array[i].trim());
  }
  System.out.println(Arrays.toString(temp));
}
Jamiu S.
  • 5,257
  • 5
  • 12
  • 34
mhGaber
  • 167
  • 1
  • 1
  • 4
  • 6
    This is a javascript question. This doesn't make sense in Java. – dotvav Aug 06 '15 at 09:14
  • I want to solve it in any language, java or javascript – mhGaber Aug 06 '15 at 09:15
  • we really want to help but I, for one, don't understand what your asking for. cn you give a Java example for input and desired output? – Sharon Ben Asher Aug 06 '15 at 09:16
  • 2
    But the method of solving that will be entirely different in Java than in JavaScript... – Michał Szydłowski Aug 06 '15 at 09:16
  • Java does not have mixed-type arrays (where an item could be either an `int` or an `int[]`). It has `List` objects though... – dotvav Aug 06 '15 at 09:18
  • I have edited the question to show inputs and outputs, hope it's clear now. – mhGaber Aug 06 '15 at 09:27
  • Java can have mixed type arrays of objects such as `Object[]` which would accept `Object[]` or `Integer` as elements for example. One such array containing nested arrays of `Integer` objects could be `new Object[]{1, 2, new Object[]{3, 4, new Object[]{5}, 6, 7}, 8, 9, 10}` – conorgriffin Feb 16 '16 at 13:48
  • I had this question in an interview........My question is.......why would anyone ever do this? This is a sincere question. – Nathan Donaldson Feb 23 '21 at 09:33

9 Answers9

22

The 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[] a? flatten(a): Stream.of(o));
}

or prior to JDK 16

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(flatInt));
Holger
  • 285,553
  • 42
  • 434
  • 765
  • Sadly, this doesn't work if the inner arrays are of primitive type like int[], because the `o instanceof Object[]` check fails and primitive arrays cannot be cast to object arrays. – Doopy Aug 29 '18 at 12:03
  • 1
    @DQQpy for primitive arrays, no recursion is necessary, as primitive arrays can’t contain arrays, so you could use `.flatMap(o -> o instanceof int[]? Arrays.stream((int[])o).boxed(): o instanceof Object[]? flatten((Object[])o): Stream.of(o));` or, to invariably map to `int` values, `public static IntStream flattenToInt(Object o) { return o instanceof int[]? Arrays.stream((int[])o): o instanceof Object[]? Arrays.stream((Object[])o) .flatMapToInt(x -> flattenToInt(x)): IntStream.of(((Number)o).intValue()); }` – Holger Aug 29 '18 at 12:29
  • 1
    If the input type is a regular array like `int[][][]`, the operation would be even simpler, as no recursion would be necessary, you just need *n-1* `flatMap` steps for *n* dimensions, `static IntStream flatten(int[][][] array) { return Arrays.stream(array).flatMap(Arrays::stream) .flatMapToInt(Arrays::stream); }` – Holger Aug 29 '18 at 12:34
  • Good point, but if the inner arrays are of mixed types it becomes really complicated with all the case differentations. I can't find a better solution though – Doopy Aug 29 '18 at 13:24
  • 2
    @DQQpy you can handle all array types generically using `java.lang.reflect.Array`, if you wish: `static Stream flatten(Object o) { return o != null && o.getClass().isArray()? IntStream.range(0, Array.getLength(o)).mapToObj(ix -> Array.get(o, ix)).flatMap(x -> flatten(x)): Stream.of(o); }` – Holger Aug 29 '18 at 13:40
10

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}}
        );
    }

}
conorgriffin
  • 4,282
  • 7
  • 51
  • 88
4

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)

Alex Joseph
  • 4,719
  • 2
  • 21
  • 25
2

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), []);
}
Renan Cidale
  • 842
  • 2
  • 10
  • 23
1

This is how I solved this problem in Java:

public class ArrayUtil {

    /**
     * Utility to 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 inputList
     */
    public static Integer[] flattenArray(ArrayList<Object> inputList) {

        ArrayList<Integer> flatten = new ArrayList<Integer>();
        if (inputList.size() <= 0) {
            return new Integer[0];                          // if the inputList is empty, return an empty Integer[] array.
        }

        for (Object obj : inputList) {
            recursiveFlatten(flatten, obj);                 // otherwise we can recursively flatten the input list.
        }

        Integer [] flatArray = new Integer[flatten.size()];
        return flatArray = flatten.toArray(flatArray);      
    }

    /**
     * Recursively flatten a nested array.
     * @param flatten
     * @param o
     */
    private static void recursiveFlatten(ArrayList<Integer> flatten, Object o){
        if(isInteger(o)){                               // if the object is of type Integer, just add it into the list.
            flatten.add((Integer)o);
        } else if(o instanceof ArrayList){              // otherwise, we need to call to recursively flatten the array
            for(Object obj : (ArrayList<Object>) o){    // for the case where there are deeply nested arrays.
                recursiveFlatten(flatten, obj);
            }
        }
    }

    /**
     * Return true if object belongs to Integer class,
     * else return false.
     * @param obj
     * @return
     */
    private static boolean isInteger(Object obj) {
        return obj instanceof Integer;
    }

}
Alien
  • 444
  • 2
  • 9
  • 28
1

It could be flattened by iterative approach.

static class ArrayHolder implements Iterator<Object> {
    private final Object[] elements;
    private int index = -1;

    public ArrayHolder(final Object[] elements) {
        this.elements = elements;
    }

    @Override
    public boolean hasNext() {
        return Objects.nonNull(elements) && ++index < elements.length;
    }

    @Override
    public Object next() {
        if (Objects.isNull(elements) || (index == -1 || index > elements.length))
            throw new NoSuchElementException();

        return elements[index];
    }
}


private static boolean hasNext(ArrayHolder current) {
    return Objects.nonNull(current) && current.hasNext();
}

private void flat(Object[] elements, List<Object> flattened) {
    Deque<ArrayHolder> stack = new LinkedList<>();
    stack.push(new ArrayHolder(elements));

    ArrayHolder current = null;
    while (hasNext(current)
            || (!stack.isEmpty() && hasNext(current = stack.pop()))) {
        Object element = current.next();

        if (Objects.nonNull(element) && element.getClass().isArray()) {
            Object[] e = (Object[]) element;
            stack.push(current);
            stack.push(new ArrayHolder(e));
            current = null;
        } else {
            flattened.add(element);
        }
    }
}

You can find the full source here You can use recursion to solve this problem.

private void flat(Object[] elements, List<Object> flattened) {
    for (Object element : elements)
    {
        if (Objects.nonNull(element) && element.getClass().isArray())
        {
            flat((Object[])element, flattened);
        }
        else
        {
            flattened.add(element);
        }
    }
}

Here is the link for recursion.

1

The recursive call approach will work for this case:

private static void recursiveCall(Object[] array) {

        for (int i=0;i<array.length;i++) {
            if (array[i] instanceof Object[]) {
                recursiveCall((Object[]) array[i]);
            }else {
                System.out.println(array[i]);
            }
            
    }
        
}
RiveN
  • 2,595
  • 11
  • 13
  • 26
0

package com.app;

import java.util.Arrays;

public class Test2 {

public static void main(String[] args) {

    Object arr[] = { 1, 2, new Object[] { 4, new int[] { 5, 6 }, 7 }, 10 };
    String deepToString = Arrays.deepToString(arr);
    String replace = deepToString.replace("[", "").replace("]", "");
    String array[] = replace.split(",");
    int temp[] = new int[array.length];
    for (int i = 0; i < array.length; i++) {
        temp[i] = Integer.parseInt(array[i].trim());
    }
    System.out.println(Arrays.toString(temp));
}

}

  • 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 Jan 05 '23 at 04:47
-5

you can try this code:

String a = "[[[1],2],[3]],4] ";
a= a.replaceAll("[(\\[|\\])]", "");
String[] b = a.split(",");
Beniamin
  • 56
  • 2