From the underscore.js documentation:
uniq _.uniq(array, [isSorted], [iteratee])
Alias: unique
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 iteratee
function.
But arrays can't be strictly compared in JavaScript.
Therefore, you can use a transformation function to enable comparison with uniq
. For example:
console.log([1,2] === [1,2]) // false, can't strict compare arrays
console.log([1,2].toString()) // "1,2" - string representation
console.log([1,2].toString() === [1,2].toString()) // true, strings can be compared
var valueToString = function(v) {return v.toString()}; // transform array to string
var arr1 = [[1,2], [2,3], [1,2]];
var arr2 = _.uniq(arr1, false, valueToString); // compare based on transformation
var arraysAreEqual = _.isEqual(arr1, arr2);
console.log("arraysAreEqual:", arraysAreEqual, arr1, arr2);
// false
// [[1, 2], [2, 3], [1, 2]]
// [[1, 2], [2, 3]]
Note that transforming to string is "hacky": you would be better off comparing each value of the array, as discussed in this StackOverflow question.
By using the proposed equals
implementation in that question, you would need to implement your own version of uniq
that uses equals
instead of ===
.
The implementation of uniq in Underscore is very straight-forward - it creates a new result
array and loops through the given array. If the current value is not already in result, insert it.
console.log("Using array comparison:");
arrayEquals = function (array1, array2) {
// if any array is a falsy value, return
if (!array1 || !array2)
return false;
// compare lengths - can save a lot of time
if (array1.length != array2.length)
return false;
for (var i = 0, l=array1.length; i < l; i++) {
// Check if we have nested arrays
if (array1[i] instanceof Array && array2[i] instanceof Array) {
// recurse into the nested arrays
if (!arrayEquals(array1[i],array2[i]))
return false;
}
else if (array1[i] !== array2[i]) {
return false;
}
}
return true;
};
_.uniqArrays = function(array) {
if (array == null) return [];
var result = [];
for (var i = 0, length = array.length; i < length; i++) {
var value = array[i];
var arrayEqualsToValue = arrayEquals.bind(this, value); // arrayEquals with first argument set to value
var existing = _.find(result, arrayEqualsToValue); // did we already find this?
if (!existing) {
result.push(value);
}
}
return result;
};
var arr3 = _.uniqArrays(arr1);
arraysAreEqual = _.isEqual(arr1, arr3);
console.log("arraysAreEqual:", arraysAreEqual, arr1, arr3); // false
I made a jsbin with all the code, if you want to play around.