jvvgg
jvvgg

Reputation: 25

How can I change the size of a 2D array?

How would I change the size of a 2d array without rearranging the numbers in an array mainly using for loops and only built in methods? For example, {{4,5,6,7,10}, {8,4,3,9,13}} to become {{4}, {5,6}, {7,10,8}, {4,3,9,13}}.

I tried making the 2d array into two separate parts then adding it back to a new 2d array. I'm not sure how I would create the second 2d array.

Upvotes: 1

Views: 286

Answers (4)

Andreas
Andreas

Reputation: 159096

Here's one way to do it, using pure Java code (e.g. loops) and no methods at all.

static int[][] toTriangle(int[][] input) {
    // Count number of values
    int valueCount = 0;
    for (int[] inputRow : input)
        valueCount += inputRow.length;
    if (valueCount == 0)
        return new int[0][0];
    
    // Determine number of result rows, and how many values that can store
    int resultRowCount = 0, resultCount = 0;
    for (int row = 0; resultCount < valueCount; row++) {
        resultRowCount++;
        resultCount += row + 1;
    }
    int oversize = resultCount - valueCount;
    
    // Build result jagged array (last row is likely truncated)
    int[][] result = new int[resultRowCount][];
    for (int row = 0; row < resultRowCount - 1; row++)
        result[row] = new int[row + 1];
    result[resultRowCount - 1] = new int[resultRowCount - oversize];
    
    // Copy values
    int inputRow = 0, inputCol = 0;
    for (int[] resultRow : result) {
        for (int i = 0; i < resultRow.length; i++) {
            while (inputCol == input[inputRow].length) {
                inputRow++;
                inputCol = 0;
            }
            resultRow[i] = input[inputRow][inputCol++];
        }
    }
    
    return result;
}

Tests

test(new int[][] {{4,5,6,7,10}, {8,4,3,9,13}});
test(new int[][] {{1,2,3,4}, {}, {5,6,7}});
static void test(int[][] input) {
    print("Input", input);
    print("Result", toTriangle(input));
}
static void print(String label, int[][] arr) {
    System.out.println(label + ":");
    for (int[] row : arr) {
        System.out.print("  [");
        String sep = "";
        for (int val : row) {
            System.out.print(sep + val);
            sep = ", ";
        }
        System.out.println("]");
    }
}

Outputs

Input:
  [4, 5, 6, 7, 10]
  [8, 4, 3, 9, 13]
Result:
  [4]
  [5, 6]
  [7, 10, 8]
  [4, 3, 9, 13]
Input:
  [1, 2, 3, 4]
  []
  [5, 6, 7]
Result:
  [1]
  [2, 3]
  [4, 5, 6]
  [7]

Notice how the last row is truncated in length, since there are not enough values to fill a triangle. The second example also shows that it can handle empty sub-arrays in the input.

Upvotes: 0

WJS
WJS

Reputation: 40034

There are many ways to do this. Here is one that works for any triangular number
of elements that originate in a 2D array.

First, flatten them all into a single array.

int[] oneD = Arrays.stream(src).flatMapToInt(Arrays::stream).toArray();

Then reversing the length which should be k where k = n(n+1)/2 the number of rows would be the following. And the 2D result is partially allocated.

int rows = (int)(Math.sqrt(2*oneD.length + .25) -.5);
int[][] dest = new int[rows][];

Now it's just a matter of copying from the oneD array to the destination.

for (int i = 0, s = 0, e = 1; i < rows; i++) {
    dest[i] = Arrays.copyOfRange(oneD, s, e );
    s = e;
    e += i+2;
}

for (int[] arr : dest) {
   System.out.println(Arrays.toString(arr));
}   

Prints

[4]
[5, 6]
[7, 10, 8]
[4, 3, 9, 13]

Upvotes: 1

Thiyanesh
Thiyanesh

Reputation: 2360

