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.

1
  • VTR. This is definitely not a duplicate of stackoverflow.com/q/11318680. Both the input and the output structures are different. Commented May 24, 2021 at 12:38

3 Answers 3

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; }

Sign up to request clarification or add additional context in comments.

5 Comments

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 Add acc.total += rest.price above return acc and change line below it from }, {})) to }, {total: 0})) and it should work as you expected.
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.
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.
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.
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.

3 Comments

Please, verify the edited question @Scott Sauyet. Thank you very much
Can I ask you, from your side, is it working when you put a value over 3? Thank you so much
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.
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);

4 Comments

Thank you Majed for your reply. I really appreciate it. But i need an array of object, not an object with index properties
Please, verify the edited question @Majed Badawi. Thank you very much
@July: It's quite easy to turn that sort of indexed object into an array: Object.assign ([], indexedObject).
@July I updated the answer to return a list with the new design

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.