58

我创建了一个对象数组,如下所示:

[
    {
        "lat": 12.123,
        "lng": 13.213,
        "city": "New York"
    },
    {
        "lat": 3.123,
        "lng": 2.213,
        "city": "New York"
    },
    {
        "lat": 1.513,
        "lng": 1.113,
        "city": "London"
    }
]

我正在尝试创建一个新数组,将其过滤places为仅包含不具有相同city属性的对象(lat/lng 重复是可以的)。是否有内置的 JS 或 Jquery 函数来实现这一点?

4

17 回答 17

65

我可能会在过滤期间使用标志对象,如下所示:

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

这使用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);
    }
});

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


注意:正如 user2736012 在下面指出的那样,我的测试if (flags[entry.city])对于名称恰好与Object.prototype诸如toString. 在这种情况下不太可能发生,但有四种方法可以避免这种可能性:

  • (我通常首选的解决方案)创建没有原型的对象: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;
    
  • 从 ES2015 开始,您可以使用 aSet代替:

    const flags = new Set();
    const newPlaces = places.filter(entry => {
        if (flags.has(entry.city)) {
            return false;
        }
        flags.add(entry.city);
        return true;
    });
    
于 2013-09-12T20:34:28.067 回答
55

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

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

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

于 2017-09-14T12:49:04.013 回答
6

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 }]
 */
于 2016-07-20T04:27:01.263 回答
4

我对@IgorL 解决方案进行了一些扩展,但扩展了原型并为其提供了选择器函数而不是属性,以使其更加灵活:

Array.prototype.unique = function(selector) {
   return this.filter((e, i) => this.findIndex((a) => {
      if (selector) {
        return selector(a) === selector(e);
      }
      return a === e;
    }) === i);
};

用法:

// with no param it uses strict equals (===) against the object
let primArr = ['one','one','two','three','one']
primArr.unique() // ['one','two','three']

let a = {foo:123}
let b = {foo:123}
let fooArr = [a,a,b]
fooArr.unique() //[a,b]

// alternatively, you can pass a selector function
fooArr.unique(item=>item.foo) //[{foo:123}] (first "unique" item returned)

绝对不是最高效的方法,但只要选择器很简单并且数组不是很大,它应该可以正常工作。

在打字稿中

Array.prototype.unique = function<T>(this: T[], selector?: (item: T) => object): T[] {
   return this.filter((e, i) => this.findIndex((a) => {
      if (selector) {
        return selector(a) === selector(e);
      }
      return a === e;
    }) === i);
};
于 2017-11-13T05:47:29.913 回答
4

您可以通过仅包含具有尚未添加到的属性值的元素来filter使用 a (之后应该将其添加到)。这可以使用逻辑与运算符 ( )在一行中完成。SetSetSet&&

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

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

演示:

var places = [{
  lat: 12.123,
  lng: 13.213,
  city: 'New York'
}, {
  lat: 3.123,
  lng: 2.213,
  city: 'New York'
}, {
  lat: 3.123,
  lng: 4.123,
  city: 'Some City'
}];
const getUniqueBy = (arr, prop) => {
  const set = new Set;
  return arr.filter(o => !set.has(o[prop]) && set.add(o[prop]));
};
console.log(getUniqueBy(places, 'city'));

于 2020-08-14T19:28:40.653 回答
3

我的建议 :

Array.prototype.uniqueCity = function() {
    var processed = [];
    for (var i=this.length-1; i>=0; i--){
        if (processed.indexOf(this[i].city)<0) {
            processed.push(this[i].city);
        } else {
            this.splice(i, 1);
        }
    }
}

正在使用 :

places.uniqueCity();

或者

Array.prototype.uniqueObjectArray = function(field) {
    var processed = [];
    for (var i=this.length-1; i>=0; i--) {
        if (this[i].hasOwnProperty(field)) {
            if (processed.indexOf(this[i][field])<0) {
                processed.push(this[i][field]);
            } else {
                this.splice(i, 1);
            }
        }
    }
}

places.uniqueObjectArray('city');

使用上述内容,您可以按对象中的任何字段对数组进行排序,即使某些对象不存在这些字段。

或者

function uniqueCity(array) {
    var processed = [];
    for (var i=array.length-1; i>=0; i--){
        if (processed.indexOf(array[i].city)<0) {
            processed.push(array[i].city);
        } else {
            array.splice(i, 1);
        }
    }
    return array;
}

places = uniqueCity(places);
于 2013-09-12T20:45:20.397 回答
3

您可以使用 Map 因此具有相同键属性的条目(在您的情况下为“城市”)仅出现一次

module.exports = (array, prop) => {
   const keyValueArray = array.map(entry => [entry[prop], entry]);
   const map = new Map(keyValueArray);
   return Array.from(map.values());
};

有关 Map 和数组对象的更多信息在这里

Codepen 的基本示例

于 2018-03-11T16:19:27.877 回答
3

另外一个选项:

const uniqueBy = prop => list => {
    const uniques = {}
    return list.reduce(
        (result, item) => {
            if (uniques[item[prop]]) return result
            uniques[item[prop]] = item
            return [...result, item]
        },
        [],
    )
}

const uniqueById = uniqueBy('id')

uniqueById([
    { id: 1, name: 'one' },
    { id: 2, name: 'two' },
    { id: 1, name: 'one' },
    { id: 3, name: 'three' }
])

