【问题标题】:groupBy and sortBy an array of object in native ES6groupBy 和 sortBy 原生 ES6 中的对象数组
【发布时间】:2018-10-12 14:50:16
【问题描述】:

我们有一个对象数组,我们需要按组 ID 对这些对象进行分组。

然后我们需要得到一个按时间戳排序的数组数组。

这是初始集合:

const arr = [
  {id: 'id1', group: '123', timestamp: 222},
  {id: 'id2', group: '123', timestamp: 999 },
  {id: 'id3', group: '456', timestamp: 333 },
  {id: 'id4', group: '789', timestamp: 111 },
  {id: 'id5', group: '554', timestamp: 555 },
];

我们尝试得到的结果是:

result = [
  [
    {id: 'id4', group: '789', timestamp: 111 }, 
  ],
  [
    {id: 'id1', group: '123', timestamp: 222},
    {id: 'id2', group: '123', timestamp: 999 },
  ],
  [
    {id: 'id3', group: '456', timestamp: 333 },
  ],
  [
    {id: 'id5', group: '554', timestamp: 555 },
  ]
]

我们使用 Underscore.js 实现了这一点,但我们正在寻找使用本机 javascript 的解决方案,并且我们很难从简化对象传递到数组数组。

var groups = _(arr).chain()
    .groupBy("group")
    .sortBy((data) => _.first(data).timestamp)
    .value();

https://jsfiddle.net/tdsow1ph/

我们首先尝试使用这种方法对项目进行分组:

const groupBy = (arr, propertyToGroupBy) => {
    return arr.reduce((groups, item) => {
      const val = item[propertyToGroupBy];

      groups[val] = groups[val] || [];
      groups[val].push(item);

      return groups;
    }, {});
  }

但是我们得到了一个不可排序的对象。所以我们需要将其转换为数组数组,但我们很难做到这一点,然后对时间戳值应用排序。

【问题讨论】:

  • 先排序再分组可能更容易。

标签: javascript ecmascript-6 underscore.js lodash


【解决方案1】:

您可以使用reduce() 将数组分组为一个对象,使用组作为键。使用Object.values()将对象转化为数组。

const arr = [{"id":"id1","group":"123","timestamp":222},{"id":"id2","group":"123","timestamp":999},{"id":"id3","group":"456","timestamp":333},{"id":"id4","group":"789","timestamp":111},{"id":"id5","group":"554","timestamp":555}];

var result = Object.values(arr.reduce((c, v) => {
  c[v.group] = c[v.group] || [];
  c[v.group].push(v);
  return c;
}, {}));


console.log(result);

sort()

const arr = [{"id":"id4","group":"789","timestamp":111},{"id":"id1","group":"123","timestamp":222},{"id":"id3","group":"456","timestamp":333},{"id":"id5","group":"554","timestamp":555},{"id":"id2","group":"123","timestamp":999}]

var result = Object.values(
  arr
  .sort((a, b) => a.timestamp - b.timestamp)
  .reduce((c, v) => {
    c[v.group] = c[v.group] || [];
    c[v.group].push(v);
    return c;
  }, {})
);

console.log(result);

【讨论】:

  • 谢谢它的工作,然后我可以正确排序。没想到 Object.values!
  • 乐于助人:)
  • 你知道如何实现正确的排序以获得时间戳排序吗?我尝试在结果上应用.sort((a: any, b: any) => a[0].timestamp - b[0].timestamp),但如果原始数组之前没有按时间戳排序,则它不起作用
  • 您可以使用.sort((a, b) => a.timestamp - b.timestamp) 进行排序:)
  • 所以提示是在之前而不是之后排序。谢谢:)
【解决方案2】:

这是另一个在 ES6 中使用 Map objectreduce 的单行实现:

const arr = [ {id: 'id1', group: '123', timestamp: 222}, {id: 'id2', group: '123', timestamp: 999 }, {id: 'id3', group: '456', timestamp: 333 }, {id: 'id4', group: '789', timestamp: 111 }, {id: 'id5', group: '554', timestamp: 555 }, ];

const result = [...arr.reduce((r,c) => (r.set(c.group, r.has(c.group) ? 
  [...r.get(c.group), c] : [c]), r), new Map()).values()]

console.log(result)

对于排序只是前缀减少与初始sort

const arr = [ {id: 'id1', group: '123', timestamp: 222}, {id: 'id2', group: '123', timestamp: 999 }, {id: 'id3', group: '456', timestamp: 333 }, {id: 'id4', group: '789', timestamp: 111 }, {id: 'id5', group: '554', timestamp: 555 }, ];

const result = [...arr.sort((a,b) => a.timestamp - b.timestamp)
   .reduce((r,c) => (r.set(c.group, r.has(c.group) ? 
   [...r.get(c.group), c] : [c]), r), new Map()).values()]

console.log(result)

【讨论】:

  • 你知道这个解决方案的性能其他公认的答案吗?
  • 你知道如何对时间戳进行正确排序吗?我尝试在您的代码给我的结果上应用.sort((a: any, b: any) => a[0].timestamp - b[0].timestamp),但如果原始数组尚未按时间戳排序,则它无法正常工作。
  • 相信 JS 性能测试的人并不多,但这里有一个证明它做得很好:jsben.ch/xFrFE
  • 也添加了排序
  • 在 typescript 2.x 中,我必须使用 [...Array.from(arr.sort 才能传播 :) 因为传播地图是不可能的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-11
  • 1970-01-01
  • 2016-01-20
  • 2018-01-28
  • 2015-06-21
相关资源
最近更新 更多