Join the Stack Overflow Community
Stack Overflow is a community of 4.7 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

I'm learning JS. Supposing I have the below array of objects:

var family = [
  {
    name: "Mike",
    age: 10
  },
  {
    name: "Matt"
    age: 13
  },
  {
    name: "Nancy",
    age: 15
  },
  {
    name: "Adam",
    age: 22
  },
  {
    name: "Jenny",
    age: 85
  },
  {
    name: "Nancy",
    age: 2
  },
  {
    name: "Carl",
    age: 40
  }
];

Notice that Nancy is showing up twice (changing only the age). Supposing I want to output only unique names. How do I output the above array of objects, without duplicates? ES6 answers more than welcome.

Related (couldn't find a good way for usage on objects):

EDIT Here's what I tried. It works well with strings but I can't figure how to make it work with objects:

family.reduce((a, b) => {
  if (a.indexOf(b) < 0 ) {
    a.push(b);
  }
  return a;
},[]);
share|improve this question
1  
can you post what you have tried? – JordanHendrix 23 hours ago
    
Actually I tried most of answers from both related questions I mentioned. – Rico Letterman 23 hours ago
1  
can someone explain why those downvotes? Should I copy/paste any code that doesn't work here just to say I tried? – Rico Letterman 23 hours ago
    
its because this isnt actually too hard, but require a little bit more research, google this: get unique objects array javascript – JordanHendrix 23 hours ago
    
@RicoLetterman I think they just want examples of what you've done however futile it might have been. Without examples showing you tried something and why it failed, it seems like you're using stackoverflow to do the thinking for you. – AR7 23 hours ago

I would probably set up some kind of object. Since you've said ECMAScript 6, you have access to Set, but since you want to compare values on your objects, it will take a little more work than that.

An example might look something like this (removed namespace pattern for clarity):

var setOfValues = new Set();
var items = [];

function add(item, valueGetter) {
   var value = valueGetter(item);
   if (setOfValues.has(value))
      return;

   setOfValues.add(value);
   items.push(item);
}

function addMany(items, valueGetter) {
   items.forEach(item => add(item, valueGetter));
}

Use it like this:

var family = [
...
];

addMany(family, item => item.name);

// items will now contain the unique items

Explanation: you need to pull a value from each object as it's added and decide if it has already been added yet, based on the value you get. It requires a value getter, which is a function that given an item, returns a value (item => item.name). Then, you only add items whose values haven't already been seen.


A class implementation:

// Prevents duplicate objects from being added

class ObjectSet {
  constructor(key) {
    this.key = key;
    this.items = [];
    this.set = new Set();
  }

  add(item) {
    if (this.set.has(item[this.key])) return;
    this.set.add(item[this.key]);
    this.items.push(item);
  }

  addMany(items) {
    items.forEach(item => this.add(item));
  }
}

var mySet = new ObjectSet('name');
mySet.addMany(family);
console.log(mySet.items);
share|improve this answer
    
This is so unnecessarily, overly complex – AR7 23 hours ago
    
@AR7 complex? I wrote it in like 5 minutes. It just looks ugly because that's how encapsulation works in javascript. – Sahuagin 22 hours ago
    
@AR7 I've remove the namespace pattern which was unnecessary – Sahuagin 22 hours ago
    
If you're going to include ES6 Sets, why not also ES6 classes since that would encapsulate your code just as well as your namespace pattern did. Now it just looks weird lol. – AR7 22 hours ago
1  
@AR7 I'm in the middle of learning ES6. not a fan of ES6 classes yet and I can't just code one up. (Actually I've switched to typescript.) – Sahuagin 22 hours ago

The solution here is to not compare objects using indexOf, which only uses the === operator between objects. The reason why your current answer doesn't work is because === in JS does not compare the objects deeply, but instead compares the references. What I mean by that you can see in the following code:

var a = { x: 1 }
var b = { x: 1 }

console.log(a === b) // false
console.log(a === a) // true

Equality will tell you if you found the same exact object, but not if you found an object with the same contents.

In this case, you can compare your object on name since it should be a unique key. So obj.name === obj.name instead of obj === obj.


So store occurrences of name external to the loop in an object, and filter if there's been a previous occurrence.

https://jsfiddle.net/nputptbb/2/

var occ = {}

var filtered = family.filter(function(x) {
  if (occ[x.name]) return false;
  occ[x.name] = true;
  return true;
})

Moreover another problem with your code that affects its runtime and not its function is that you use an indexOf inside of your reduce. indexOf is O(n), which makes the complexity of your algorithm O(n^2).

It's better to use an object, which has O(1) lookup.

share|improve this answer
    
Not a fan of the downvotes :/ – AR7 23 hours ago

You could use a Set in combination with Array#map and a spread operator ... in a single line.

Map returns an array with all names, which are going into the set initializer and then all values of the set are returned in an array.

var family = [{ name: "Mike", age: 10 }, { name: "Matt", age: 13 }, { name: "Nancy", age: 15 }, { name: "Adam", age: 22 }, { name: "Jenny", age: 85 }, { name: "Nancy", age: 2 }, { name: "Carl", age: 40 }],
    unique = [...new Set(family.map(a => a.name))];

console.log(unique);

share|improve this answer
    
Is there a way to output my array of objects instead of this flat array? – Rico Letterman 1 hour ago
    
@RicoLetterman it's in my answer. – AR7 1 hour ago

With the code you mentioned, you can try:

family.filter((item, index, array) => {
  return array.map((mapItem) => mapItem['name']).indexOf(item['name']) === index
})

Or you can have a generic function to make it work for other array of objects as well:

function printUniqueResults (arrayOfObj, key) {
  return arrayOfObj.filter((item, index, array) => {
    return array.map((mapItem) => mapItem[key]).indexOf(item[key]) === index
  })
}

and then just use printUniqueResults(family, 'name')

(FIDDLE)

share|improve this answer
var temp = [];

family.forEach(function(object ){
     var dupe = false;

    temp.forEach(function(temp){

       If(temp.name == object.name){

       dupe = true;

    });

    If(!dupe){
        temp.push(object)
    }

});

Something like this, currently on my phone possible syntax errors.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.