0

I have an array in this format:

let array = [
  { text: "Green marbles", number: 10, n: 1 },
  { text: "Pink marbles", number: 5, n: 1 },
  { text: "Yellow marbles", number: 2, n: 1 },
  { text: "Green marbles", number: 10, n: 2 },
  { text: "Pink marbles", number: 5, n: 2 },
  { text: "Yellow marbles", number: 2, n: 2 }
]

The desired result would be:

let formatArray = [
  { n: 1, "Green marbles": 10, "Pink marbles": 5, "Yellow marbles": 2 },
  { n: 2, "Green marbles": 10, "Pink marbles": 5, "Yellow marbles": 2 }
];

The number is not constant (just for this example it is)I tried doing this:

let formatArray = [];

array.forEach(el=> {
  Object.keys(el).forEach(eel=> {
   formatArray.push(/*…???*/)
  })
}) 

Stuck here, but I think that this would be the idea

pilchard
  • 12,414
  • 5
  • 11
  • 23
  • Does this answer your question? [Group objects by multiple properties in array then sum up their values](https://stackoverflow.com/questions/46794232/group-objects-by-multiple-properties-in-array-then-sum-up-their-values) – pilchard Oct 24 '21 at 00:42
  • It does not, since i want each marble color to be in the same object – Joshua Bitton Oct 24 '21 at 00:54

3 Answers3

0

Try to use the reduce method to return a new array with the values you want.

let array = [
  { text: "Green marbles", number: 10, n: 1 },
  { text: "Pink marbles", number: 5, n: 1 },
  { text: "Yellow marbles", number: 2, n: 1 },
  { text: "Green marbles", number: 10, n: 2 },
  { text: "Pink marbles", number: 5, n: 2 },
  { text: "Yellow marbles", number: 2, n: 2 }
]

const formatArray = array.reduce((accum, iter) => {
  const {n, text, number} = iter
  if (accum[n-1] === undefined) accum[n-1] = {}
  accum[n-1].n = n
  accum[n-1][text] = number
  return accum
}, [])

console.log(formatArray)

I am using the [] to set the key to be one of the iterator's values. I am also letting the n value set which object the iterator needs to be added to.

PsiKai
  • 1,803
  • 1
  • 5
  • 19
  • When i do it, I only get 2 objects in the array, the n values can go to 20, 100, …. In my case i showed only 2 n values – Joshua Bitton Oct 24 '21 at 01:25
  • Is there a way of having n number of objects? – Joshua Bitton Oct 24 '21 at 01:26
  • I am confused, because the output of my code snippet is the desired result you asked for. Can you edit your question, or elaborate here what you would desire? If you add more `n` values to my solution, you should get more objects... – PsiKai Oct 24 '21 at 01:26
  • My answer is assuming the `n` values increment by one, and that the total number of objects should be the highest `n` value. Happy to update my answer to include many more `n` if my assumption is correct. – PsiKai Oct 24 '21 at 01:28
  • @JoshuaBitton I guess we got our wires crossed. It seems like you hadn't gotten my edit when you tried to use my solution. Apologies for the confusion. – PsiKai Oct 24 '21 at 01:40
0

This can be handled as a 'group by' on n accumulating the various marble colors into each grouped object.

let array = [{ text: "Green marbles", number: 6, n: 1 }, { text: "Pink marbles", number: 3, n: 1 }, { text: "Yellow marbles", number: 2, n: 1 }, { text: "Green marbles", number: 12, n: 2 }, { text: "Pink marbles", number: 7, n: 2 }, { text: "Yellow marbles", number: 1, n: 2 }];

const temp = {};
for (const { n, number, text } of array) {
  if (temp[n] === undefined) temp[n] = { n, "Green marbles": 0, "Pink marbles": 0, "Yellow marbles": 0 };

  temp[n][text] += number;
}

const result = Object.values(temp);

console.log(result);

If you're not sure of the 'marble colors' properties ahead of time you can make it more dynamic

let array = [{ text: "Green marbles", number: 6, n: 1 }, { text: "Pink marbles", number: 3, n: 1 }, { text: "Yellow marbles", number: 2, n: 1 }, { text: "Green marbles", number: 12, n: 2 }, { text: "Pink marbles", number: 7, n: 2 }, { text: "Yellow marbles", number: 1, n: 2 }];

const temp = {};
for (const { n, number, text } of array) {
  if (temp[n] === undefined) temp[n] = { n };

  temp[n][text] = (temp[n][text] ?? 0) + number;
}

const result = Object.values(temp);

console.log(result);

or as a reduce() call

let array = [{ text: "Green marbles", number: 6, n: 1 }, { text: "Pink marbles", number: 3, n: 1 }, { text: "Yellow marbles", number: 2, n: 1 }, { text: "Green marbles", number: 12, n: 2 }, { text: "Pink marbles", number: 7, n: 2 }, { text: "Yellow marbles", number: 1, n: 2 }];

const result = Object.values(
  array.reduce((a, { n, number, text }) =>
    (a[n] = { ...(a[n] ??= { n }), [text]: (a[n][text] ?? 0) + number }, a), {}));

console.log(result);

But it seems that your array might actually be ordered with every x rows describing a new n.

If this is the case you can use a simple for loop and check if the index is evenly divisible by x.

let array = [{ text: "Green marbles", number: 6, n: 1 }, { text: "Pink marbles", number: 3, n: 1 }, { text: "Yellow marbles", number: 2, n: 1 }, { text: "Green marbles", number: 12, n: 2 }, { text: "Pink marbles", number: 7, n: 2 }, { text: "Yellow marbles", number: 1, n: 2 }];

let
  result = [],
  color_count = 3,
  prev;
for (let i = 0; i < array.length; i++) {
  const { n, number, text } = array[i];

  if (i % color_count === 0) {
    prev = { n };
    result.push(prev);
  }

  prev[text] = number;
}

console.log(result);

Or, if they are indeed in order, but you don't know how many per n you can track the current object, and if the n doesn't match on any iteration push a new obect to the result.

let array = [{ text: "Green marbles", number: 6, n: 1 }, { text: "Pink marbles", number: 3, n: 1 }, { text: "Yellow marbles", number: 2, n: 1 }, { text: "Green marbles", number: 12, n: 2 }, { text: "Pink marbles", number: 7, n: 2 }, { text: "Yellow marbles", number: 1, n: 2 }];

let result = [], prev;
for (const { n, number, text } of array) {

  if (!prev || prev.n !== n) {
    prev = { n };
    result.push(prev);
  }

  prev[text] = number;
}

console.log(result);
pilchard
  • 12,414
  • 5
  • 11
  • 23
0

1) You can easily achieve the result efficiently using Map as:

