一个属性的 JavaScript 数组总数

Total JavaScript Array by one Property

提问人:Chris 提问时间:10/23/2023 最后编辑:Chris 更新时间:10/23/2023 访问量:123

问:

我看了很多例子,但无法让它起作用。充其量,我最终会得到一个现有数组的版本,该版本复制了应该在摘要中丢失的属性。

我想为每个位置输出一个新的总计数组,并且不包括特定期间、期间属性等的任何详细信息。 我认为我的问题是 Object.assign({}, o);我正在创建一个完整的副本,但我看不到如何创建一个仅具有位置的对象,可以将总数添加到该位置。

这是一个简化的示例,在原版中,我将多个列相加并删除多个列。

let helper = {};
let locationPeriodCounts = [
  {"period": "2023-10-21", "location": "228", "countIn": 6, "countOut": 1},
  {"period": "2023-10-22", "location": "228", "countIn": 8, "countOut": 2},
  {"period": "2023-10-23", "location": "228", "countIn": 3, "countOut": 3},
  {"period": "2023-10-24", "location": "228", "countIn": 1, "countOut": 4},
  {"period": "2023-10-21", "location": "229", "countIn": 5, "countOut": 6},
  {"period": "2023-10-22", "location": "229", "countIn": 18, "countOut": 6},
  {"period": "2023-10-23", "location": "229", "countIn": 8, "countOut": 6},
  {"period": "2023-10-24", "location": "230", "countIn": 3, "countOut": 6},
  {"period": "2023-10-25", "location": "230", "countIn": 4, "countOut": 6}
];

let locationCounts = locationPeriodCounts.reduce(function(r, o) {
  let key = o.location;
  if (!helper[key]) {
    helper[key] = Object.assign({}, o); // create a copy of o
    helper[key].totalCountIn = 0;
    helper[key].totalCountOut = 0;
    r.push(helper[key]);
  } else {
    helper[key].totalCountIn += o.countIn;
    helper[key].totalCountOut += o.countOut;
  }
  return r;
}, []);

console.log(locationCounts);

这就是我正在寻找的输出......

[
  {"location": "228", "totalCountIn": 18, "totalCountOut": 10},
  {"location": "229", "totalCountIn": 31, "totalCountOut": 18},
  {"location": "230", "totalCountIn": 7, "totalCountOut": 12}
];
JavaScript JavaScript Reduce

评论

0赞 Chris 10/23/2023
添加了所需的输出@mplungjan
0赞 mplungjan 10/23/2023
更新了代码以生成 objectArray
1赞 pilchard 10/23/2023
这回答了你的问题吗?如何对对象数组进行分组和求和?

答:

4赞 mplungjan 10/23/2023 #1

我添加了一个 Object.entries 以转换为对象数组

现在,我们需要将嵌套对象展开,使两个计数器与位置位于同一级别

let locationCounts = Object.entries(
  locationPeriodCounts
  .reduce(function (acc, { location, countIn, countOut }) {
    acc[location] ??= { totalCountIn: 0, totalCountOut: 0 }; // create if it does not exist
    acc[location].totalCountIn += countIn; // always add to the total
    acc[location].totalCountOut += countOut; // always add to the total
    return acc;
  }, {})
)
.map(([location, { totalCountIn, totalCountOut }]) => ({ location, totalCountIn, totalCountOut }));

console.log(locationCounts);
<script>
let locationPeriodCounts = [
  {"period": "2023-10-21", "location": "228", "countIn": 6, "countOut": 1},
  {"period": "2023-10-22", "location": "228", "countIn": 8, "countOut": 2},
  {"period": "2023-10-23", "location": "228", "countIn": 3, "countOut": 3},
  {"period": "2023-10-24", "location": "228", "countIn": 1, "countOut": 4},
  {"period": "2023-10-21", "location": "229", "countIn": 5, "countOut": 6},
  {"period": "2023-10-22", "location": "229", "countIn": 18, "countOut": 6},
  {"period": "2023-10-23", "location": "229", "countIn": 8, "countOut": 6},
  {"period": "2023-10-24", "location": "230", "countIn": 3, "countOut": 6},
  {"period": "2023-10-25", "location": "230", "countIn": 4, "countOut": 6}
];
</script>

评论

0赞 Chris 10/23/2023
谢谢@mplungjan。totakCount 具体是如何设置的?如果我想将两个值添加到两个总数中,我该怎么做(出于此处的目的,我们可以将 countIn 添加到两个总数)?
0赞 mplungjan 10/23/2023
我的代码创建一个对象:并按位置添加到每个对象中。然后,它将该对象转换为对象数组,并替换为 和 替换为 。同样,当您要求转换某些输入时,请显示预期的输出{ "228":0,"229":0,"230":0}228"location":"228"18"totalCount":18
0赞 Chris 10/23/2023
很抱歉@mplungjan,我已经澄清了代码以证明我需要它来处理多个属性。
0赞 mplungjan 10/23/2023
请参阅更新的代码。我现在正在展开嵌套计数器,以将它们与位置放在同一水平上
1赞 Filo. 10/23/2023 #2

我发现@mplungjan的反应比我的更聪明、更紧凑。 然而,这是我通常做的:

let locationPeriodCounts = [
    { "period": "2023-10-21", "location": "228", "countIn": 6 },
    { "period": "2023-10-22", "location": "228", "countIn": 8 },
    { "period": "2023-10-23", "location": "228", "countIn": 3 },
    { "period": "2023-10-24", "location": "228", "countIn": 1 },
    { "period": "2023-10-21", "location": "229", "countIn": 5 },
    { "period": "2023-10-22", "location": "229", "countIn": 18 },
    { "period": "2023-10-23", "location": "229", "countIn": 8 },
    { "period": "2023-10-24", "location": "230", "countIn": 3 },
    { "period": "2023-10-25", "location": "230", "countIn": 4 }
]

