【问题标题】:Functional programming pattern for tabulation in JavaScriptJavaScript 制表的函数式编程模式
【发布时间】:2023-02-25 23:36:21
【问题描述】:

我发现自己经常使用这样的模式:

const objectArray = [
  { name: "Bob", value: 20 },
  { name: "Jane", value: 15 },
  { name: "John", value: 5 },
  { name: "Jane", value: 10 },
];

objectArray.reduce((result, nextValue) => {
  if (!(nextValue.name in result)) result[nextValue.name] = 0;
  result[nextValue.name] += nextValue.value;
  return result;
}, {}); // { Bob: 20, Jane: 25, John: 5 }

IE。我使用对象中的一些键和值以及一个 reduce 函数(在上面的示例中为 +,但可以是任何东西)将一个对象数组缩减为一个键值对字典。重要的是,如果键尚不存在,则必须用一些值对其进行初始化。

我想知道,任何人都知道一些优雅的方法可以使它更具功能性,即抽象出命令式if 语句以检查密钥是否存在和变异语句,同时仍然保持它在Array.prototype.reduce() 中可用。

【问题讨论】:

  • 如果你的意思是只让它成为一行,你可以只使用 nullish 合并,但它不会让它更“实用”const result = objectArray.reduce((result, nextValue) => ((result[nextValue.name] = result[nextValue.name] ?? 0 + nextValue.value),result),{});为了让它更实用,你可以将分组逻辑抽象为一个通用状态,该状态接受封装了细节。
  • 不。其他语言 have data structures that solve this problemsolve it with a missing default property procedure 但 JS 中没有“更好”的解决方案。您可以使用 Proxy 拦截对象属性访问并返回 0,但我不推荐这样做:增加的复杂性(和代理不能被填充)只是不值得。
  • 你能更详细地谈谈你认为抽象应该采用什么形式吗?无论您如何处理,您仍然需要检查属性键是否存在,并且您仍然需要进行一些添加。

标签: javascript functional-programming


【解决方案1】:

您可以将 reduce 与对象解构和传播运算符 ... 一起使用,如下所示:

const objectArray = [
  { name: "Bob", value: 20 },
  { name: "Jane", value: 15 },
  { name: "John", value: 5 },
  { name: "Jane", value: 10 },
];

const result = objectArray.reduce((acc, { name, value }) => ({
  ...acc,
  [name]: (acc[name] || 0) + value,
}), {});

console.log(result);

【讨论】:

  • 不是 DV,但这并不能真正回答问题。
  • 不要在reduce中使用spread,它只会增加操作的时间和空间复杂度。
  • @pilchard,感谢您的提示。我经常将 spread 与 reduce 一起使用。在简单数据的情况下,性能影响无论如何都可以忽略不计。
【解决方案2】:

const objectArray = [
  { name: "Bob", value: 20 },
  { name: "John", value: 25 },
  { name: "Jane", value: 15 },
  { name: "John", value: 5 },
  { name: "Jane", value: 10 },
];

const res=objectArray.reduce((a,c)=>(a[c.name]=a[c.name]??0,(a[c.name]+=c.value),a),{})

console.log(res)

【讨论】:

    【解决方案3】:

    是的,您可以使用 Map() 对象而不是对象文字 {} 来存储中间结果。 Map() 是一个内置的 JavaScript 对象,它允许您存储键值对并轻松检查地图中是否存在键。

    以下是如何修改代码以使用 Map() 的示例:

    const objectArray = [
      { name: "Bob", value: 20 },
      { name: "Jane", value: 15 },
      { name: "John", value: 5 },
      { name: "Jane", value: 10 },
    ];
    
    const result = objectArray.reduce((acc, { name, value }) => {
      const current = acc.get(name) || 0;
      acc.set(name, current + value);
      return acc;
    }, new Map());
    
    console.log(Object.fromEntries(result)); // { Bob: 20, Jane: 25, John: 5 }
    

    在此示例中,我们使用解构来提取数组中每个对象的名称和值属性。我们还使用 Map() 对象的 get() 方法来检索给定键的当前值(如果键尚不存在,则返回 0)。最后,我们使用 set() 方法更新映射中键的值。

    最后,我们使用 Object.fromEntries() 方法将 Map() 对象转换回常规对象。

    这种方法更实用,因为它避免了改变 acc 对象并使用内置的函数式编程方法。

    【讨论】:

    • 这并没有回答问题,实际上是比OP给出并试图简化的例子麻烦。