3

数组按类分组新方法:Array.prototype.groupBy

 2 years ago
source link: https://segmentfault.com/a/1190000041140503
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

你是否想过在js中如何对数组正确地进行分组?让我猜猜,你是否对结果不太满意?

数组分组是一种很常见的操作,并有很多种实现方法,但是直到现在也没有原生方法并且所有实现的方法都有些...冗长难懂?

我们将会探讨如何进行分组并简化这一切。

Setup

items数组

const items = [
  {
    type: 'fruit',
    value: '🍎',
  },
  {
    type: 'fruit',
    value: '🍇',
  },
  {
    type: 'fruit',
    value: '🍊',
  },
  {
    type: 'vegetable',
    value: '🥦',
  },
  {
    type: 'vegetable',
    value: '🥕',
  },
  {
    type: 'vegetable',
    value: '🌶️',
  },
];

items数组基于type进行分组,得到的结果如下:

{
  "fruit": [
    {
      "type": "fruit",
      "value": "🍎"
    },
    {
      "type": "fruit",
      "value": "🍇"
    },
    {
      "type": "fruit",
      "value": "🍊"
    }
  ],
  "vegetable": [
    {
      "type": "vegetable",
      "value": "🥦"
    },
    {
      "type": "vegetable",
      "value": "🥕"
    },
    {
      "type": "vegetable",
      "value": "🌶️"
    }
  ]
}

之前的分组方式

如果你想看到最好的方案可以直接查看页面底部。

Reduce

使用Array.protoype.reduce,尽管由于这种语法往往会导致难以理解,关于reducers的想法经常被弱化。

items.reduce(
  (acc, item) => ({
    ...acc,
    [item.type]: [...(acc[item.type] ?? []), item],
  }),
  {},
);

Edit:有些人提出使用{...spread}可能是 an anti-patterna bad idea,。因此让我们提供另一个reduce的解决方案。

items.reduce((acc, item) => {
  if (acc[item.type]) {
    acc[item.type].push(item);
  } else {
    acc[item.type] = [item];
  }

  return acc;
}, {});

for...of 语法

const groupedBy = {};
for (const item of items) {
  if (groupedBy[item.type]) {
    groupedBy[item.type].push(item);
  } else {
    groupedBy[item.type] = [item];
  }
}

Filter

使用多个filter方法可能是最易读的方案,但它不是最优的。需要多次过滤数组并且需要你清楚地知道每一个分组。

const groupedBy = {
  fruit: items.filter((item) => item.type === 'fruit'),
  vegetable: items.filter((item) => item.type === 'vegetable'),
};

Chaining pure functions

⚠️ Please don't do this! This is purely for demonstration purposes.

如果我们想使用函数式编程的同时避免reducers,很快就会显得很荒谬。甚至GitHub Copilot: Labs 也很难解释清楚.

我们想获取items所有的类型并将其转换为对象, 然后迭代对象的值,将值作为键,并使用过滤器按类型分组进行填充. 😵‍💫

Object.values({
  ...Array.from(new Set(items.map(({ type }) => type))),
}).map((type) => ({ [type]: items.filter((item) => item.type === type) }));

Array.prototype.groupBy

TC39 committee 正在介绍 数组分组提议 (⚠️ Stage 3). 🎉

从今天就开始使用它,需要为Array.prototype.groupBy方法提供一个符合规范的shim/polyfill: https://github.com/es-shims/A....
它应该也很快作为 stage-3 预设的一部分.

items.groupBy(({ type }) => type);

还有什么好说得,很简单不是吗?我已经迫不及待其随着ES2022一起发布,以及ts和浏览器的支持。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK