Create array of unique objects by property
I'd probably use a flags object during the filtering, like this:
var flags = {};
var newPlaces = places.filter(function(entry) {
if (flags[entry.city]) {
return false;
}
flags[entry.city] = true;
return true;
});
That uses Array#filter
from ECMAScript5 (ES5), which is one of the ES5 additions that can be shimmed (search for "es5 shim" for several options).
You can do it without filter
, of course, it's just a bit more verbose:
var flags = {};
var newPlaces = [];
var index;
for (index = 0; index < places.length; ++index) {
if (!flags[entry.city]) {
flags[entry.city] = true;
newPlaces.push(entry);
}
});
Both of the above assume the first object with a given city should be kept, and all other discarded.
Note: As user2736012 points out below, my test if (flags[entry.city])
will be true for cities with names that happen to be the same as properties that exist on Object.prototype
such as toString
. Very unlikely in this case, but there are four ways to avoid the possibility:
(My usual preferred solution) Create the object without a prototype:
var flags = Object.create(null);
. This is a feature of ES5. Note that this cannot be shimmed for obsolete browsers like IE8 (the single-argument version ofObject.create
can be except when that argument's value isnull
).Use
hasOwnProperty
for the test, e.g.if (flags.hasOwnProperty(entry.city))
-
Put a prefix on that you know doesn't exist for any
Object.prototype
property, such asxx
:var key = "xx" + entry.city; if (flags[key]) { // ... } flags[key] = true;
-
As of ES2015, you could use a
Set
instead:const flags = new Set(); const newPlaces = places.filter(entry => { if (flags.has(entry.city)) { return false; } flags.add(entry.city); return true; });
Shortest, but not best performance (see update bellow) solution for es6 :
function unique(array, propertyName) {
return array.filter((e, i) => array.findIndex(a => a[propertyName] === e[propertyName]) === i);
}
performance: https://jsperf.com/compare-unique-array-by-property
https://lodash.com/docs#uniqBy
https://github.com/lodash/lodash/blob/4.13.1/lodash.js#L7711
/**
* This method is like `_.uniq` except that it accepts `iteratee` which is
* invoked for each element in `array` to generate the criterion by which
* uniqueness is computed. The iteratee is invoked with one argument: (value).
*
* @static
* @memberOf _
* @since 4.0.0
* @category Array
* @param {Array} array The array to inspect.
* @param {Array|Function|Object|string} [iteratee=_.identity]
* The iteratee invoked per element.
* @returns {Array} Returns the new duplicate free array.
* @example
*
* _.uniqBy([2.1, 1.2, 2.3], Math.floor);
* // => [2.1, 1.2]
*
* // The `_.property` iteratee shorthand.
* _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
* // => [{ 'x': 1 }, { 'x': 2 }]
*/