【问题标题】:Reducing/Grouping an array in Javascript在Javascript中减少/分组数组
【发布时间】:2017-07-20 16:51:02
【问题描述】:

基于this 示例,我想以稍微不同的方式按对象分组。结果应该如下:

[{
  key: "audi"
  items: [
    {
      "make": "audi",
      "model": "r8",
      "year": "2012"
    },
    {
      "make": "audi",
      "model": "rs5",
      "year": "2013"
    }
  ]
},
...
]

我怎样才能做到这一点?我写的以下代码没有做我想要的:

reduce(function (r, a) {
        r[a.art] = {key: r[a.art], items: []} || [];
        r[a.art].items.push(a);
        return r;
    }, Object.create(null));

【问题讨论】:

  • 您开始使用的数据是什么?你有什么尝试,你的错误是什么?
  • 我的问题是,我不太明白原始示例中的缩减是如何工作的。我尝试了以下代码,但它产生了一个非常奇怪的结果:reduce(function (r, a) { r[a.art] = {key: r[a.art], items: []} || []; r[a.art].items.push(a); return r; }, Object.create(null));
  • 欢迎来到 Stack Overflow!您似乎在要求某人为您编写一些代码。 Stack Overflow 是一个问答网站,而不是代码编写服务。请see here学习如何写出有效的问题。
  • 我认为您使用的数据与您发布的示例中的数据相同,但后来我看到您的代码查找 a.art,我不知道它是什么,所以我无法编辑你的问题的起始数据,你必须这样做。
  • @James:是的,我从我自己的代码中复制了它,它使用艺术而不是制造。对不起^^

标签: javascript arrays reduce


【解决方案1】:

您可以使用哈希表按make 进行分组,并使用数组来获得想要的结果。

对于hash 中的每个组,一个新对象,例如

{
    key: a.make,
    items: []
}

被创建并推送到结果集。

哈希表是用一个真正空的对象初始化的。没有原型,以防止碰撞。

var cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }],
    hash = Object.create(null),
    result = [];

cars.forEach(function (a) {
    if (!hash[a.make]) {
        hash[a.make] = { key: a.make, items: [] };
        result.push(hash[a.make]);
    }
    hash[a.make].items.push(a);
});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 谢谢!效率呢?我听说,forEach 函数不是最快的。这是真的吗?
  • 您可以对索引使用 for 循环,这实际上是用于迭代的禁食,但我认为这应该不是问题。你使用的数组长度是多少?
  • @NinaScholz 一如既往的好答案——我希望你不介意我在我的回答中复制了你的测试data 用于演示目的^_^
  • @naomik,没关系,我喜欢你的回答(它们看起来像讲座)
  • @NinaScholz 最多 50 条记录。所以我想这不是什么大问题,对吧?
【解决方案2】:

@Nina 的答案实用、高效,绝对是您应该阅读的答案。但是,这些问题对我来说很有趣,我喜欢考虑以其他方式解决它们,即使这意味着进行交易。

JavaScript 中的复合数据相等性

在 JavaScript 中测试复合数据相等性可能很麻烦

console.log (1 === 1)         // true
console.log ('a' === 'a')     // true
console.log ([1,2] === [1,2]) // false
console.log ({a:1} === {a:1}) // false

这种简单的相等性测试也可能使处理 JavaScript 的原生 SetMap 变得有些挑战

const m = new Map ()
m.set ([1,2], 'hello')
console.log (m.get ([1,2]))                      // undefined
console.log (m.get (Array.from (m.keys ()) [0])) // 'hello'

混蛋!复合数据相等性再次困扰我们。 m.get 找不到键 [1,2] 因为第一个键(我们设置的)[1,2] 与第二个键(获取)[1,2] 不同——即[1,2] 的两个实例位于不同的内存位置并因此被认为(由 JavaScript)不相等 (!==)


复合数据相等,取2

如果我们不想,我们不必遵守 JavaScript 的规则。在这部分答案中,我们创建了自己的Dict(字典)复合数据类型,它接受用于确定键相等性的函数

想象一下Dict 正在做这样的事情