您可以将其粘贴到控制台上以查看它是否正常工作。它应该适用于呈现的场景和其他一些场景。

于 2018-11-29T20:02:41.950 回答
1

正如评论中指出的那样,您可以将对象用作地图,这将允许您避免重复,然后您可以枚举对象的属性。

工作小提琴:http: //jsfiddle.net/gPRPQ/1/

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);

var unique = {}

for (var i = 0; i < places.length; i++) {
    var place = places[i];
    unique[place.city] = place;
}

for (var name in unique) {
    var place = unique[name];
    console.log(place);
}
于 2013-09-12T20:39:58.607 回答
1
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);

getUniqAR(places,'city'); //Return Uniq Array by property

function getUniqAR(Data,filter){
var uniar =[];
Data.forEach(function(item,ind,arr){
    var dupi=false;
    if(!uniar.length) uniar.push(item) //push first obj into uniq array 
    uniar.forEach(function(item2, ind2,arr){
    if(item2[filter] == item[filter]){  //check each obj prop of uniq array 
      dupi=true; //if values are same put duplicate is true
        }     
    })
if(!dupi){  uniar.push(item)} //if no duplicate insert to uniq

})
console.log(uniar)
return uniar;
}
于 2017-05-19T14:46:53.123 回答
1

我们可以使用 JavaScript Map通过任何属性创建唯一对象列表。

例如 :

var places = [{ 'lat': 12.123, 'lng': 13.213, 'city': "New York"},
                { 'lat': 3.123, 'lng': 2.213, 'city': "New York"},
                { 'lat': 43.123, 'lng': 12.213, 'city': "London"}];
                
var cityMap = new Map();
places.forEach(p=> cityMap.set(p.city, p));

console.log([...cityMap.values()]);

执行代码片段以查看结果。

于 2020-05-12T08:29:05.987 回答
0

从数组列表中Javascript删除重复城市的简单代码是places

var places = [{ 'lat': 12.123, 'lng': 13.213, 'city': "New York"},
                { 'lat': 3.123, 'lng': 2.213, 'city': "New York"},
                { 'lat': 43.123, 'lng': 12.213, 'city': "London"}];
var unique = [];
var tempArr = [];
places.forEach((value, index) => {
    if (unique.indexOf(value.city) === -1) {
        unique.push(value.city);
    } else {
        tempArr.push(index);    
    }
});
tempArr.reverse();
tempArr.forEach(ele => {
    places.splice(ele, 1);
});
console.log(places);
于 2019-05-10T10:24:21.363 回答
0

基于以上https://stackoverflow.com/a/18773857/49564的通用 Typescript 答案:

export function isDistinct<T>(mapper: (value: T) => string): (value: T) => boolean {
  const keys: { [index: string]: boolean } = {};

  return (entry: T) => {
    const key = mapper(entry);

    if (keys[key] !== undefined) {
      return false;
    }

    return keys[key] = true;
  };
}

// Usage example:
const items = [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 1 } ];
const unique = items.filter(isDistinct(i => i.id));
于 2019-12-11T13:41:10.453 回答
0

我想你想要这个,

注意: 不需要库。

let array = [{ id: 1}, {id: 2}, {id: 3}];

function addUniqeObj(data) {
  let index = -1;

  for(let i = 0, i < array.length; i++) {
    if(array[i].id === data.id) {
      index = i;
    }
  }

  if(index > -1) {
    array[index] = data;
  } else {
    array.push(data)
  }

}
于 2020-04-14T16:27:47.077 回答
0

rafaelbiten方法的另一种变体:

const dedupExample = [
    {id: 1, c: 'whatever'},
    {id: 1, c: '1whatever'},
    {id: 2, c: '2whatever'},
    {id: 2, c: '2whatever'},
    {id: 3, c: '2whatever'},
]

const getUniqueBy = (prop, list) => {
    const objUniq = list.reduce((res, item) => ({ ...res, [item[prop]]: item }), {})
    return Object.keys(objUniq).map(item => objUniq[item])
}

const uniq = getUniqueBy('id', dedupExample)

console.info('info', { uniq })
   /* [
    {id: 1, c: 'whatever'},
    {id: 2, c: '2whatever'},
    {id: 3, c: '2whatever'},
  ] */

于 2020-07-24T00:45:54.607 回答
0
const distinctArrayByCity= [
    ...new Map(array.map((item) => [item.city, item])).values(),
];
于 2022-02-18T10:09:13.210 回答
-1

这个线程可能很旧,但我认为我应该分享它。它基于纯 JavaScript,并根据指定的属性删除重复对象。

function removeDuplicates(originalArray, properties) {
  var newArray = [];
  var index = 0;
  var lookupObject = {};
  var totalProperties = properties.length;

  for (var i = 0; i < originalArray.length; i++) {
    var exists = false;

    for (var a = 0; a < newArray.length; a++) {
      var propsFound = 0;
      for (var b = 0; b < totalProperties; b++) {
        if (originalArray[i][properties[b]] == newArray[a][properties[b]]) {
          propsFound++;
        }
      }

      //If there is a match then break the for loop
      if (propsFound == totalProperties) {
        exists = true;
        break;
      }
    } //End of New Array

    if (!exists) {
      newArray[index] = originalArray[i];
      index++;
    }
  } //End of originalArray

  return newArray;
}

你可以在这里查看小提琴

于 2016-12-28T19:10:34.240 回答