从对象数组中获取所有唯一的对象属性

17

让我们想象一下,我有一个对象数组,例如:

 [{
    "firstName": "John",
    "lastName": "Doe"
 }, {
    "firstName": "Anna",
    "car": true
 }, {
    "firstName": "Peter",
    "lastName": "Jones"
 }]

我想从这个对象数组中获取所有唯一的属性名称,因此结果将是:

[firstName, lastName, car]

我该如何做:

我想象可以用类似这样的东西来实现:

function getPropertiesNames(obj){
  var arr = [];
  for(var name in obj) {
    if(arr.indexOf(name) != -1) arr.push(name);
  }
 return arr;
} 

我为什么需要它:

我要制作一个多个对象的表格。由于每个对象可能有点不同,我需要独特的属性名称。然而,我将在angularJS中完成它,所以使用循环一次用于<th>获取属性名称,再使用带有<tr ng-repeat></tr>的循环来显示值是不太适合我的选择。

我的需求:

有没有一些选项可以从对象数组中获取所有独特的属性名称,而无需迭代它?也许有一些lodash或内置的JS函数我不知道吗?

9个回答

25

只使用以下三个方法的解决方案:

var data = [{
  "firstName": "John",
  "lastName": "Doe"
}, {
  "firstName": "Anna",
  "car": true
}, {
  "firstName": "Peter",
  "lastName": "Jones"
}];

var uniqueKeys = Object.keys(data.reduce(function(result, obj) {
  return Object.assign(result, obj);
}, {}))

console.log(uniqueKeys);


嗨,谢谢你的回答。看起来更易读了一些。但是我可以问一下,与常规循环或此问题中的其他答案相比,它会更快吗? - Andurit
它不会比普通循环更快。我不确定是否有显著的差异。使用这些标准方法的主要好处是它们是安全的(即,它们遵循js标准),并且易于阅读和理解。请查看链接文章中的polyfills,以了解它们在底层执行的操作。 - user3297291

5
你可以使用 Object.assign()展开语法 将对象数组合并为一个单一对象。然后从合并的对象中获取键:
Object.keys(Object.assign({}, ...array)) 

以下是一个片段:

这里是一个片段:

const array = [{firstName:"John",lastName:"Doe"},{firstName:"Anna",car:true},{firstName:"Peter",lastName:"Jones"}],
      unique = Object.keys(Object.assign({}, ...array))

console.log(unique)


另一种选项是使用Object.keys作为flatMap的回调函数。这将返回一个包含所有键的数组。然后,创建一个Set来获取唯一键,并使用Array.from()将该集合转换为数组。

const keys = input.flatMap(Object.keys),
      unique = Array.from(new Set(keys));

这里是一个可行的片段:

const input=[{firstName:"John",lastName:"Doe"},{firstName:"Anna",car:true},{firstName:"Peter",lastName:"Jones"}],
      unique = Array.from(new Set(input.flatMap(Object.keys)));

console.log(unique)

如果不支持 flatMap,可以使用
const keys = [].concat(...input.map(Object.keys)),

4

您可以使用map()keys()返回每个对象的键,然后使用union()flatten()

var data = [{
  "firstName": "John",
  "lastName": "Doe"
}, {
  "firstName": "Anna",
  "car": true
}, {
  "firstName": "Peter",
  "lastName": "Jones"
}]

var result = _.union(_.flatten(_.map(data, (e) => _.keys(e))));
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>


感谢@NenadVracar的回答,我有两个问题。
  1. ...代表什么意思?
  2. 与我的选项或Tholle的选项相比,性能有何不同?
- Andurit
那是扩展运算符,你可以使用flatten代替,就像在update中一样。 - Nenad Vracar
你也可以像这样使用 uniqvar result = _.uniq(_.flatten(_.map(data, (e) => _.keys(e)))); - Nenad Vracar
谢谢,如果可以的话请不要犹豫将其添加到您的答案中 :) 我喜欢有多个选项。您是否了解所有这些选项与我的代码或其他人的代码相比的速度? - Andurit
你不需要用箭头函数包装 _.keys()。只需像这样传递它:_.map(data, _.keys) - Adam Boduch

