删除对象数组中的重复项 Javascript

Remove duplicates in an object array Javascript

我有一个对象数组

list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {x:1,y:2}]

我正在寻找一种有效的方法(如果可能 O(log(n)))来删除重复项并最终得到

list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}]

我试过 _.uniq 甚至 _.contains 但找不到令人满意的解决方案。

谢谢!

编辑:该问题已被识别为与另一个问题重复。我在发布之前看到了这个问题,但它没有回答我的问题,因为它是一个对象数组(而不是 2-dim 数组,谢谢 Aaron),或者至少另一个问题的解决方案在我的案例中不起作用。

以下将起作用:

var a = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {x:1,y:2}];

var b = _.uniq(a, function(v) { 
    return v.x && v.y;
})

console.log(b);  // [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 } ]

原版 JS 版本:

const list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {x:1,y:2}];

function dedupe(arr) {
  return arr.reduce(function(p, c) {

    // create an identifying id from the object values
    var id = [c.x, c.y].join('|');

    // if the id is not found in the temp array
    // add the object to the output array
    // and add the key to the temp array
    if (p.temp.indexOf(id) === -1) {
      p.out.push(c);
      p.temp.push(id);
    }
    return p;

    // return the deduped array
  }, {
    temp: [],
    out: []
  }).out;
}

console.log(dedupe(list));

在检查是否已经在 O(n) 的临时对象中后过滤数组。

var list = [{ x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 }, { x: 1, y: 2 }],
    filtered = function (array) {
        var o = {};
        return array.filter(function (a) {
            var k = a.x + '|' + a.y;
            if (!o[k]) {
                o[k] = true;
                return true;
            }
        });
    }(list);

document.write('<pre>' + JSON.stringify(filtered, 0, 4) + '</pre>');

普通 javascript (ES2015),使用 Set

const list = [{ x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 }, { x: 1, y: 2 }];

const uniq = new Set(list.map(e => JSON.stringify(e)));

const res = Array.from(uniq).map(e => JSON.parse(e));

document.write(JSON.stringify(res));

我会结合使用 Arrayr.prototype.reduceArrayr.prototype.some 方法以及扩展运算符。

1.显式解决方案。基于对数组对象包含的完整知识。

list = list.reduce((r, i) => 
  !r.some(j => i.x === j.x && i.y === j.y) ? [...r, i] : r
, [])

这里我们对比较对象的结构有严格的限制:{x: N, y: M}。而 [{x:1, y:2}, {x:1, y:2, z:3}] 将被过滤为 [{x:1, y:2}].

2。通用解决方案,JSON.stringify()。比较的对象可以有任意数量的任意属性。

list = list.reduce((r, i) => 
  !r.some(j => JSON.stringify(i) === JSON.stringify(j)) ? [...r, i] : r
, [])

此方法对属性顺序有限制,因此[{x:1, y:2}, {y:2, x:1}]不会被过滤。

3。通用解决方案,Object.keys()。顺序无关紧要。

list = list.reduce((r, i) => 
  !r.some(j => !Object.keys(i).some(k => i[k] !== j[k])) ? [...r, i] : r
, [])

这种方法还有另一个限制:比较的对象必须具有相同的键列表。 因此 [{x:1, y:2}, {x:1}] 将被过滤,尽管存在明显差异。

4。通用解决方案,Object.keys() + .length

list = list.reduce((r, i) => 
  !r.some(j => Object.keys(i).length === Object.keys(j).length 
    && !Object.keys(i).some(k => i[k] !== j[k])) ? [...r, i] : r
, [])

最后一种方法是通过键的数量、键本身和键值来比较对象。

我创建了一个 Plunker 来玩它。

尝试使用以下方法:

list = list.filter((elem, index, self) => self.findIndex(
    (t) => {return (t.x === elem.x && t.y === elem.y)}) === index)

一个 ES6+ 的内衬

如果你想通过 x 和 y 找到 uniq:

arr.filter((v,i,a)=>a.findIndex(t=>(t.x === v.x && t.y===v.y))===i)

如果您想按所有属性查找唯一值:

arr.filter((v,i,a)=>a.findIndex(t=>(JSON.stringify(t) === JSON.stringify(v)))===i)

使用 lodash 你可以使用这个单行代码:

 _.uniqBy(list, e => { return e.x && e.y })

无库,适用于任何深度

限制:

  • 您必须仅提供 stringNumber 属性作为哈希对象,否则您会得到不一致的结果
/** 
 * Implementation, you can convert this function to the prototype pattern to allow
 * usage like `myArray.unique(...)`
 */ 
function unique(array, f) {
  return Object.values(
    array.reduce((acc, item) => ({ ...acc, [f(item).join(``)]: item }), {})
  );
}

const list = [{ x: 1, y: 2}, {x: 3, y: 4}, { x: 5, y: 6}, { x: 1, y: 2}];

// Usage
const result = unique(list, item => [item.x, item.y]);

// Output: [{ x: 1, y: 2}, {x: 3, y: 4}, { x: 5, y: 6}]
console.log(result); 

片段样本

// Implementation
function unique(array, f) {
  return Object.values(
    array.reduce((acc, item) => ({ ...acc, [f(item).join(``)]: item }), {})
  );
}

// Your object list
const list = [{ x: 1, y: 2}, {x: 3, y: 4}, { x: 5, y: 6}, { x: 1, y: 2}];

// Usage
const result = unique(list, item => [item.x, item.y]);

// Add result to DOM
document.querySelector(`p`).textContent = JSON.stringify(result, null, 2);
<p></p>

使用 Underscore 的 _.uniq 和标准的 JSON.stringify 它是一个 oneliner:

var list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {x:1,y:2}];

var deduped = _.uniq(list, JSON.stringify);

console.log(deduped);
<script src="https://underscorejs.org/underscore-umd-min.js"></script>

但是,这假设键总是以相同的顺序指定。通过完善迭代器,即使键的顺序不同,我们也可以使解决方案有效。此问题以及解决方案也适用于涉及 JSON.stringify.

的其他答案

var list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {y:2, x:1}];

// Ensure that objects are always stringified
// with the keys in alphabetical order.
function replacer(key, value) {
    if (!_.isObject(value)) return value;
    var sortedKeys = _.keys(value).sort();
    return _.pick(value, sortedKeys);
}

// Create a modified JSON.stringify that always
// uses the above replacer.
var stringify = _.partial(JSON.stringify, _, replacer, null);

var deduped = _.uniq(list, stringify);

console.log(deduped);
<script src="https://underscorejs.org/underscore-umd-min.js"></script>

对于 Lodash 4,使用 _.uniqBy 而不是 _.uniq