Filter array of objects for unique items
Given an array of objects:
var arr = [
{a: "foo", b: "bar", c: "baz" },
{a: "foo", b: "bar", c: "qux" },
{a: "foo", b: "bar", c: "baz" },
{a: "foo", b: "bar", c: "qux" },
{a: "bar", b: "foo", c: "qux" },
{a: "bar", b: "qux", c: "foo" },
{a: "bar", b: "foo", c: "qux" }
];
The array must be filtered, so only unique objects remain:
var arr = [
{a: "foo", b: "bar", c: "baz" },
{a: "foo", b: "bar", c: "qux" },
{a: "bar", b: "foo", c: "qux" },
{a: "bar", b: "qux", c: "foo" }
];
For arrays of simple strings, I use
arr.filter(function (value, index, self) {
return self.indexOf(value) === index;
}
but that doesn't work with objects. All properties should be mapped in my case. Think you need some kind of deep comparison?
source to share
If your objects have the same keys, you can use the lodash function or underscore _.findWhere
:
Performs a deep comparison between each item in the collection and the original object, returning the first item with equivalent property values.
var arr = [
{a: "foo", b: "bar", c: "baz" },
{a: "foo", b: "bar", c: "qux" },
{a: "foo", b: "bar", c: "baz" },
{a: "foo", b: "bar", c: "qux" },
{a: "bar", b: "foo", c: "qux" },
{a: "bar", b: "qux", c: "foo" },
{a: "bar", b: "foo", c: "qux" }
];
function uniqueObjects(arr) {
var u = [];
arr.forEach(function(obj) {
if (!_.findWhere(u, obj)) {
u.push(obj);
}
});
return u
}
document.getElementById('result').innerHTML = JSON.stringify(uniqueObjects(arr),null,2);
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.7.0/lodash.min.js"></script>
<pre id='result'></pre>
If not, a deep comparison can be made using _.matches
both directions:
var arr = [
{a: "foo", b: "bar", c: "baz" },
{a: "foo", b: "bar", c: "qux" },
{a: "foo", b: "bar", c: "baz" },
{a: "foo", b: "bar", c: "qux" },
{a: "bar", b: "foo", c: "qux" },
{a: "bar", b: "qux", c: "foo" },
{a: "bar", b: "foo", c: "qux" },
// Missing property
{a: "foo", b: "bar" },
// Ordering
{a: "foo", c: "qux" },
{c: "qux", a: "foo" }
];
function uniqueObjects(arr) {
var u = [];
arr.forEach(function(obj) {
if (!u.filter(deepCompare(obj)).length) {
u.push(obj);
}
});
return u;
}
function deepCompare(obj) {
return function(source) {
return _.matches(obj)(source) && _.matches(source)(obj);
};
}
document.getElementById('result').innerHTML = JSON.stringify(uniqueObjects(arr),null,2);
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.7.0/lodash.min.js"></script>
<pre id='result'></pre>
source to share
I used the approach I mentioned in my comment.
var arr = [
{c: "baz", a: "foo", b: "bar" },
{c: "qux", a: "foo", b: "bar" },
{c: "baz", b: "bar", a: "foo" },
{c: "qux", b: "bar", a: "foo" },
{a: "bar", c: "qux", b: "foo" },
{a: "bar", c: "foo", b: "qux" },
{a: "bar", b: "foo", c: "qux" }
];
var modified = arr.map(function (value) {
var result = [];
var keys = Object.keys(value).sort();
for (var i = 0; i < keys.length; i++) {
result.push(value[keys[i]]);
}
return result.join('');
});
for (var i = (modified.length - 1); i >= 0; i--) {
if (modified.indexOf(modified[i]) !== i) {
arr.splice(i, 1);
}
}
alert(JSON.stringify(arr));
source to share
Use lodash! https://lodash.com/docs#uniq
Creates a duplicate version of the array using SameValueZero for equality comparison, which stores only the first occurrence of each element. Providing true to isSorted performs a faster search algorithm for sorted arrays. If an iterative function is provided, it is called for each element in the array to generate a criterion against which uniqueness is calculated. The iterator is bound to thisArg and is called with three arguments: (value, index, array).
Include it in your HTML
<script src="bower_components/lodash/lodash.js"></script>
Then in your controller.
var filteredArray = _.uniq(arr, function(item, key, a) {
return item.a;
});
source to share