按属性创建唯一对象数组

the*_*one 34 javascript jquery filter

我创建了一个像这样的对象数组:

var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";

places.push(a);

var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";

places.push(b);

...
Run Code Online (Sandbox Code Playgroud)

我正在尝试创建一个新的数组,过滤地点只包含不具有相同城市属性的对象(lat/lng重复项是可以的).是否有内置的JS或Jquery函数来实现这一目标?

T.J*_*der 47

我可能在过滤期间使用了flags对象,如下所示:

var flags = {};
var newPlaces = places.filter(function(entry) {
    if (flags[entry.city]) {
        return false;
    }
    flags[entry.city] = true;
    return true;
});
Run Code Online (Sandbox Code Playgroud)

它使用Array#filter来自ECMAScript5(ES5),这是可以填充的ES5添加之一(为几个选项搜索"es5 shim").

你可以做到这一点filter,当然,它只是更冗长:

var flags = {};
var newPlaces = [];
var index;
for (index = 0; index < places.length; ++index) {
    if (!flags[entry.city]) {
        flags[entry.city] = true;
        newPlaces.push(entry);
    }
});
Run Code Online (Sandbox Code Playgroud)

以上两者都假定应保留给定城市的第一个对象,并丢弃所有其他对象.


注意:user2736012低于所指出的,我的测试if (flags[entry.city])将是真正的与该恰巧是一样上存在的所有属性名的城市Object.prototypetoString.在这种情况下非常不可能,但有四种方法可以避免这种可能性:

  • (我通常喜欢的解决方案)创建没有原型的对象:var flags = Object.create(null);.这是ES5的一个功能.请注意,对于像IE8这样的过时浏览器(单参数版本Object.create可以是除了该参数的值之外),这不能被填充null.

  • 使用hasOwnProperty的测试,例如,if (flags.hasOwnProperty(entry.city))

  • 在您知道不存在任何Object.prototype属性的前缀上添加前缀,例如xx:

    var key = "xx" + entry.city;
    if (flags[key]) {
        // ...
    }
    flags[key] = true;
    
    Run Code Online (Sandbox Code Playgroud)
  • 从ES2015开始,您可以使用Set:

    const flags = new Set();
    const newPlaces = places.filter(entry => {
        if (flags.has(entry.city)) {
            return false;
        }
        flags.add(entry.city);
        return true;
    });
    
    Run Code Online (Sandbox Code Playgroud)

  • 是的,这有点偏执.我承认,当然对于这种情况.它确实使它成为更安全的通用解决方案. (2认同)

Igo*_*orL 31

es6的最短但不是最佳性能(参见下面的更新)解决方案:

function unique(array, propertyName) {
   return array.filter((e, i) => array.findIndex(a => a[propertyName] === e[propertyName]) === i);
}
Run Code Online (Sandbox Code Playgroud)

性能:https://jsperf.com/compare-unique-array-by-property

  • 你真是个天才! (3认同)
  • @MANaseer 抱歉,现在知道为什么链接被删除,无法恢复。 (2认同)

hev*_*ev1 11

您可以通过仅包含具有尚未添加到 的属性值的元素来filter使用a (之后应将其添加到 )。这可以使用逻辑与运算符 ( ) 在一行中完成。使用这种数据结构具有亚线性查找时间(通常)的优点。SetSetSet&&O(1)

prop下面是一个通用函数,用于根据对象数组 ( ) 中的特定属性 ( ) 获取唯一的对象数组arr。请注意,如果存在重复项,则仅保留第一个具有属性值的对象。

const getUniqueBy = (arr, prop) => {
  const set = new Set;
  return arr.filter(o => !set.has(o[prop]) && set.add(o[prop]));
};
Run Code Online (Sandbox Code Playgroud)

演示:

const getUniqueBy = (arr, prop) => {
  const set = new Set;
  return arr.filter(o => !set.has(o[prop]) && set.add(o[prop]));
};
Run Code Online (Sandbox Code Playgroud)


小智 5

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 }]
 */
Run Code Online (Sandbox Code Playgroud)