3
我认为你无法避免检查每个对象中的每个键。您可以使用例如 reduce 来完成此操作:
var result = _.reduce(array, function(memory, obj) {
  for (var key in obj) {
    if(memory.indexOf(key) === -1) memory.push(key)
  }
  return memory;
}, []);

var array = [{
  "firstName": "John",
  "lastName": "Doe"
}, {
  "firstName": "Anna",
  "car": true
}, {
  "firstName": "Peter",
  "lastName": "Jones"
}];

var result = _.reduce(array, function(memory, obj) {
  for (var key in obj) {
    if(memory.indexOf(key) === -1) memory.push(key)
  }
  return memory;
}, []);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>

或者,您可以将键存储在一个新对象中,然后只提取键:

var temp = _.reduce(array, function(memory, obj) {
  for (var key in obj) {
    memory[key] = null;
  }
  return memory;
}, {});
var result = Object.keys(temp);

var array = [{
  "firstName": "John",
  "lastName": "Doe"
}, {
  "firstName": "Anna",
  "car": true
}, {
  "firstName": "Peter",
  "lastName": "Jones"
}];

var temp = _.reduce(array, function(memory, obj) {
  for (var key in obj) {
    memory[key] = null;
  }
  return memory;
}, {});
var result = Object.keys(temp);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>


谢谢您回答,@Tholle,我可以问一下使用这个的好处是什么,而不是经典的循环吗?我打赌我错过了什么,但从文档中看来像是经典循环。 - Andurit
@Andurit 不用谢!如果你有一个大数组,使用普通的循环可能会更快。如果你有一个小数组,并且像我一样觉得 reduce 更易读,那么你可以选择其中一种解决方案。 - Tholle

2

使用平坦结构的替代方案

const arr = 
[{
    "firstName": "John",
    "lastName": "Doe"
 }, {
    "firstName": "Anna",
    "car": true
 }, {
    "firstName": "Peter",
    "lastName": "Jones"
 }].map(obj => Object.getOwnPropertyNames(obj)).flat()
const s = [...new Set(arr)]
console.log(s) 


2
您可以使用以下内容:
var result = [];
array.reduce( function(pre, item) {
    Object.keys(item).forEach(function(i){
        if (result.indexOf(i) === -1){
            result.push(i);
        }
    });
});

var array = [{
  "firstName": "John",
  "lastName": "Doe"
}, {
  "firstName": "Anna",
  "car": true
}, {
  "firstName": "Peter",
  "lastName": "Jones"
}];

var result = [];
array.reduce( function(pre, item) {
    Object.keys(item).forEach(function(i){
        if (result.indexOf(i) === -1){
            result.push(i);
        }
    });
});

console.log(result);


2
你也可以尝试这个:
var source = [{
"firstName": "John",
"lastName": "Doe"
 }, {
"firstName": "Anna",
"car": true
 }, {
"firstName": "Peter",
"lastName": "Jones"
 }];

uniq(source);

function uniq(source){
   var result = source.reduce(function(p, c) {

     Object.keys(c).forEach(function(key) {
         p[key] = true;
     });

     return p;
    }, {});

    return Object.keys(result);
}

2

我的解决方案不使用任何库。

var array = [{
  "firstName": "John",
  "lastName": "Doe"
}, {
  "firstName": "Anna",
  "car": true
}, {
  "firstName": "Peter",
  "lastName": "Jones"
}];

var arr = [],merged,uniqArray;

array.forEach(function(val){ //Getting all properties
   arr.push(Object.keys(val))
 });

merged = [].concat.apply([], arr);//Merging all array to single array

merged.forEach(function(val){
  if(uniqArray.indexOf(val)== -1){// Getting uniqe values
   uniqArray.push(val)
}})

结果

["firstName", "lastName", "car"]

0

一行代码的选项:

[...new Set(a.map(Object.keys).reduce((a, b) => a.concat(b)))];

我看到有人不喜欢它。 - The Witness

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