Reducing/Grouping Javascript 中的数组

Reducing/Grouping an array in Javascript

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

[{
  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));

您可以使用散列 table 按 make 进行分组,并使用数组来获得所需的结果。

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

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

创建并推送到结果集。

散列 table 是用一个真正的空对象初始化的。没有原型,防止碰撞

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; }

@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 具有 一些 的一致性 – 这里最没有 table 区别Dict 是持久性的 (immutable)(在这种情况下是偏好问题)

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

好的,发明我们自己的数据类型有点紧张!在上面的示例中,我们将 Dict 基于 Array(本机)对 - 这种天真的实现细节使得 Dict 与其他使用二叉搜索树或哈希 table 到 get/set 键。我用这个例子来展示如何从一个更原始的类型构建一个更复杂的类型,但我们也可以很容易地制作我们自己的 Tree 类型并使用它。

实际上,JavaScript 给了我们 Map,我们不必(得到)担心 如何 它是如何实现的 – 以及当它没有我们想要的 exact 行为,我们可以稍微调整它的行为,而不必从头开始发明一个全新的类型。

值得注意的是,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))