-1

let's say i have an array [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and i want to split it into n parts let's say 3 part so the result is supposed to be [ 1, 2, 3, 4 ],[ 4, 5, 6, 7],[ 8, 9, 10 ] but the code i have right now is or O(n*m) which is bad. Is there an optimal way of doing this?

const items = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const n = 3
const result = [[], [], []]

const wordsPerLine = Math.ceil(items.length / 3)

for (let line = 0; line < n; line++) {
  for (let i = 0; i < wordsPerLine; i++) {
     const value = items[i + line * wordsPerLine]
      if (!value) continue //avoid adding "undefined" values
      result[line].push(value)
  }
}

5 Answers5

3

Assuming the result needs to be [[ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10 ]] you could use Array.reduce:

const items = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

const parts = 3
const wordsPerLine = Math.ceil(items.length / parts)

const result = items.reduce((resultArray, item, index) => {
  const arrayIndex = Math.floor(index / wordsPerLine)
  if (!resultArray[arrayIndex]) {
    resultArray[arrayIndex] = [] // start a new array
  }
  resultArray[arrayIndex].push(item)
  return resultArray
}, [])

// result => [[ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10 ]]
Lennert
  • 153
  • 7
2

We can write chunk(arr, size) using inductive reasoning -

  1. If arr.length is less than or equal to size, there are no more chunks to make. Return the singleton chunk of [ arr ]
  2. Otherwise (inductive) arr.length is greater than size, there is at least one chunk to make. Slice one chunk off the left of the array and prepend it to the result of the recursive sub-problem.

function chunk(arr, size) {
  if (arr.length <= size)
    return [ arr ]                                                 // 1
  else
    return [ arr.slice(0, size), ...chunk(arr.slice(size), size) ] // 2
}

const a =
  [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

const size =
  Math.ceil(a.length / 3)

const result =
  chunk(a, size)

console.log(
  JSON.stringify(result)
)
[[0,1,2,3],[4,5,6,7],[8,9]]

Visualize the evaluation -

chunk([0,1,2,3,4,5,6,7,8,9], 4)
[ [0,1,2,3], ...chunk([4,5,6,7,8,9], 4) ]
[ [0,1,2,3], ...[ [4,5,6,7], ...chunk([8,9], 4) ] ]
[ [0,1,2,3], ...[ [4,5,6,7], ...[ [8,9] ] ] ]
[ [0,1,2,3], ...[ [4,5,6,7], [8,9] ] ]
[ [0,1,2,3], [4,5,6,7], [8,9] ]
Mulan
  • 129,518
  • 31
  • 228
  • 259
1

You could also use array.slice.

As the documentation reads, given the correct parameters, it will return a subset of your array, which you can then store into a new one.

For instance:

let n = 3;
let i = 0;
let resultArray = [];
let startArray = [1,2,3,4,5,6,7,8,9,10];
let arrayPortion = [];
const wordsPerLine = Math.ceil(startArray.length / n);

do {
  arrayPortion = startArray.slice(n*i, n*(i+1));
  resultArray.push(arrayPortion);
  i++;
} while (arrayPortion.length==n && i*n<startArray.length); // Your end conditions.

This should work for any n.

1

To split up the array as evenly as possible:

function split_array(a, nparts) {
  const quot = Math.floor(a.length / nparts)
  const rem = a.length % nparts
  var parts = []
  for (var i = 0; i < nparts; ++i) {
    const begin = i * quot + Math.min(rem, i)
    const end = begin + quot + (i < rem)
    parts.push(a.slice(begin, end))
  }
  return parts
}

var chunks = split_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3)
console.log(JSON.stringify(chunks))

Output:

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

The size of each part will never differ by more than 1.

Matt
  • 20,108
  • 1
  • 57
  • 70
  • Splice also has O(n) complexity right? So is this O(n^2) ? – sojin Jul 05 '22 at 15:02
  • Overall this algorithm is O(n), since the original array is never iterated over more than once. The `for` loop is iterated over `nparts` times and each call to slice is of cost `O(n / nparts)` roughly speaking. So `nparts * O(n / nparts) = O(n)` if you can forgive my abuse of the big-Oh notation to make this point. – Matt Jul 05 '22 at 15:09
0

With O(n)

const items =  [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20]
  const partitionSize = Math.ceil(items.length / 3)
  const result = [[], [], []]
  let resultIndex = 0, subArrayIndex = 0;
  for (let line = 0; line < items.length; line++) {
     result[resultIndex].push(items[line])
     subArrayIndex++;
     if(subArrayIndex == partitionSize) {
     resultIndex++;
     subArrayIndex = 0;
     }
  }

Result:

 0: (7) [1, 2, 3, 4, 5, 6, 7]
1: (7) [8, 9, 10, 11, 12, 13, 14]
2: (6) [15, 16, 17, 18, 19, 20]
Bhushan Uniyal
  • 5,575
  • 2
  • 22
  • 45
  • this doesn't work for const items = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20] like i want to dive the array into 3 parts it doesn't work – Moro Owusu Afriyie Jul 05 '22 at 14:46
  • @MoroOwusuAfriyie updated code according.. now you can pass required number of element.. just take care of null and undefine, i did't handle edge cases :) – Bhushan Uniyal Jul 05 '22 at 14:52
  • if i change the partitionSize to Math.ceil(items.length /4 ) it fails.. All i wanted was to be able to create sub arrays given the number of partitions i want . you can take a look at the second answer and try it out. – Moro Owusu Afriyie Jul 05 '22 at 14:55