17

Let's imagine I have an array of objects e.g.

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

I want to get all the unique property names from this array of objects, so the result will be:

[firstName, lastName, car]

How can I do it:

I can imagine that it's possible to do this with something like this:

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

Why I need it:

I will have to make a table of multiple objects. Because each object can be a bit different I need unique property names. However I am going to do it in angularJS so it's kind of a bad option for me to once use loop to get property names for <th> and once again use loop with <tr ng-repeat></tr> to display values.

What i want:

Is there some option to get all unique property names from an array of objects without iterating it? Maybe some lodash or build in JS function which I don't know?

9 Answers 9

25

A solution using only:

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

Sign up to request clarification or add additional context in comments.

2 Comments

Hi, thank you for answer. It looks a bit more readable. However may I ask you if it will be faster in compare with regular loop or answers in this questions?
It will not be faster than a regular loop. I'm not sure if there's a significant difference. The main benefits of using these standard methods is that they're safe (as in, they follow the js standards), and easy to read and understand. Have a look at the polyfills in the linked articles to get a feel of what they're doing under the hood.
5

You can use Object.assign() and spread syntax to merge the array of objects to a single object. Then get the keys from the merged object:

Object.keys(Object.assign({}, ...array)) 

Here's a snippet:

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

console.log(unique)


Another option is to use Object.keys as callback to flatMap. This returns an array of all the keys. Then, create a Set to get unique keys and use Array.from() to convert the set to an array.

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

Here's a working snippet:

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)

If flatMap is not supported, you can use

const keys = [].concat(...input.map(Object.keys)),

Comments

4

You could use map() and keys() to return keys of each object and then union() and 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>

5 Comments

Thank you for answer @NenadVracar, I would have 2 questions about it. 1. What this: ... means? 2. Any idea about performance in compare with my option or one from Tholle
That is spread operator, you can use flatten instead, like in update.
You can also use uniq like this var result = _.uniq(_.flatten(_.map(data, (e) => _.keys(e))));
Thank you, please do not hesitate to add it to your answer :) I like that there are multiple options. Do you have any knowleage about speed of all of this options in compare with my code or codes below from other ppl?
You don't need to wrap _.keys() in an arrow function. Just pass it like this: _.map(data, _.keys)
3

I don't think you can get away from checking each key in each object. You could accomplish it with e.g 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>

Alternatively, you could store the keys in a new object, and just extract the keys:

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>

2 Comments

Thank you for answer @Tholle, may I ask you what's that advantage of using this instead of clasic loop? I bet i miss something but from documentation it sounds like clasic loop
@Andurit You're welcome! If you have a big array, a regular loop will probably be faster. If you have a small array, and you are like me and find reduce more readable, you might go with one of these solutions.
2

You may also try this:

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

Comments

2

You can use this:

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

Comments

2

My solution without any library.

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

RESULT

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

Comments

2

Alternative using flat

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) 

Comments

0

One liner option:

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

1 Comment

I see someone didn't like it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.