A possible not-recommended solution

  1. Flatten the 2d array
  2. Collect with grouping on summation sequence
  3. It might be improved to use single atomic counter (i am unable to think)
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class ArrayResize {

    public static void main(String[] args) {
        Integer[][] input = new Integer[][] {{10, 11, 12 ,13, 14}, {15, 16, 17, 18, 19}};
        AtomicInteger index = new AtomicInteger(0);
        AtomicInteger group = new AtomicInteger(1);
        Collection<List<Integer>> out = Arrays.stream(input)
            .flatMap(i -> Arrays.stream(i)) // flatten
            .collect(Collectors.groupingBy(i -> {
                final int currentIndex = index.getAndIncrement();
                return (group.get() * (group.get() + 1)) / 2 == currentIndex
                    ? group.incrementAndGet() // increment group if index matches the summation of the range
                    : group.get(); // return the group
            }))
            .values();
        System.out.println(out);
    }
}

output has to be any Integer array

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class ArrayResize {

    public static void main(String[] args) {
        Integer[][] input = new Integer[][] {{10, 11, 12 ,13, 14}, {15, 16, 17, 18, 19}};
        AtomicInteger index = new AtomicInteger(0);
        AtomicInteger group = new AtomicInteger(1);
        Integer[][] output = Arrays.stream(input)
            .flatMap(i -> Arrays.stream(i)) // flatten
            .collect(Collectors.groupingBy(i -> {
                final int currentIndex = index.getAndIncrement();
                return (group.get() * (group.get() + 1)) / 2 == currentIndex
                    ? group.incrementAndGet() // increment group if index matches the summation of the range
                    : group.get(); // return the group
            })).values().stream() // stream collection
            .map(subgroup -> subgroup.toArray(new Integer[0])) // map subgroups to arrays
            .collect(Collectors.toList()) // collect as list of arrays
            .toArray(new Integer[0][0]); // convert to array

        System.out.println(Arrays.deepToString(output));
    }
}

Disclaimer

  1. I would not write the above code in any of my production systems
  2. At the current state, it will fail with parallel stream usage

Solution using index count

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

public class ArrayResize {
    public static void main(String[] args) {
        Integer[][] input = new Integer[][] {{10, 11, 12 ,13, 14}, {15, 16, 17, 18, 19}};
        List<Integer> flattened = Arrays.stream(input).flatMap(i -> Arrays.stream(i)).collect(Collectors.toList());
        List<Integer[]> temporary = new ArrayList<>();
        final int count = flattened.size();
        int start = 0;
        int range = 1;
        while (start < count) {
            temporary.add(flattened.subList(start, Math.min(start + range, count)).toArray(new Integer[0]));
            start += range;
            range++;
        }
        Integer[][] result = temporary.toArray(new Integer[0][0]);
        System.out.println(Arrays.deepToString(result));
    }
}

Upvotes: 0

If I understood your question, I recommend you use List<List<Integer>>, for example ArrayList, insted of pure arrays, and you can set your aggregations like this:

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

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

    //  add {4,5,6,7,10} to array, result = {{4,5,6,7,10}}
    values = new ArrayList<>(Arrays.asList(4,5,6,7,10));
    matrix.add(values);

    //  add {8,4,3,9,13} to last array, result = {{4,5,6,7,10}, {8,4,3,9,13}}
    values = new ArrayList<>(Arrays.asList(8,4,3,9,13));
    matrix.add(values);

    System.out.println("start="+ matrix);

    //  reset array or you can use another array to copy
    matrix = new ArrayList<>();

    values = new ArrayList<>(Arrays.asList(4));
    matrix.add(values);

    values = new ArrayList<>(Arrays.asList(5,6));
    matrix.add(values);

    values = new ArrayList<>(Arrays.asList(7,10,8));
    matrix.add(values);

    values = new ArrayList<>(Arrays.asList(4,3,9,13));
    matrix.add(values);

    System.out.println("end="+ matrix);

Output: start=[[4, 5, 6, 7, 10], [8, 4, 3, 9, 13]] end=[[4], [5, 6], [7, 10, 8], [4, 3, 9, 13]]

Upvotes: 0

Related Questions