Search code examples
javascriptarraysmultidimensional-array

Split array of objects into multidimensional array using property to group items


I’ve got an array of objects that each carry a columnSpan property with a numeric value between 1 and 16.

[
  {
    "columnSpan": 4,
    "id": "DatoCmsEntry-MaixsOYtSwS7mloD59Rvng"
  },
  {
    "columnSpan": 4,
    "id": "DatoCmsEntry-fEuNly9-QFiRh4DoUZ-Y_w"
  },
  {
    "columnSpan": 2,
    "id": "DatoCmsEntry-HsAgXDMHQkSXS_4lKSGfGA"
  },
  {
    "columnSpan": 2,
    "id": "DatoCmsEntry-BM-fSBruSM67IFzCrBSLBg"
  },
  {
    "columnSpan": 3,
    "id": "DatoCmsEntry-JPushhKGSBCwR_uWcwIFSw"
  },
  {
    "columnSpan": 3,
    "id": "DatoCmsEntry-Q0sfjP9ZQZSDZZVP_sI9ew"
  },
  {
    "columnSpan": 2,
    "id": "DatoCmsEntry-CdVhbQENQ4ib2z4w3wc20Q"
  },
  {
    "columnSpan": 2,
    "id": "DatoCmsEntry-Fn2U_0CuQDiBEOZLmS3ovQ"
  }
]

I need to split this array up into a multidimensional array where each top-level item represents a collection of records where the total columnSpan is 16 or less, keeping the original order, like this:

[
  [
    {
      "columnSpan": 4,
      "id": "DatoCmsEntry-MaixsOYtSwS7mloD59Rvng"
    },
    {
      "columnSpan": 4,
      "id": "DatoCmsEntry-fEuNly9-QFiRh4DoUZ-Y_w"
    },
    {
      "columnSpan": 2,
      "id": "DatoCmsEntry-HsAgXDMHQkSXS_4lKSGfGA"
    },
    {
      "columnSpan": 2,
      "id": "DatoCmsEntry-BM-fSBruSM67IFzCrBSLBg"
    },
    {
      "columnSpan": 3,
      "id": "DatoCmsEntry-JPushhKGSBCwR_uWcwIFSw"
    }
  ],
  [
    {
      "columnSpan": 3,
      "id": "DatoCmsEntry-Q0sfjP9ZQZSDZZVP_sI9ew"
    },
    {
      "columnSpan": 2,
      "id": "DatoCmsEntry-CdVhbQENQ4ib2z4w3wc20Q"
    },
    {
      "columnSpan": 2,
      "id": "DatoCmsEntry-Fn2U_0CuQDiBEOZLmS3ovQ"
    }
  ]
]

Suggestions on ways to pull this off?


Solution

  • Reduce the array. In the reducer start by checking if a new chunk should be added. Always add the current item to the last chunk.

    const fn = (arr, max) => arr.reduce((acc, o) => {
      // add a new chunk and reset sum if no chunks, or next item would go over the max
      if(!acc.res.length || acc.sum + o.columnSpan > max) {
        acc.sum = 0
        acc.res.push([])
      }
    
      acc.res.at(-1).push(o) // push current item to last chunk
    
      acc.sum += o.columnSpan // add current columnSpan to sum
    
      return acc
    }, { res: [], sum: 0 }).res
    
    const data = [{"columnSpan":4,"id":"DatoCmsEntry-MaixsOYtSwS7mloD59Rvng"},{"columnSpan":4,"id":"DatoCmsEntry-fEuNly9-QFiRh4DoUZ-Y_w"},{"columnSpan":2,"id":"DatoCmsEntry-HsAgXDMHQkSXS_4lKSGfGA"},{"columnSpan":2,"id":"DatoCmsEntry-BM-fSBruSM67IFzCrBSLBg"},{"columnSpan":3,"id":"DatoCmsEntry-JPushhKGSBCwR_uWcwIFSw"},{"columnSpan":3,"id":"DatoCmsEntry-Q0sfjP9ZQZSDZZVP_sI9ew"},{"columnSpan":2,"id":"DatoCmsEntry-CdVhbQENQ4ib2z4w3wc20Q"},{"columnSpan":2,"id":"DatoCmsEntry-Fn2U_0CuQDiBEOZLmS3ovQ"}]
    
    const result = fn(data, 16)
    
    console.log(result)