2
\$\begingroup\$

The function below is part of an overall chunk of code to partition an array of size M into sub-arrays of size N. The function in question is written to handle the case: what if M isn't divisible by N? Then, the way the rest of the code works, the last element in the partitioned array might have fewer than N items. (E.g., if N = 3, there might be an array like [[A, B, C], [D, E, F], [G, H]].

There are two reasonable solutions: (a) just accept a shorter array at the end (maybe with padding, depending on use case), or (b) take the shorter array and distribute it among the remaining arrays. This code implements solution (b).

function mushInLittleArray(arrs, size){
    var correctSize = arrs[arrs.length - 1].length == size;
    if (correctSize) {
        return arrs;
    }
    var last = arrs.pop();
    var len = last.length;
    var j = 0;
    var div = false;
    var arrlen = arrs.length;
    for (var i = 0; i < len; i++) {
        div = divisible(j, arrlen);
        if (div) {
            j = 0;
        }
        arrs[j].push(last[i]);
        j++;
    }
    return arrs;
}

This code just feels awkward and hackish to me. In particular, the sleight of hand in the conditional toward the end beginning if (div) smells bad. It's meant to deal with the edge case where leftover group is larger than number of other groups, for example, where N = 5 and we have something like [[A, B, C, D, E], [F, G, H, I, J], [K, L, M, N]], and hence the iteration over the large array needs to reset. The code as written seems to work fine, but having two loop variables and modifying one of them manually within the block seems like dirty pool to me. Surely there must be a better way?

\$\endgroup\$
2
\$\begingroup\$

Based on your constraints, which are:

  • Single loop variable
  • No restarting of iteration over large arrays

You could calculate your looping step sizes first, then go over the array only once.

  const mushedArr = [];
  const remainderSize = arr.length % size
  const numberOfChunks = Math.floor(arr.length / size);
  let remainderStepSize = Math.floor(remainderSize / numberOfChunks);

I'm going to define the remainder with negative index slicing, so there has to be a guard against calculating a remainder even when division is exact.

let remainder = remainderSize ? arr.slice(-remainderSize) : []

Second edge case is where the remainder is smaller than the chunk size, which would cause my step size to evaluate to 0 and not loop through the remainder properly.

if(remainderStepSize === 0) remainderStepSize ++;

Finally, loop over the array and the remainder:

 let i = 0;
  while(i < numberOfChunks){
    let remainderPortion = remainder
        .slice(i*remainderStepSize, i*remainderStepSize+remainderStepSize);

    let arrayPortion = arr.slice(i*size, i*size+size);

    mushedArr.push([...arrayPortion, ...remainderPortion]);
    i++;
  };
  return mushedArr;
};

Variable names here could be shorter, and the slice can be simplified to something like arr.slice(size*i, size*(i+1), but the idea is to loop over the array and copy the items in sizes that are equal to the chunk size.

Testing for both inputs in your question yielded:

Calling function with input: [A,B,C,D,E,F,G,H,I,J,K,L,M,N]
 chunk size: 5
[ [ 'A', 'B', 'C', 'D', 'E', 'K', 'L' ],
  [ 'F', 'G', 'H', 'I', 'J', 'M', 'N' ] ]
Calling function with input: [A,B,C,D,E,F,G,H]
 chunk size: 3
[ [ 'A', 'B', 'C', 'G' ], [ 'D', 'E', 'F', 'H' ] ]
| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Welcome to Code Review! Thanks for supplying an answer! I'd suggest you utilize markdown formatting instead of HTML since it requires fewer keystrokes and not all HTML tags are supported. \$\endgroup\$ – Sᴀᴍ Onᴇᴌᴀ Mar 13 at 0:10
  • \$\begingroup\$ Thanks for the pointer. Updated the post to md format. \$\endgroup\$ – Matheus Deister Veiga Mar 13 at 0:42
1
\$\begingroup\$

For solution

(a) - just accept a shorter array at the end

code can be very short

function mushInLittleArray(arr, size) {
  let resultArr = [];
  for (let i = 0; i < (arr.length / size); i++ ) {
    resultArr.push( arr.slice( i * size, (i+1) * size ) );
  }
  return resultArr;
}
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.