javascript - lodash - create a unique list based on multiple attributes

15,716

Solution 1

You can use uniqBy() to compute the criterion by which the list is generated wherein the iteratee callback returns the joined value of id and sequence of each item of the colection. The solution below works great when you only want to have specific properties to be the criterion of uniqueness especially when each object in your collection may not only have the id and sequence properties.

var result = _.uniqBy(list, v => [v.id, v.sequence].join());

var list = [{
  id: '12345',
  sequence: null
}, {
  id: '12346',
  sequence: null
}, {
  id: '12347',
  sequence: null
}, {
  id: '12348',
  sequence: 1
}, {
  id: '12348',
  sequence: 2
}, {
  id: '12349',
  sequence: 1
}, {
  id: '12349',
  sequence: 1
}, {
  id: '123490',
  sequence: 1,
  extra: 1
}, {
  id: '123490',
  sequence: 1,
  extra: 2
}];

var result = _(list)
  .uniqBy(v => [v.id, v.sequence].join())
  .value();

document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.12.0/lodash.js"></script>

Alternatively, if you have properties that contains non-primitive values (objects or arrays) to be used as the criterion then you can still use the solution above but instead joining the array of values, you have to use JSON.stringify()

var result = _.uniqBy(list, v => JSON.stringify([v.id, v.sequence]));

var list = [{
  id: '12345',
  sequence: null
}, {
  id: '12346',
  sequence: null
}, {
  id: '12347',
  sequence: null
}, {
  id: '12348',
  sequence: 1
}, {
  id: '12348',
  sequence: 2
}, {
  id: '12349',
  sequence: 1
}, {
  id: '12349',
  sequence: 1
}, {
  id: '123490',
  sequence: 1,
  extra: 1
}, {
  id: '123490',
  sequence: 1,
  extra: 2
}];

var result = _.uniqBy(list, v => JSON.stringify([v.id, v.sequence]));

document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.12.0/lodash.js"></script>

UPDATE:

For the lodash3 solution, for properties with primitive values, you can still use the pattern in the first solution using uniq().

var result = _.uniq(list, v => [v.id, v.sequence].join());

var list = [{
  id: '12345',
  sequence: null
}, {
  id: '12346',
  sequence: null
}, {
  id: '12347',
  sequence: null
}, {
  id: '12348',
  sequence: 1
}, {
  id: '12348',
  sequence: 2
}, {
  id: '12349',
  sequence: 1
}, {
  id: '12349',
  sequence: 1
}, {
  id: '123490',
  sequence: 1,
  extra: 1
}, {
  id: '123490',
  sequence: 1,
  extra: 2
}];

var result = _.uniq(list, v => [v.id, v.sequence].join());

document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>

And for the non-primitive properties, you can still use the 2nd example above with uniq()

var result = _.uniq(list, v => JSON.stringify([v.id, v.sequence]));

var list = [{
  id: '12345',
  sequence: null
}, {
  id: '12346',
  sequence: null
}, {
  id: '12347',
  sequence: null
}, {
  id: '12348',
  sequence: 1
}, {
  id: '12348',
  sequence: 2
}, {
  id: '12349',
  sequence: 1
}, {
  id: '12349',
  sequence: 1
}, {
  id: '123490',
  sequence: 1,
  extra: 1
}, {
  id: '123490',
  sequence: 1,
  extra: 2
}];

var result = _.uniq(list, v => JSON.stringify([v.id, v.sequence]));

document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>

Solution 2

You may use uniqWith

This method is like _.uniq except that it accepts comparator which is invoked to compare elements of array. The comparator is invoked with two arguments: (arrVal, othVal).

and isEqual

Performs a deep comparison between two values to determine if they are equivalent.

_.uniqWith(list, _.isEqual);
Share:
15,716
Sanath
Author by

Sanath

Senior Frontend Engineer with 12+ years of experience in the software development industry including 7+ years of Australian experience, enabling me to improve my professional skills and gain technical competencies. I have worked extensively with Angular JS, React, VueJs, and the related front-end technology stack. My experience in NodeJs and J2EE based technologies has allowed me to gain a good technical understanding about the total application architecture and the best practices required in end to end implementation of a business solution. I am passionate about the evolving front-end technology landscape and have been working with some of the cutting-edge technologies in that domain.

Updated on June 11, 2022

Comments

  • Sanath
    Sanath almost 2 years

    My collection looks like this.

      var list = [{id:'12345', sequence:null}, {id:'12346', sequence:null}, {id:'12347', sequence:null}, {id:'12348', sequence:1}, {id:'12348', sequence:2}, {id:'12349', sequence:1}, {id:'12349', sequence:1}];
    

    I am trying to get a unique list so that object with same id AND sequence will only return one of the objects (we have 2 here- {id:'12349', sequence:1})

    my code

      var uniqueList = _.uniq(list, function(obj) {
        return obj.id && obj.sequence;
      });
    

    can we use lodash uniq like this? what is the approach to fix this issue?