How to Fold an Array in Java


The challenge

In this challenge, you have to write a method that folds a given array of integers by the middle x-times.

An example says more than a thousand words:

Fold 1-times:
[1,2,3,4,5] -> [6,6,3]

A little visualization (NOT for the algorithm but for the idea of folding):

 Step 1         Step 2        Step 3       Step 4       Step5
                     5/           5|         5\          
                    4/            4|          4\      
1 2 3 4 5      1 2 3/         1 2 3|       1 2 3\       6 6 3
----*----      ----*          ----*        ----*        ----*


Fold 2-times:
[1,2,3,4,5] -> [9,6]

As you see, if the count of numbers is odd, the middle number will stay. Otherwise, the fold-point is between the middle-numbers, so all numbers would be added in a way.

The array will always contain numbers and will never be null. The parameter runs will always be a positive integer greater than 0 and say how many runs of folding your method has to do.

If an array with one element is folded, it stays as the same array.

The input array should not be modified!

The solution in Java code

Option 1:

public class Solution {
  public static int[] foldArray(int[] a, int r) {
    int[] f = java.util.Arrays.copyOfRange(a,0,(int)Math.ceil(a.length/2.));
    for (int i=0; i<a.length/2; i++) f[i] += a[a.length-1-i];
    return f.length==1|r==1 ? f : foldArray(f,--r);
  }
}

Option 2:

import java.util.Arrays;
import java.util.stream.IntStream;

public class Solution {
  public static int[] foldArray(int[] array, int runs) {
    final int[] result = Arrays.copyOfRange(array, 0, Math.round(array.length / 2.0f));
    IntStream.range(0, array.length / 2)
             .forEach(i -> result[i] = array[i] + array[array.length - 1 - i]);
    return runs > 1 ? foldArray(result, --runs) : result;
  }
}

Option 3:

import java.util.*;

public class Solution {
    public static int[] foldArray(int[] array, int runs) {
        int[] arr = Arrays.copyOf(array, array.length);
        int bound = arr.length;
        for (int k = 0; k < runs; k++) {
            for (int i = 0; i < bound / 2; i++) {
                arr[i] += arr[bound - 1 - i];
            }
            bound = (bound + 1) / 2;
        }
        return Arrays.copyOf(arr, bound);
    }
}

Test cases to validate our solution

import org.junit.Test;
import static org.junit.Assert.assertEquals;
import org.junit.runners.JUnit4;
import java.util.Arrays;

public class SolutionTests {
    @Test
    public void basicTests() {
        int[] input = new int[] { 1, 2, 3, 4, 5 };
        int[] expected = new int[] { 6, 6, 3 };
        assertEquals(Arrays.toString(expected), Arrays.toString(Solution.foldArray(input, 1)));

        expected = new int[] { 9, 6 };
        assertEquals(Arrays.toString(expected), Arrays.toString(Solution.foldArray(input, 2)));

        expected = new int[] { 15 };
        assertEquals(Arrays.toString(expected), Arrays.toString(Solution.foldArray(input, 3)));

        input = new int[] { -9, 9, -8, 8, 66, 23 };
        expected = new int[] { 14, 75, 0 };
        assertEquals(Arrays.toString(expected), Arrays.toString(Solution.foldArray(input, 1)));
    }

    @Test
    public void extendedTests() {
        int[] input = new int[] { 1, 2, 3, 4, 5, 99, 88, 78, 74, 73 };    
        assertEquals(Arrays.toString(new int[] { 427 }), Arrays.toString(Solution.foldArray(input, 5)));

        input = new int[] { -1, -2, -3, -4, -5, -99, -88, -78, -74, -73 };    
        assertEquals(Arrays.toString(new int[] { -427 }), Arrays.toString(Solution.foldArray(input, 5)));

        input = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };    
        assertEquals(Arrays.toString( new int[] { 0 }), Arrays.toString(Solution.foldArray(input, 10)));

        input = new int[] { 2 };
        assertEquals(Arrays.toString(input), Arrays.toString(Solution.foldArray(input, 1)));
    
        input = new int[] { 2 };
        assertEquals(Arrays.toString(input), Arrays.toString(Solution.foldArray(input, 20)));
    }
    
    interface FoldArrayInterface {
      int[] foldArray(int[] array, int runs);
    }
    
    @Test
    public void randomTests() {
    
      FoldArrayInterface myMethod = new FoldArrayInterface() {
        public int[] foldArray(int[] array, int runs) {
          int length = (int)Math.ceil(array.length/2.0);
          int[] foldedArray = new int[length];
    
          for(int i=0;i<length;i++) {
            if(i != array.length - 1 - i) {
              foldedArray[i] = array[i] + array[array.length - 1 - i];
            } else {
              foldedArray[i] = array[i];
            }
          }
    
          if(runs == 1) {
            return foldedArray;
          }
          return foldArray(foldedArray, runs - 1);
        }
      };
    
      for(int r=0;r<20;r++) {
        int length = (int)(Math.random() * 199 + 1);
        int runs = (int)(Math.random() * 19 + 1);
        int[] input = new int[length];
        for(int i=0;i<length;i++) {
          input[i] = (int)(Math.random() * 401 - 200);
        }        
      
        int[] expected = myMethod.foldArray(input, runs);
        assertEquals(Arrays.toString(expected), Arrays.toString(Solution.foldArray(input, runs)));
      }
    }
}