2

I have next data:

const data = [
    {amount: 100, percent: 1, days: 7},
    {amount: 75, percent: 3, days: 8},
    {amount: 75, percent: 3, days: 3},
    {amount: 100, percent: 2, days: 5},
    {amount: 100, percent: 1, days: 10},
    {amount: 50, percent: 3, days: 9}
];

The task is to group objects by amount and percent, so that if amount and percent in different objects are the same I need to add their days. The result should look the next way:

const data = [
    {amount: 100, percent: 1, days: 17},
    {amount: 75, percent: 3, days: 11},
    {amount: 100, percent: 2, days: 5},
    {amount: 50, percent: 3, days: 9}
];

1st and 3rd objects are not grouped because of different percent. I have tried to use reduce method:

const result = data.reduce((groupedByAmount, current) => {
    let amount = current.amount;
    let percent= current.percent;
    if (amount == amount && percent == percent) {
        groupedByAmount.amount = amount;
        current.days += current.days
    }
    return groupedByAmount
}, {
    amount: 0,
    percent: null,
    days: 0
});

I don't how to check if amount and percent are the same, maybe map or filter would help

3
  • Looks like it might help: groupby on array of objects Commented Jan 4, 2022 at 9:56
  • Note .reduce() always returns a single value, not an array - solutions in the above return a single object with keys. Commented Jan 4, 2022 at 9:57
  • might be soluion with map or filter for example? Commented Jan 4, 2022 at 10:03

4 Answers 4

1

Considering each of the methods you've suggested:

  • reduce => reduce array elements to a single value. Can be used to create an object, then convert that object back to the array

  • map => return a value for every value in the array

  • filter=> return multiple (or single) matching values

You can loop through each value, building a new array and using filter() to find existing values to add to.

Note: this likely won't perform well with large arrays due to the multiple use of .filter() - using a composite key (combining two values into a single value) would likely perform better.

const data = [
    {amount: 100, percent: 1, days: 7},
    {amount: 75, percent: 3, days: 8},
    {amount: 75, percent: 3, days: 3},
    {amount: 100, percent: 2, days: 5},
    {amount: 100, percent: 1, days: 10},
    {amount: 50, percent: 3, days: 9}
];

var result = [];

data.forEach(e => {
    var current = result.filter(d => d.amount == e.amount && d.percent == e.percent);
    if (current.length === 0) {
        result.push(e);
    } else {
        current[0].days += e.days;
    }
});
console.log(result);

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

Comments

1

const data = [
    {amount: 100, percent: 1, days: 7},
    {amount: 75, percent: 3, days: 8},
    {amount: 75, percent: 3, days: 3},
    {amount: 100, percent: 2, days: 5},
    {amount: 100, percent: 1, days: 10},
    {amount: 50, percent: 3, days: 9}
];

const doGroup = (data) => {
    const obj = data.reduce((result, next) => {
    const key = `${next.amount}_${next.percent}`
    const dd =  result[key]?.days ?? 0;
    result[key] = {...next, days: next.days + dd};
    return result;
  }, {});
  return Object.values(obj);
}

console.log(doGroup(data))

2 Comments

Say data had objects {amount: 10, percent: 11, days: ?} and amount: 101, percent: 1, days: ?} will they not get incorrectly get grouped together with the key you are using right now? Maybe add a character between amount and percent e.g., ${next.amount}_${next.percent}
make sense, will edit it
0

const data = [
    {amount: 100, percent: 1, days: 7},
    {amount: 75, percent: 3, days: 8},
    {amount: 75, percent: 3, days: 3},
    {amount: 100, percent: 2, days: 5},
    {amount: 100, percent: 1, days: 10},
    {amount: 50, percent: 3, days: 9}
];

const temp = data.reduce((acc, {amount, percent, days}) => {
  acc[amount] ||= {}
  acc[amount][percent] ||= 0
  acc[amount][percent] += days
  return acc;
}
,{});

const result = Object.entries(temp).reduce((acc, [amount, obj]) => {
  acc.push(...Object.entries(obj).map(([percent, days]) => ({ amount: Number(amount), percent: Number(percent), days })))
  return acc
},[])

console.log(result)

Comments

0

This example is larger in number of lines, but simpler, perhaps.

const data = [
  {amount: 100, percent: 1, days: 7},
  {amount: 75, percent: 3, days: 8},
  {amount: 75, percent: 3, days: 3},
  {amount: 100, percent: 2, days: 5},
  {amount: 100, percent: 1, days: 10},
  {amount: 50, percent: 3, days: 9}
];

function group_by_amount_percent(array) {
  var ret = []
  var cache = {}
  array.forEach(function(e, i, a) {
    if (cache[e.amount] == null) { cache[e.amount] = {} }
    if (cache[e.amount][e.percent] == null) { cache[e.amount][e.percent] = 0 }
    cache[e.amount][e.percent] += e.days
  })
  for (var amount in cache) {
    for (var percent in cache[amount]) {
      ret.push({amount: amount, percent: percent, days: cache[amount][percent]})
    }
  }
  return ret
}

console.log('group_by_amount_percent(data): ', group_by_amount_percent(data))

Comments

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.