使用reduce函数获取JavaScript数组中所有对象的键的并集

4
假设我们有:
var all=[
    {firstname:'Ahmed', age:12},
    {firstname:'Saleh', children:5 }
    {fullname: 'Xod BOD', children: 1}
];

预期结果是['firstname','age', 'children', 'fullname']:该数组中所有对象的键的并集:
all.map((e) => Object.keys(e) ).reduce((a,b)=>[...a,...b],[]); 

这个代码可以正常运行,但我想使用更高效的方法,直接使用reduce而不是map。我尝试了以下代码,但它失败了。

all.reduce((a,b) =>Object.assign([...Object.keys(a),...Object.keys(b)]),[])

1
可能更适合在codereview上发布,但Object.keys(Object.assign({}, ...all))是一个选择。我对性能一无所知。 - user1106925
4个回答

5

你可以使用Setreduce()Object.keys(),不需要使用map。

var all=[
  {firstname:'Ahmed', age:12},
  {firstname:'Saleh', children:5 },
  {fullname: 'Xod BOD', children: 1}
];

var result = [...new Set(all.reduce((r, e) => [...r, ...Object.keys(e)], []))];
console.log(result)


1
为什么要用Set?你的all.reduce((r, e) => r.concat(Object.keys(e)), [])已经返回了一个数组。 - user1106925

1
这里有一个使用通用程序concatflatMap和ES6 Set的解决方案。它类似于@NenadVracar的解决方案,但使用高阶函数而不是复杂的一行实现。这减少了转换中的复杂性,并使在程序的其他区域中重复使用程序更容易。请注意,虽然...扩展语法并不差,但您还会注意到这个解决方案并不需要它。

var all = [
  {firstname:'Ahmed', age:12},
  {firstname:'Saleh', children:5 },
  {fullname: 'Xod BOD', children: 1}
];

const concat = (x,y) => x.concat(y);

const flatMap = f => xs => xs.map(f).reduce(concat, []);

const unionKeys = xs =>
  Array.from(new Set(flatMap (Object.keys) (xs)));

console.log(unionKeys(all));
// [ 'firstname', 'age', 'children', 'fullname' ]


0

只是出于好奇,我一直在使用不同的方法(reduce vs foreach vs set)对您的问题进行基准测试。看起来Set对于小数组表现良好,但对于更大的数组非常慢(最佳解决方案是foreach)。

希望这有所帮助。

var all = [{
    firstname: 'Ahmed',
    age: 12
  }, {
    firstname: 'Saleh',
    children: 5
  }, {
    fullname: 'Xod BOD',
    children: 1
  }],
  result,
  res = {};

const concat = (x,y) => x.concat(y);

const flatMap = f => xs => xs.map(f).reduce(concat, []);

const unionKeys = xs =>
  Array.from(new Set(flatMap (Object.keys) (xs)));

for(var i = 0; i < 10; i++)
  all = all.concat(all);

console.time("Reduce");
result = Object.keys(all.reduce((memo, obj) => Object.assign(memo, obj), {}));
console.timeEnd("Reduce");

console.time("foreach");
all.forEach(obj => Object.assign(res, obj));
result = Object.keys(res);
console.timeEnd("foreach");

console.time("Set");
result = [...new Set(all.reduce((r, e) => r.concat(Object.keys(e)), []))];
console.timeEnd("Set");

console.time("Set2");
result = unionKeys(all);
console.timeEnd("Set2");


你不应该将 {} 作为备忘录使用。应该使用 SetMap 来处理这种情况。 - Mulan
@naomik,正如您在帖子中所读到的那样,我正在测试一些实现的性能,其中一个实现使用了Set。 - acontell
你并没有将 Set 作为备忘录使用,因此你的比较几乎完全没有意义。Set 只用于从结果中删除非唯一值。 - Mulan

0

尝试这段代码:

var union = new Set(getKeys(all));

console.log(union);
// if you need it to be array
console.log(Array.from(union));

//returns the keys of the objects inside the collection
function getKeys(collection) {
    return collection.reduce(
        function(union, current) {
            if(!(union instanceof Array)) {
                union = Object.keys(union);
            }
            return union.concat(Object.keys(current));
        });
}

这是一个勇敢的尝试,但 addToSet 改变了 set,在函数式程序中通常不被建议。 - Mulan
@naomik 你所说的“mutates set”是什么意思?这里的set只是一个变量。 - victor175
@naomik 在这种特定情况下,为什么这样做会是一件不好的事情? - victor175
函数式编程的目标是创建纯函数 - 变异应该以一种允许您的函数运行而不产生副作用的方式进行本地化。 - Mulan
@naomik 谢谢您的评论,我已经进行了修改,尝试去除这个缺陷 (: - victor175

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接