用下划线移除 Javascript 的重复对象

我有这样一个数组:

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];

我想过滤一下:

var bar = [ { "a" : "1" }, { "b" : "2" }];

I tried using _.uniq, but I guess because { "a" : "1" } is not equal to itself, it doesn't work. Is there any way to provide underscore uniq with an overriden equals function?

139517 次浏览

Shiplu 答案的实现。

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];


var x = _.uniq( _.collect( foo, function( x ){
return JSON.stringify( x );
}));


console.log( x ); // returns [ { "a" : "1" }, { "b" : "2" } ]

. uniq/ . unique 接受回调

var list = [{a:1,b:5},{a:1,c:5},{a:2},{a:3},{a:4},{a:3},{a:2}];


var uniqueList = _.uniq(list, function(item, key, a) {
return item.a;
});


// uniqueList = [Object {a=1, b=5}, Object {a=2}, Object {a=3}, Object {a=4}]

备注:

  1. 用于比较的回调返回值
  2. 使用唯一返回值作为唯一返回值的第一个比较对象
  3. Underscorejs.org 显示没有回调用法
  4. lodash.com shows usage

Another example : 使用回调提取汽车制造,颜色从一个列表

如果你想根据 id 删除副本,你可以这样做:

var res = [
{id: 1, content: 'heeey'},
{id: 2, content: 'woah'},
{id: 1, content:'foo'},
{id: 1, content: 'heeey'},
];
var uniques = _.map(_.groupBy(res,function(doc){
return doc.id;
}),function(grouped){
return grouped[0];
});


//uniques
//[{id: 1, content: 'heeey'},{id: 2, content: 'woah'}]

试试迭代器函数

例如,可以返回第一个元素

x = [['a',1],['b',2],['a',1]]


_.uniq(x,false,function(i){


return i[0]   //'a','b'


})

= > [[‘ a’,1] ,[‘ b’,2]

下面是一个简单的解决方案,它使用深度对象比较来检查重复项(不需要转换为 JSON,这样做效率低下且不合适)

var newArr = _.filter(oldArr, function (element, index) {
// tests if the element has a duplicate in the rest of the array
for(index += 1; index < oldArr.length; index += 1) {
if (_.isEqual(element, oldArr[index])) {
return false;
}
}
return true;
});

如果后面的数组中有重复的元素,它会过滤掉所有的元素——这样就保留了最后一个重复的元素。

重复的测试使用 _.isEqual,它在两个对象之间执行优化的深度比较,更多信息见 下划线是相等的文档

编辑: 更新到使用 _.filter,这是一个清洁的方法

这是我的解决方案(咖啡脚本) :

_.mixin
deepUniq: (coll) ->
result = []
remove_first_el_duplicates = (coll2) ->


rest = _.rest(coll2)
first = _.first(coll2)
result.push first
equalsFirst = (el) -> _.isEqual(el,first)


newColl = _.reject rest, equalsFirst


unless _.isEmpty newColl
remove_first_el_duplicates newColl


remove_first_el_duplicates(coll)
result

例如:

_.deepUniq([ {a:1,b:12}, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ],[ 2, 1, 2, 1 ], {a:1,b:12} ])
//=> [ { a: 1, b: 12 }, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ] ]

当我有一个属性 id 时,这是我在下划线中的首选方式:

var x = [{i:2}, {i:2, x:42}, {i:4}, {i:3}];
_.chain(x).indexBy("i").values().value();
// > [{i:2, x:42}, {i:4}, {i:3}]

with 下划线 i had to use String () in the 接受者 function

function isUniq(item) {
return String(item.user);
}
var myUniqArray = _.uniq(myArray, isUniq);

Using underscore unique lib following is working for me, I m making list unique on the based of _id then returning String value of _id:

var uniqueEntities = _.uniq(entities, function (item, key, a) {
return item._id.toString();
});

You can do it in a shorthand as:

_.uniq(foo, 'a')

Lodash4.6.1文档将此作为对象键相等的示例:

_.uniqWith(objects, _.isEqual);

Https://lodash.com/docs#uniqwith

我想用一种直接的写法来解决这个简单的解决方案,但是有一点计算开销的痛苦... ... 但是这不是一个带有最小变量定义的微不足道的解决方案吗?

function uniq(ArrayObjects){
var out = []
ArrayObjects.map(obj => {
if(_.every(out, outobj => !_.isEqual(obj, outobj))) out.push(obj)
})
return out
}
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) {
return JSON.stringify(f);
}), function (gr) {
return gr[0];
}
);

Lets break this down. First lets group the array items by their stringified value

var grouped = _.groupBy(foo, function (f) {
return JSON.stringify(f);
});

grouped看起来像:

{
'{ "a" : "1" }' = [ { "a" : "1" } { "a" : "1" } ],
'{ "b" : "2" }' = [ { "b" : "2" } ]
}

Then lets grab the first element from each group

var bar = _.map(grouped, function(gr)
return gr[0];
});

bar looks like: [ { "a" : "1" }, { "b" : "2" } ]

把它们放在一起:

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) {
return JSON.stringify(f);
}), function (gr) {
return gr[0];
}
);