let locationCounts = locationPeriodCounts.reduce((acc, { location, countIn }) => {

    let currLocationCount = acc.find(count => count.location === location)

    if (currLocationCount) {
        currLocationCount.totalCount += countIn
    } else {
        acc.push({
            location: location,
            totalCount: countIn
        })
    }

    return acc;
}, []);

//If you also need sorting
locationCounts.sort((a, b) => {
    //return a.totalCount - b.totalCount // asc
    return b.totalCount - a.totalCount //desc
})

console.log(locationCounts);

这是输出:

[
   {location: "229", totalCount: 31},
   {location: "228", totalCount: 18},
   {location: "230", totalCount: 7}
]

评论

1赞 Konrad 10/23/2023
这要慢得多,因为find
0赞 Filo. 10/23/2023
是的,这就是为什么我说@mplungjan比我的聪明得多!
0赞 Filo. 10/23/2023
但是,一个问题:这样做是像 ~ O(1) 中的哈希图一样访问数组还是进行隐式查找?非常感谢!acc[location]
2赞 Konrad 10/23/2023
在另一个答案中,它不是一个数组,而是一个对象,所以很可能是 O(1)accacc[location]
0赞 Filo. 10/23/2023
呃,很好,谢谢。绝对有趣!
2赞 pilchard 10/23/2023 #3

在原始代码中使用“helper”对象并不是一个坏的倾向,而常见的分组模式通过使用在传递给 的累加器对象中保存一个结果数组来做到这一点。这样做的好处包括:在reduce调用完成后不需要序列化对象,并且在reduce调用完成并检索结果数组后丢弃“helper”对象。reducereduce

let locationCounts = locationPeriodCounts.reduce(
  (acc, { location, countIn, countOut }) => {
    if (acc[location] === undefined) {
      acc[location] = { location, totalCountIn: 0, totalCountOut: 0 };
      acc._.push(acc[location]); // push a reference to the result array
    }
    acc[location].totalCountIn += countIn;
    acc[location].totalCountOut += countOut;
    return acc;
  },
  { _: [] } // declare a result array in the accumulator
)._; // retrieve the result array

console.log(locationCounts);
<script>
let locationPeriodCounts = [
  {"period": "2023-10-21", "location": "228", "countIn": 6, "countOut": 1},
  {"period": "2023-10-22", "location": "228", "countIn": 8, "countOut": 2},
  {"period": "2023-10-23", "location": "228", "countIn": 3, "countOut": 3},
  {"period": "2023-10-24", "location": "228", "countIn": 1, "countOut": 4},
  {"period": "2023-10-21", "location": "229", "countIn": 5, "countOut": 6},
  {"period": "2023-10-22", "location": "229", "countIn": 18, "countOut": 6},
  {"period": "2023-10-23", "location": "229", "countIn": 8, "countOut": 6},
  {"period": "2023-10-24", "location": "230", "countIn": 3, "countOut": 6},
  {"period": "2023-10-25", "location": "230", "countIn": 4, "countOut": 6}
];
</script>

无需解构

let locationCounts = locationPeriodCounts.reduce(
  (acc, obj) => {
    const key = obj.location;

    if (acc[key] === undefined) {
      acc[key] = {
        location: obj.location,
        totalCountIn: 0,
        totalCountOut: 0,
      };
      acc._.push(acc[key]);
    }

    acc[key].totalCountIn += obj.countIn;
    acc[key].totalCountOut += obj.countOut;

    return acc;
  },
  { _: [] }
)._;

console.log(locationCounts);
<script>
let locationPeriodCounts = [
  {"period": "2023-10-21", "location": "228", "countIn": 6, "countOut": 1},
  {"period": "2023-10-22", "location": "228", "countIn": 8, "countOut": 2},
  {"period": "2023-10-23", "location": "228", "countIn": 3, "countOut": 3},
  {"period": "2023-10-24", "location": "228", "countIn": 1, "countOut": 4},
  {"period": "2023-10-21", "location": "229", "countIn": 5, "countOut": 6},
  {"period": "2023-10-22", "location": "229", "countIn": 18, "countOut": 6},
  {"period": "2023-10-23", "location": "229", "countIn": 8, "countOut": 6},
  {"period": "2023-10-24", "location": "230", "countIn": 3, "countOut": 6},
  {"period": "2023-10-25", "location": "230", "countIn": 4, "countOut": 6}
];
</script>


您现有的代码

您所缺少的只是创建一个仅具有相关属性的新对象(在您的问题中正确指出为问题)。要么使用上面的解构,要么像现在这样手动。Object.assign()

要使您自己的代码按原样工作,只需替换为 ,然后将求和移出块即可。这仍然在全局范围内徘徊,但通过将其作为闭包传递,可以很容易地解决:helper[key] = Object.assign({}, o);helper[key] = { location: o.location };elsehelper

let locationCounts = (helper =>
  locationPeriodCounts.reduce(function (r, o) {
    let key = o.location;
    if (!helper[key]) {
      helper[key] = { location: o.location };
      helper[key].totalCountIn = 0;
      helper[key].totalCountOut = 0;
      r.push(helper[key]);
    }

    helper[key].totalCountIn += o.countIn;
    helper[key].totalCountOut += o.countOut;

    return r;
  }, []))({});

或使用上述设置。