52

I have an array of objects and I want to get a new array from it that is unique based only on a single property, is there a simple way to achieve this?

Eg.

[ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]

Would result in 2 objects with name = bill removed once.

8 Answers 8

81

Use the uniq function

var destArray = _.uniq(sourceArray, function(x){
    return x.name;
});

or single-line version

var destArray = _.uniq(sourceArray, x => x.name);

From the docs:

Produces a duplicate-free version of the array, using === to test object equality. If you know in advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iterator function.

In the above example, the function uses the objects name in order to determine uniqueness.

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

4 Comments

Hi, Is it possible to check unique on multiple attribute for example for the above example say by id and name both?
@APaul Check the answer given by me. Hope you would find what you are actually looking for.
As of the latest version of underscore (1.10.2) the syntax is as follows: _.uniq(array, [isSorted], [iteratee]), i.e., you should pass the function as the third argument. See the docs underscorejs.org/#uniq
lodash uniq breaks here! took me an hour to debug. you have to use uniqBy as lodash is NOT a drop in replacement for underscore, it silently fails on a few things.
10

If you prefer to do things yourself without Lodash, and without getting verbose, try this uniq filter with optional uniq by property:

const uniqFilterAccordingToProp = function (prop) {
    if (prop)
        return (ele, i, arr) => arr.map(ele => ele[prop]).indexOf(ele[prop]) === i
    else
        return (ele, i, arr) => arr.indexOf(ele) === i
}

Then, use it like this:

const obj = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
obj.filter(uniqFilterAccordingToProp('abc'))

Or for plain arrays, just omit the parameter, while remembering to invoke:

[1,1,2].filter(uniqFilterAccordingToProp())

Comments

8

If you want to check all the properties then lodash 4 comes with _.uniqWith(sourceArray, _.isEqual)

1 Comment

How about 2+ properties, but not all properties?
5

A better and quick approach

var table = [
  {
    a:1,
    b:2
  },
  {
    a:2,
    b:3
  },
  {
    a:1,
    b:4
  }
];

let result = [...new Set(table.map(item => item.a))];
document.write(JSON.stringify(result));

Found here

5 Comments

Thanks Mudasar Rauf. No need of underscore js or any other library plain JavaScript. But use var instead of let so that it can work in IE or old browsers.
@SyedNasirAbbas Set is not supported anyways.
This is slightly different than the OP-s request, as this returns the unique values of a, whereas we should get all children of table that have a unique a property. Difference in results is [1, 2] (current) vs [{ a: 1, b: 2 }, { a: 2, b: 3 }] (expected).
@kano it is taking the first records. otherwise the best solution is to use underscore answered by ColinE this is a quick way when you know the tradeoff.
it does not solve the OP request because it returns the properties and not the objects
3

You can use the _.uniqBy function

var array = [ { id: 1, name: 'bob' }, { id: 2, name: 'bill' }, { id: 1, name: 'bill' },{ id: 2, name: 'bill' } ];

var filteredArray = _.uniqBy(array,function(x){ return x.id && x.name;});

console.log(filteredArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>

In the above example, filtering is based on the uniqueness of combination of properties id & name.

if you have multiple properties for an object. then to find unique array of objects based on specific properties, you could follow this method of combining properties inside _.uniqBy() method.

4 Comments

@el_pup_le could you check this answer and let me know if this was not what you were expecting.
doesn't work. The callback function to uniqBy needs to return a value that is unique for that data item. You always return true, which is not unique to any of the data items. Try this return ${x.name}/{x.id};
@BruceJo , I have included a sample as well for demoying the solution and is showing the expected behaviour. The question was for obtaining an array of unique objects based on a single property. What i have given as a solution is for making it uniq by considering multiple properties if required.
Yes, that is how I understand that you posed the problem. But your answer is only unique by name. x.id is always !falsy, so your return is simply x.name. If you wanted uniqueness of 2 properties, then id: 1, name: bill should be in the result. It is not. Try using the return `${x.id}/${x.name]` and you will see what I mean.
2

I was looking for a solution which didn't require a library, and put this together, so I thought I'd add it here. It may not be ideal, or working in all situations, but it's doing what I require, so could potentially help someone else:

const uniqueBy = (items, reducer, dupeCheck = [], currentResults = []) => {
  if (!items || items.length === 0) return currentResults;
  
  const thisValue = reducer(items[0]);
  
  const resultsToPass = dupeCheck.indexOf(thisValue) === -1 ?
    [...currentResults, items[0]] : currentResults;
    
  return uniqueBy(
    items.slice(1),
    reducer,
    [...dupeCheck, thisValue],
    resultsToPass,
  );    
}

const testData = [
  {text: 'hello', image: 'yes'},
  {text: 'he'},
  {text: 'hello'},
  {text: 'hell'},
  {text: 'hello'},
  {text: 'hellop'},
];

const results = uniqueBy(
  testData,
  item => {
    return item.text
  },
)

console.dir(results)

2 Comments

“Set” looks better
@sonnenhaft Set doesn't give you the option to determine uniqueness programatically as far as I know
1

In case you need pure JavaScript solution:

var uniqueProperties = {};

var notUniqueArray = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ];


for(var object in notUniqueArray){
   uniqueProperties[notUniqueArray[object]['name']] = notUniqueArray[object]['id'];
}

var uniqiueArray = [];

for(var uniqueName in uniqueProperties){
   uniqiueArray.push(
     {id:uniqueProperties[uniqueName],name:uniqueName});
}

//uniqiueArray

3 Comments

one up for pure Javascript solution. Like the alternative
Seems wrong to me. You're assuming a property name will only ever occur once?
@hasen I think you have not got the idea here. Execute the code in your browser console or read the OP question again.
1

unique array by id property with ES6:

arr.filter((a, i) => arr.findIndex(b => b.id === a.id) === i);  // unique by id

replace b.id === a.id with the relevant comparison for your case

Comments

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.