41

Say I have an array like this: [1, 1, 2, 2, 3]

I want to get the duplicates which are in this case: [1, 2]

Does lodash support this? I want to do it in the shortest way possible.

2

12 Answers 12

57

You can use this:

_.filter(arr, (val, i, iteratee) => _.includes(iteratee, val, i + 1))

or without using lodash, you can use plain JavaScript:

arr.filter((val, i) => arr.includes(val, i + 1))

Note: that if a number appears more than two times in your array you can always use _.uniq.

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

5 Comments

One-liner, with some ES2015 sugar: const duplicates = _.filter(array, (value, index, iteratee) => _.includes(iteratee, value, index + 1))
It works with objects and more complex structures if you use _.find instead of _.includes _.filter(array, (value, index, iteratee) => { return _.find(iteratee, value, index + 1) })
Could you add an explanation of what's going on for those who are not as familiar with iteratee?
@ahong iteratee===array this is equivalent to a nested loop for(;;){ for(;;){
@Vitim.us I understand nested loop, but what does iteratee===array mean?
36

Another way is to group by unique items, and return the group keys that have more than 1 item

_([1, 1, 2, 2, 3]).groupBy().pickBy(x => x.length > 1).keys().value()

Comments

19
var array = [1, 1, 2, 2, 3];
var groupped = _.groupBy(array, function (n) {return n});
var result = _.uniq(_.flatten(_.filter(groupped, function (n) {return n.length > 1})));

This works for unsorted arrays as well.

1 Comment

This seems to be significantly faster for larger arrays than the accepted answer is. Nice work.
7

Another way, but using filters and ecmaScript 2015 (ES6)

var array = [1, 1, 2, 2, 3];

_.filter(array, v => 
  _.filter(array, v1 => v1 === v).length > 1);

//→ [1, 1, 2, 2]

2 Comments

I like, very simple! The trick is that it identifies if more than one exists on the sub-filter.
yes, note the output. Using this method if you input [1,1,1] it will return [1,1,1] Which is sometimes what you want. And sometimes not what you want :-)
6

How about using countBy() followed by reduce()?

const items = [1,1,2,3,3,3,4,5,6,7,7];

const dup = _(items)
    .countBy()
    .reduce((acc, val, key) => val > 1 ? acc.concat(key) : acc, [])
    .map(_.toNumber)

console.log(dup);
// [1, 3, 7]

http://jsbin.com/panama/edit?js,console

Comments

6

Pure JS solution:

export function hasDuplicates(array) {
  return new Set(array).size !== array.length
}

For an array of objects:

/**
 * Detects whether an array has duplicated objects.
 * 
 * @param array
 * @param key
 */
export const hasDuplicatedObjects = <T>(array: T[], key: keyof T): boolean => {
  const _array = array.map((element: T) => element[key]);

  return new Set(_array).size !== _array.length;
};

1 Comment

Question is asking to get duplicates, not testing for duplicates. That's in a linked question.
3

Here is another concise solution:

let data = [1, 1, 2, 2, 3]

let result = _.uniq(_.filter(data, (v, i, a) => a.indexOf(v) !== i))

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

_.uniq takes care of the dubs which _.filter comes back with.

Same with ES6 and Set:

let data = [1, 1, 2, 2, 3]

let result = new Set(data.filter((v, i, a) => a.indexOf(v) !== i))

console.log(Array.from(result))

Comments

1

Well you can use this piece of code which is much faster as it has a complexity of O(n) and this doesn't use Lodash.

[1, 1, 2, 2, 3]
.reduce((agg,col) => {
  agg.filter[col] = agg.filter[col]? agg.dup.push(col): 2;
  return agg
 },
 {filter:{},dup:[]})
.dup;

//result:[1,2]

Comments

1

here is mine, es6-like, deps-free, answer. with filter instead of reducer

// this checks if elements of one list contains elements of second list 
// example code
[0,1,2,3,8,9].filter(item => [3,4,5,6,7].indexOf(item) > -1)

// function
const contains = (listA, listB) => listA.filter(item => listB.indexOf(item) > -1) 
contains([0,1,2,3], [1,2,3,4]) // => [1, 2, 3]

// only for bool
const hasDuplicates = (listA, listB) => !!contains(listA, listB).length

edit: hmm my bad is: I've read q as general question but this is strictly for lodash, however my point is - you don't need lodash in here :)

Comments

1

You can make use of a counter object. This will have each number as key and total number of occurrence as their value. You can use filter to get the numbers when the counter for the number becomes 2

const array = [1, 1, 2, 2, 3],
      counter = {};
      
const duplicates = array.filter(n => (counter[n] = counter[n] + 1 || 1) === 2)

console.log(duplicates)

Comments

0

Hope below solution helps you and it will be useful in all conditions

  hasDataExist(listObj, key, value): boolean {
    return _.find(listObj, function(o) { return _.get(o, key) == value }) != undefined;
  }



  let duplcateIndex = this.service.hasDataExist(this.list, 'xyz', value);

Comments

0

No need to use lodash, you can use following code:

function getDuplicates(array, key) {
  return array.filter(e1=>{
    if(array.filter(e2=>{
      return e1[key] === e2[key];
    }).length > 1) {
      return e1;
    }
  })
}

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.