0

I need a function that takes in an array and will return an array with all the duplicates. I would prefer to use underscore if possible.

given the array:

[
    "apple",
    "apple",
    "pear",
    "pear",
    "kiwi",
    "peach"
]

I need to return an array

[
    "apple",
    "pear"
]

Many of the methods I've found will return a boolean and not an array of the duplicates.

For example

var fruits = ["apple","apple"];
var uniq_fruits = _.uniq(fruits);
var duplicates_exist = (fruits.length == uniq_fruits.length);
4
  • How would you do this if you didn't have a computer? Commented Aug 16, 2013 at 18:35
  • @Pointy conveyor belt and color recognition? Commented Aug 16, 2013 at 18:37
  • So if I wrote down a list of fruit names on a piece of paper, you'd need a conveyor belt to find the duplicates? Isn't there a simpler way to handle a list of names of fruits? Like, is there some way you could keep track of each name you've seen? Commented Aug 16, 2013 at 18:42
  • @Pointy Not sure where you're going with this. I do appreciate your time, effort, and patience though. I don't get to talk to many coders and I don't like to reinvent the wheel, I was assuming that there was a easy way to get this done with underscore and possibly without .sort(). Commented Aug 16, 2013 at 19:09

7 Answers 7

5

You could use _.countBy to get the word frequencies and then use _.reduce to collect up the values with a frequency greater than one:

function collect_dups(a, n, word) {
    if(n > 1)
        a.push(word);
    return a;
}
var dups = _(words).chain()
                   .countBy()
                   .reduce(collect_dups, [])
                   .value();

Demo: http://jsfiddle.net/ambiguous/gKmfh/1/

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

1 Comment

This is obviously the real idiomatic Underscore answer :)
3

Turn your list into a map, then turn the map into a list.

var fruits = ["apple", // ... ];

function fruitCounter(countMap, fruit) {
  if (countMap[fruit] == null)
    countMap[fruit] = 1;
  else
    countMap[fruit]++;
  return countMap;
}

function dupFinder(dupList, count, fruit) {
  if (count > 1)
    dupList.push(fruit);
  return dupList;
}

var dups = _.reduce(_.reduce(fruits, fruitCounter, {}), dupFinder, []);

It's sort-of unfortunate that there's nothing really like "filter" for the properties of an object, but it's not too bad with "reduce".

edit — a comment from someone better at Underscore than me points out that the inner "reduce" could be replaced by a simpler "countBy":

var dups = _.reduce(_.countBy(fruits, function(f) { return f; }), dupFinder, []);

5 Comments

You can use _.countBy instead of the inner _.reduce.
@muistooshort ah OK; I barely know Underscore :)
You can also replace "function(f) { return f; }" with _.identity
@ne8il oh cool. I've never done much serious work with Underscore, but I've used Functional and it has a similar "I" identity function.
You can also use _.chain to avoid the nested _.X calls (see below).
1
var common = function(array){

    var tally = function(array){
        var make_object = {};
        _.each(array, function(item){
            make_object[item] = (typeof make_object[item] == "undefined") ? 1 : make_object[item] + 1;
        });
        return make_object;        
    }(array);

    var duplicates = function(obj){
        var duplicates = [];
        _.each(obj, function(value, key){
            if(value > 1){
                duplicates.push(key);
            }
        });
        return duplicates;
    }(tally);

    return duplicates;

};

Comments

1

The idea is very straight forward. Group the items by its value and then find which group having more than 1 items. Finally pick only one item from each group.

lst = [ "apple", "apple", "pear", "pear", "kiwi", "peach"];
var result = _.chain(lst)
    .groupBy(function (i) { return i; })
    .filter(function (v, k) { return v.length > 1; })
    .map(function(v){return v[0]; })
    .value();

>>["apple", "pear"] 

Comments

1

where arr is your input, you just check to see if the element is a key on the obj object - if it is, pass it to the output array and reloop, otherwise add the key to the object:

function findDupes(arr) {
  var obj = {}, newArr = [];
  for (var i = 0, l = arr.length; i < l; i++) {
    if (obj[arr[i]]) { newArr.push(arr[i]); continue; }
    obj[arr[i]] = true;
  }
  return newArr;
}

var dupes = findDupes(arr);

2 Comments

For what it's worth, Thomas, what @Pointy was talking about, and he and I demonstrated, is that there's one great thing about Javascript objects - their keys are unique. This means you can assign things to them and do a simple test to see if they exist. Unlike Pointy who converted the whole array to an object (map) first, you can do it on the fly as I described above, FWIW, while I like underscore for a lot of heavy-lifting, it's much easier this way IMO.
This will push the same item multiple times to the result if duplicated more than twice.
0

Giving you have a simple one level array of strings, I would suggest to sort an array first and then loop through it trying to compare current item with the next one. Something like this:

var fruit = [
    "apple",
    "apple",
    "apple",
    "pear",
    "pear",
    "cantalope"
];

var common = function(array){
    var mySortedArray = array.sort();
    var myResultArray = [];

    for (var i = 0; i < mySortedArray.length - 1; i++)
      if ( (mySortedArray[i + 1] == mySortedArray[i]) && 
        (mySortedArray[i] != myResultArray[myResultArray.length-1]) )
        myResultArray.push(mySortedArray[i]);

    return myResultArray;
};

alert(common(fruit));

2 Comments

This doesn't work if there is three or more the same things in the array here's my example gist.github.com/reggi/c619ecdab5e9b6746bc8
I modified you common function for it to start working, see above
0

I started from this function : https://stackoverflow.com/a/840849/1636522

function getDuplicates(arr) {
    var i,
        len = arr.length,
        out = [],
        obj = {};
    for (i = 0; i < len; i++) {
        switch (obj[arr[i]]) {
            case undefined: obj[arr[i]] = 1; break;
            case 1: obj[arr[i]] = 2; out.push(arr[i]); break;
        }
    }
    return out;
}

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.