array.reduce((map, curr) => map.set(curr.n, { ...(map.get(curr.n) ?? {}), [curr.text]: curr.number }), new Map())

let array = [
  { text: "Green marbles", number: 10, n: 1 },
  { text: "Pink marbles", number: 5, n: 1 },
  { text: "Yellow marbles", number: 2, n: 1 },
  { text: "Green marbles", number: 10, n: 2 },
  { text: "Pink marbles", number: 5, n: 2 },
  { text: "Yellow marbles", number: 2, n: 2 },
];

let collection = array.reduce((map, curr) => map.set(curr.n, { ...(map.get(curr.n) ?? {}), [curr.text]: curr.number }), new Map());

const formatArray = [...collection.values()];
console.log(formatArray);

2) One-liner solution

let formatArray = [ ...array.reduce((map, curr) => map.set(curr.n, { ...(map.get(curr.n) ?? {}), [curr.text]: curr.number }), new Map()).values()];

let array = [
  { text: "Green marbles", number: 10, n: 1 },
  { text: "Pink marbles", number: 5, n: 1 },
  { text: "Yellow marbles", number: 2, n: 1 },
  { text: "Green marbles", number: 10, n: 2 },
  { text: "Pink marbles", number: 5, n: 2 },
  { text: "Yellow marbles", number: 2, n: 2 },
];

let formatArray = [ ...array.reduce((map, curr) => map.set(curr.n, { ...(map.get(curr.n) ?? {}), [curr.text]: curr.number }), new Map()).values()];

console.log(formatArray);
DecPK
  • 24,537
  • 6
  • 26
  • 42