1

I have an object with some properties like:

const rows_obj = {
    prova1:[{price:3,description:"test11"},{price:7,description:"test12"}],
    prova2:[{price:11,description:"test21"},{price:2,description:"test22"}],
    prova3:[{price:1,description:"test31"},{price:23,description:"test32"}],
}

and i need to print the rows in one or more pages, so the limit per page is for instance 3 rows. So in this scenario, I need to have an array of object obj like:

obj[0] = {
        total:21,
        prova1:[{price:3,description:"test11"},{price:7,description:"test12"},],
        prova2:[{price:11,description:"test21"}],
    }

obj[1] = {
        total:26,
        prova2:[{price:2,description:"test22"}],
        prova3:[{price:1,description:"test31"},{price:23,description:"test32"},],
    }

(Since in this case the limit is 3 rows per page/object)

But the limit could be also 20 rows so the final object will be:

obj[0] = {
        total:47,
        prova1:[{price:3,description:"test11"},{price:7,description:"test12"},],
        prova2:[{price:11,description:"test21"},{price:2,description:"test22"},],
        prova3:[{price:1,description:"test31"},{price:23,description:"test32"},],
    }

Because in the original object there are 6 rows, then, since is under the limit, the function has to retrive an array with one element and this one element is equal to the original one.

I tried but so far i have made this code:

const size = 3
const rows_obj = {
    prova1:[{price:22,description:"test11"},{price:23,description:"test12"},],
    prova2:[{price:22,description:"test21"},{price:23,description:"test22"},],
    prova3:[{price:22,description:"test31"},{price:23,description:"test32"},],
}

var rows_length = 0;

for(var char in rows_obj){
  // Confirm that the key value is an array before adding the value.
  if(Array.isArray(rows_obj[char])){
    rows_length += rows_obj[char].length;   
  }
}

  if (!rows_length) {
    return [[]]
  }

  const arrays = []
  let i = 0

  const keys = Object.keys(rows_obj)
  let obj = null
  
  while (i<rows_length) {
    obj = {}
    for(let j=0;j<keys.length;j++) {
      obj[keys[j]] = rows_obj[keys[j]].slice(i, i + size)
      i = i + 2 + size
      console.log(i)
    } 
    arrays.push(obj)
  }

And it is not working, i'm doing a mess... any help? thank you in advance.

July
  • 516
  • 1
  • 7
  • 25
  • VTR. This is definitely not a duplicate of https://stackoverflow.com/q/11318680/. Both the input and the output structures are different. – Scott Sauyet May 24 '21 at 12:38

3 Answers3

1

Using Object#entries, Array#reduce, and Array#forEach:

const 
  rows_obj = {
    prova1:[{price:3,description:"test11"},{price:7,description:"test12"}],
    prova2:[{price:11,description:"test21"},{price:2,description:"test22"}],
    prova3:[{price:1,description:"test31"},{price:23,description:"test32"}],
  },
  SIZE = 3;
let count = 0; // count of items in last object

const rowEntries = Object.entries(rows_obj);
// iterate over rows_obj entries while updating finalList
const res = rowEntries.reduce((finalList, [currentKey, currentItems]) => {
  // iterate over current items
  currentItems.forEach(item => {
    // if SIZE is reached in the last object, add new one
    if(count === SIZE) {
      count = 0;
      finalList.push({ total: 0 });
    }
    // update last object
    const last = finalList[finalList.length-1];
    finalList[finalList.length-1] = {
      ...last,
      total:        last.total + item.price,
      [currentKey]: [...(last[currentKey] || []), item]
    };
    count++;
  });
  return finalList;
}, rowEntries.length > 0 ? [{ total: 0 }] : []);

console.log(res);
Majed Badawi
  • 27,616
  • 4
  • 25
  • 48
1

I feel like this is not best possible solution, but works as you expected.

const rows_obj = {
    prova1:[{price:22,description:"test11"},{price:23,description:"test12"},],
    prova2:[{price:22,description:"test21"},{price:23,description:"test22"},],
    prova3:[{price:22,description:"test31"},{price:23,description:"test32"},],
}

const rows = Object.entries(rows_obj).flatMap( ([k, v]) => (
  v.map(e => ({ key: k, ...e }) )
))

const size = 3
const groups = []
let i = 0

while(i < rows.length) {
  groups.push(rows.slice(i, i + size))
  i += size
}

const res = groups.map(e => e.reduce((acc, {key, ...rest}) => {
  acc[key]
    ? acc[key].push({...rest})
    : acc[key] = [{...rest}]
  return acc
}, {}))

console.log(res)
.as-console-wrapper { max-height: 100% !important; top: 0; }
ulou
  • 5,542
  • 5
  • 37
  • 47
  • Thank you @ulou for the reply. Since you were so kind for replying this question, can I ask you, what if I would like to add the total of price for each subobject obj like obj[0] = { 'prova1':[array], 'prova2':[array],...,total:67 } since is 22+23+22. (for the second obj is 68). Thank you – July May 24 '21 at 09:55
  • 1
    @July Add `acc.total += rest.price` above `return acc` and change line below it from `}, {}))` to `}, {total: 0}))` and it should work as you expected. – ulou May 24 '21 at 10:06
  • 1
    You already asked about "new requirements" and I responded you in comment above how it can be done. SO Community is here to help you to solve you problem, not to do homework or task instead of you, changing accepted answer and updating your expected output every time when you got new requirements (from someone?) is not how it works. – ulou May 24 '21 at 15:28
  • I'm really sorry ulou, i've edited the question because it is not working if you put a size like 20 and i have wrote: (Since in this case the limit is 3 rows per page/object) . But it was because i have explained my self wrong, and the limit could be higher, so i edited the question. And there is no accepted answer, because i explained my self wrong. Thank you for understanding, have a good day, I appreciate your help. – July May 24 '21 at 15:38
  • What do you mean it doesn't work? If you put size 20 (or any other positive number) it works as it suppose to work. – ulou May 24 '21 at 15:39