const d = Dict (({a} => a)
d.has ({a:1}) // false
d.set ({a:1}, 'hello') .has ({a:1}) // true
d.set ({a:1}, 'hello') .get ({a:1}) // 'hello'
d.get ({a:2}) // undefined
d.set ({a:2}, 'world') .get ({a:2}) // 'world'

如果我们有一个像Dict 这样工作的数据类型,那么我们可以轻松地为我们的数据编写必要的转换

// our Dict type with custom key comparator
const DictByMake =
  Dict (x => x.make)

const dict =
  data.reduce((d, item) =>
    d.set (item, d.has (item)
      ? d.get (item) .concat ([item])
      : [item]), DictByMake ())

我说如果我们有像Dict 这样的数据类型,因为乐观是好事。我为什么要做出牺牲并选择一种无法满足我需求的数据类型,而我不必这样做?如果我需要的类型不存在,我可以制作一个。在此先感谢 JavaScript!

下面我实现Dict 与JS 的SetMap一些 一致性——这里最显着的区别是Dict 是持久的(不可变的)(一个偏好问题,在本例)

const Pair = (left, right) => ({
  left,
  right
})

const Dict = eq => (pairs=[]) => ({
  equals (x, y) {
    return eq (x) === eq (y)
  },
  has (k) {
    for (const {left} of pairs)
      if (this.equals (k, left))
        return true
    return false
  },
  get (k) {
    for (const {left, right} of pairs)
      if (this.equals (k, left))
        return right
    return undefined
  },
  set (k, v) {
    for (const [i, {left, right}] of pairs.entries ())
      if (this.equals (k, left))
        return Dict (eq) (pairs
          .slice (0, i)
          .concat ([Pair (k, v)])
          .concat (pairs.slice (i+1)))
    return Dict (eq) (pairs.concat ([Pair (k, v)]))
  },
  entries () {
    return {
      *[Symbol.iterator] () {
        for (const {left, right} of pairs)
          yield [eq (left), right]
      }
    }
  }
})

const DictByMake =
  Dict (x => x.make)

const main = data => {
  // build the dict
  const dict =
    data.reduce((d, x) =>
      d.set(x, d.has (x)
        ? [...d.get (x), x]
        : [x]), DictByMake ())
  // convert dict key/value pairs to desired {key, items} shape
  return Array.from (dict.entries (), ([key, items]) =>
      ({ key, items }))
} 

const data = 
  [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }]
  
console.log (main (data))

复合数据相等,取3

好的,发明我们自己的数据类型有点紧张!在上面的示例中,我们基于 Array(本机)对的 Dict 与其他使用二叉搜索树或哈希表来获取/设置的关联类型相比,这种幼稚的实现细节使 Dict 效率低下键。我用这个例子来展示如何从一个更原始的类型构建一个更复杂的类型,但是我们可以很容易地创建我们自己的 Tree 类型并使用它来代替。

实际上,JavaScript 给了我们Map 并且不必(得到)担心如何它是如何实现的——虽然它没有确切的 em> 我们想要的行为,我们可以稍微调整它的行为,而无需从头开始发明一种全新的类型。

值得注意的是,MapBy 没有在此处实现为持久结构

const MapBy = ord => (map = new Map ()) => ({
  has: k =>
    map.has (ord (k)),
  get: k =>
    map.get (ord (k)),
  set: (k, v) =>
    MapBy (ord) (map.set (ord (k), v)),
  keys: () =>
    map.keys (),
  values: () =>
    map.values (),
  entries: () =>
    map.entries ()
})

// the rest of the program stays exactly the same (with exception to variable names)
const MapByMake =
  MapBy (x => x.make)

const main = data => {
  const map =
    data.reduce((m, x) =>
      m.set(x, m.has (x)
        ? [...m.get (x), x]
        : [x]), MapByMake ())
  return Array.from (map.entries (), ([key, items]) =>
      ({ key, items }))
} 

const data = 
  [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }]
      
console.log (main (data))

【讨论】:

猜你喜欢
  • 2021-08-07
  • 2023-02-24
  • 1970-01-01
  • 2021-03-10
  • 2020-12-07
  • 2018-09-01
  • 1970-01-01
  • 2023-03-06
相关资源
最近更新 更多