Home > Back-end >  How to reshape a flattened array to a multidimensional array in arbitray shape in Javascript?
How to reshape a flattened array to a multidimensional array in arbitray shape in Javascript?

Time:10-16

[what I have]

An flattened, or 1-dimentional, array with n elements, e.g. the array A with 12 items below.

const A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 

[what i want to do]

Reshape A to a multidimensional array B whose shape is determined by array C which may vary depending on other conditions.

// scenario 1 
const C = [ 2, 3, 2 ] 
// reshaped A should be: 
// [[[1, 2], 
//   [3, 4],  
//   [5, 6]],
//  [[7, 8], 
//   [9, 10], 
//   [11, 12]]]

// scenario 2
const C = [ 3, 4 ]
// reshaped A should be: 
// [[1, 2, 3, 4], 
//  [5, 6, 7, 8], 
//  [9, 10, 11, 12]]

// and so on... 

[what I have tried]

I found the follolwing reference which can unflattend a flattened array to a 2-dimensional array by arbitray number of rows: Unflatten Arrays into groups of fours [closed]

function arrayUnflatten (_flattenedArray, _numRows) {
    const len = _flattenedArray.length;
    const unflattenedArray = []; 
    while (_flattenedArray.length > 0) unflattenedArray.push(_flattenedArray.splice(0, _numRows));
    return unflattenedArray; 
}

[what I haven't figured out]

I haven't figured out how to make a "dynamically nested for loop", or some kind of recursion is needed, to reshape the 1-D array to a multidimensional arry in arbitrary shape determied by another array.

Help is appreciated.

CodePudding user response:

Here's my solution:


const A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const C = [3, 4];

function unflattenArray(arr, dim) {
  let elemIndex = 0;

  if (!dim || !arr) return [];

  function _nest(dimIndex) {
    let result = [];

    if (dimIndex === dim.length - 1) {
      result = result.concat(arr.slice(elemIndex, elemIndex   dim[dimIndex]));
      elemIndex  = dim[dimIndex];
    } else {
      for (let i = 0; i < dim[dimIndex]; i  ) {
        result.push(_nest(dimIndex   1));
      }
    }

    return result;
  }
  return _nest(0);
}

console.log(unflattenArray(A, C));
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>


_nest is a recursive function. Because of closure it can access elemIndex, arr and dim. elemIndex refers to the index of the current element being read from array arr.

_nest takes the index of the current dimension: dimIndex. It's first called with 0.

  • If dimIndex refers to the last dimension in dim array, then a 1D array of the corresponding size has to be returned. This is the base condition of recursion. It returns a slice of arr starting at elemIndex, of size dim[dimIndex].
  • Else it makes a call to _nest with the next dimension, dim[dimIndex] times. For e.g when dimIndex is 0 i.e size is 3 => we need 3 arrays hence we call _nest thrice with the size of each array which is 4 in the above example.

CodePudding user response:

Recursion is a functional heritage and so using it with functional style yields the best results. This means avoiding things like mutation, variable reassignments, and other side effects.

Additionally, your input is over-specified. The last number isn't needed as the output can be determined without it -

function nest (t, q) {
  if (q.length < 1)
    return t
  else
    return cut(t, t.length/q[0]).map(r => nest(r, q.slice(1)))
}

function cut (t, n) {
  if (n >= t.length)
    return [t]
  else
    return [t.slice(0,n), ...cut(t.slice(n), n)]
}

const A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 

console.log(JSON.stringify(nest(A, [3])))
console.log(JSON.stringify(nest(A, [2,3])))
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

[[1,2,3,4],
 [5,6,7,8],
 [9,10,11,12]]

[[[1,2],
  [3,4],
  [5,6]],
 [[7,8],
  [9,10],
  [11,12]]]

⚠️ Expect unexpected output when q inputs are not factors of t.length

  • Related