javascript 中的对象数组的过滤和求和

filter and sum array of objects in javascript

您好,感谢您查看我的问题。

我有一个对象数组,其中包含对应于不同日期的同一个人的多个条目。我需要对每个人的值求和。

const data = [
  {
    name: "Bob",
    date: 3/27/22
    value: 300
  },
  {
    name: "Alice",
    date: 1/13/22
    value: 500
  },
  {
    name: "Bob",
    date: 5/13/22
    value: 400
  },
  {
    name: "Alice",
    date: 4/19/22
    value: 350
  },
  {
    name: "John",
    date: 2/15/22
    value: 700
  },
]

我需要的结果是:

const result = [
  {
    name: "Bob",
    value: 700
  },
  {
    name: "Alice",
    value: 850
  },
  {
    name: "John",
    value: 700
  },
]

我怎样才能以最有效的方式做到这一点?

到目前为止,我只能通过使用过滤数组方法返回名称值、将结果推送到新数组并对该数组求和来实现此目的。但是,我事先并不知道所有的名称值,所以这行不通。

感谢您的宝贵时间

一种方法可以是:

const data = [
  {
    name: 'Bob',
    date: '3/27/22',
    value: 300,
  },
  {
    name: 'Alice',
    date: '1/13/22',
    value: 500,
  },
  {
    name: 'Bob',
    date: '5/13/22',
    value: 400,
  },
  {
    name: 'Alice',
    date: '4/19/22',
    value: 350,
  },
  {
    name: 'John',
    date: '2/15/22',
    value: 700,
  },
];

let res = data.reduce((agg, curr) => {
  agg[curr.name] = (agg[curr.name] || 0) + curr.value;
  return agg;
}, {});

const res2 = Object.keys(res).map((v) => {
  return {
    name: v,
    value: res[v],
  };
});

console.log(res2);

我认为这种方法具有合理的性能:

const data = [
  {
    name: "Bob",
    value: 300
  },
  {
    name: "Alice",
    value: 500
  },
  {
    name: "Bob",
    value: 400
  },
  {
    name: "Alice",
    value: 350
  },
  {
    name: "John",
    value: 700
  },
];

const reduceData = (data) => data.reduce((acc, cur) => {
  const {name, value} = cur;                            // Get name and value from current item
  const item = acc.find(it => it.name === name);        // Find in our accumulator the desired object
  item ? item.value += value : acc.push({name, value}); // Update object or create a new object if it doesn't exist
  return acc;                                           // Return accumulator
} , []);

console.log(reduceData(data));

Array.reduce 将一个内部函数作为第一个参数。

这个内部函数通常有两个参数:一个是将被函数return编辑的“累加器”,另一个是“当前数组项”。

Array.reduce 将 运行 每个数组项的内部函数,最后 return 它的值。

每次 运行 传递给内部函数的“累加器”参数是前一个内部函数调用的 return 值。

Array.reduce可以有一个额外的参数,它是传递给第一个内部函数调用的初始累加器值(这里我们使用一个空数组)。

更多信息在这里 -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

您也可以通过映射来做到这一点

const data = [
    {
        name: "Bob",
        date: ' 3 / 27 / 22',
        value: 1,
    },
    {
        name: "Alice",
        date: '1 / 13 / 22',
        value: 2,
    },
    {
        name: "Bob",
        date: '5 / 13 / 22',
        value: 1,
    },
    {
        name: "Alice",
        date: ' 4 / 19 / 22',
        value: 2
    },
    {
        name: "John",
        date: '2 / 15 / 22',
        value: 3
    },
]
const res = {};
for (let i = 0; i < data.length; i++) {

    if (res[data[i].name]) { //here if any key(name) is already present in res then we add the value in already present value of that name
        res[data[i].name].value += data[i].value
    } else {
        res[data[i].name] = data[i] //here we are inserting data into res object if doesn't find any key with already name present in it
    }
}



const res2 = Object.keys(res).map(person => {
    return {
        name: person,
        value: res[person].value
    }
})

console.log(res2);

您可以使用 reduce 函数合并具有相同键名 属性 的值。

const mergedData = data.reduce((prev, curr) => {
      if(prev[curr.name]) {
         // Sum the value if the name matches for multiple objects
         prev[curr.name].value = prev[curr.name].value + curr.value
      } else {
         // Return the actual objects if not matches any
         prev[curr.name] = curr
      }
         return prev
}, {});

console.log(Object.values(mergedData))

输出将是:

[
  {
    "name": "Bob",
    "date": "3/27/22",
    "value": 700
  },
  {
    "name": "Alice",
    "date": "1/13/22",
    "value": 850
  },
  {
    "name": "John",
    "date": "2/15/22",
    "value": 700
  }
]

要了解有关 reduce 的更多信息,您可以从 documentation here

中学习

这是一次(可能有缺陷的)尝试比较此页面上答案的速度。它是否有用在很大程度上取决于正在处理的实际数据集的样子。这里的数据数组是对最坏情况的疯狂猜测(可能是错误的)。

随时fiddle使用它。

const rand_letter = () => String.fromCharCode(65+Math.floor(Math.random() * 26));
const data = Array.from({ length: 10000 }, () => ({ name: rand_letter() + rand_letter(), value: Math.floor(Math.random() * 1000) }));


const A = (data) => {
  let name_map = new Map();

  for(let i = 0; i < data.length; i++) {
    const { name, value } = data[i];

    name_map.set(name, (name_map.get(name) ?? 0) + value);
  }

  return [...name_map.entries()].map(([name, value]) => ({ name, value }));
};

const B = (data) => {
  let name_map = {};

  for(let i = 0; i < data.length; i++) {
    const { name, value } = data[i];

    name_map[name] = (name_map[name] ?? 0) + value;
  }

  return Object.entries(name_map).map(([name, value]) => ({ name, value }));
};

const C = (data) =>
  Object.entries(
    data.reduce((acc, { name, value }) => {
      acc[name] = (acc[name] ?? 0) + value;
      return acc;
    }, {})
  ).map(([name, value]) => ({ name, value }));

const D = (data) =>
  data.reduce((acc, cur) => {
    const {name, value} = cur;
    const item = acc.find(it => it.name === name);
    item ? item.value += value : acc.push({name, value});
    return acc;
  }, []);


const time = (fn) => {
  const iter = 100;
  const t0 = performance.now();
  
  for(let i = 0; i < iter; i++)
    fn(data);
  
  console.log('time for ' + fn.name + ' (milliseconds)', (performance.now() - t0) / iter);
};

[A, B, C, D].forEach(time);