在Javascript中减少/分组数组

时间:2017-07-20 16:51:03

标签: javascript arrays reduce

基于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));

2 个答案:

答案 0 :(得分:3)

您可以使用哈希表按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; }




答案 1 :(得分:3)

@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来实现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

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

实际上,我们通过JavaScript获得Map并且没有(得到)担心 如何实现 - 并且它没有精确我们想要的行为,我们可以稍微调整其行为,而无需从头开始创建一个全新的类型。

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