1

Update

It's not difficult to add the totaling of the additional requirement from the question update:

const regroup = (n) => (xs) => 
  chunk (n) (Object .entries (xs) .flatMap (([k, xs]) => xs .map (x => [k, x])))
    .map (group => group .reduce (
      ({total, ...r}, [k, v]) => ({
        total: total + v.price, 
        ...r, 
        [k]: (r [k] || []) .concat (v)
      }), {total: 0}
    ))

You can see this by expanding this snippet:

const chunk = (n) => (xs) => 
  xs .length < n ? [[...xs]] : [xs .slice (0, n), ...chunk (n) (xs. slice(n))]

const regroup = (n) => (xs) => 
  chunk (n) (Object .entries (xs) .flatMap (([k, xs]) => xs .map (x => [k, x])))
    .map (group => group .reduce (
      ({total, ...r}, [k, v]) => ({total: total + v.price, ...r, [k]: (r [k] || []) .concat (v)}), 
      {total: 0}
    ))

const rows_obj = {prova1: [{price: 3, description: "test11"}, {price: 7, description: "test12"}], prova2: [{price: 11, description: "test21"}, {price: 2, description: "test22"}], prova3: [{price: 1, description: "test31"}, {price: 23, description: "test32"}]}


console .log ('3', regroup (3) (rows_obj))
console .log ('4', regroup (4) (rows_obj))
console .log ('5', regroup (5) (rows_obj))
.as-console-wrapper {max-height: 100% !important; top: 0}

Original Answer

This version uses a helper function chunk, which breaks an array into chunks of the given length (plus possibly a leftover). So chunk (3) ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) //=> [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]. It goes through several simple transformations of the input, and the last one is your target:

const chunk = (n) => (xs) => 
  xs .length < n ? [[...xs]] : [xs .slice (0, n), ...chunk (n) (xs. slice(n))]

const regroup = (n) => (xs) => 
  chunk (n) (Object .entries (xs) .flatMap (([k, xs]) => xs .map (x => [k, x])))
    .map (
      group => group .reduce ((a, [k, v]) => ({...a, [k]: (a [k] || []) .concat (v)}), 
      {}
    ))

const rows_obj = {prova1: [{price: 22, description: "test11"}, {price: 23, description: "test12"}], prova2: [{price: 22, description: "test21"}, {price: 23, description: "test22"}], prova3: [{price: 22, description: "test31"}, {price: 23, description: "test32"}]}

console .log (regroup (3) (rows_obj))
.as-console-wrapper {max-height: 100% !important; top: 0}

After Object .entries (xs) .flatMap (...), we get

[
  ["prova1", {description: "test11", price: 22}],
  ["prova1", {description: "test12", price: 23}],
  ["prova2", {description: "test21", price: 22}],
  ["prova2", {description: "test22", price: 23}],
  ["prova3", {description: "test31", price: 22}],
  ["prova3", {description: "test32", price: 23}],
]

Then calling chunk (3) on that we get

[
  [
    ["prova1", {description: "test11", price: 22}],
    ["prova1", {description: "test12", price: 23}],
    ["prova2", {description: "test21", price: 22}],
  ], [
    ["prova2", {description: "test22", price: 23}],
    ["prova3", {description: "test31", price: 22}],
    ["prova3", {description: "test32", price: 23}],
  ]
]

and finally, with map (group => group.reduce (...)), we end up with

[
  {
    prova1: [
      {description: "test11", price: 22},
      {description: "test12", price: 23}
    ],
    prova2: [
      {description: "test21", price: 22}
    ]
  },
  {
    prova2: [
      {description: "test22", price: 23}
    ],
    prova3: [
      {description: "test31", price: 22},
      {description: "test32", price: 23}
    ]
  }
]

But there is to my mind a fundamental mismatch between your requirements and your input data structure. When you start with an object, you are starting with a fundamentally unordered collection. While modern JS does define its iteration order, an object is still not the correct structure to use for an ordered collection. I would suggest that this might be better:

const rows_obj = [
  {prova1: [{price: 22, description: "test11"}, {price: 23, description: "test12"}]},
  {prova2: [{price: 22, description: "test21"}, {price: 23, description: "test22"}]},
  {prova3: [{price: 22, description: "test31"}, {price: 23, description: "test32"}]}
]

It should be simple to change this code to handle that structure.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
  • Please, verify the edited question @Scott Sauyet. Thank you very much – July May 24 '21 at 15:09
  • Can I ask you, from your side, is it working when you put a value over 3? Thank you so much – July May 25 '21 at 09:01
  • Sorry, there was a typo in `chunk`. It's fixed now. That'll teach me to retype a function rather than paste it, and then to not seriously test it. – Scott Sauyet May 25 '21 at 14:11