什么是摆脱JavaScript列表中重复项的最佳方法?

时间:2017-05-01 15:52:13

标签: javascript ecmascript-6

我写了几个方法来向数组添加项目,如果它们已经在数组中,它们将被忽略。在对数据结构进行一些研究之后,我意识到我可以通过简单地将它们放在一个集合中来摆脱重复(特别是因为我不关心对象的顺序)。但是,在使用JavaScript之后,我注意到相同的对象被添加到一个集合中,例如:

var mySet = new Set();
mySet.add({a:23})
console.log(mySet);
mySet.add({b:14})
console.log(mySet);
mySet.add({a:23})
console.log(mySet);

输出:

Set {Object {a: 23}}
Set {Object {a: 23}, Object {b: 14}}
Set {Object {a: 23}, Object {b: 14}, Object {a: 23}}

在这个例子中,我使用key-> a和value-> 23添加对象,然后使用完全不同的键,值集再添加另一个,然后再添加原始值。我希望在我的设置中只有a-> 23和b-> 14。

摆脱列表中重复项的最佳方法是什么?

2 个答案:

答案 0 :(得分:4)

这些对象虽然具有相同的结构,但却截然不同。每个都代表一个独立的内存空间,是对该内存的唯一引用。



console.log(
  { a: 23 } === { a: 23 } // false
);




这是你将同一个对象添加到集合两次的方法,第二次添加将被忽略。



var obj = { a: 23 };
var s = new Set();

// first addition
s.add(obj);
console.log(
  ...s.values()
);

// second addition of the same object is ignored
s.add(obj);
console.log(
  ...s.values()
);




如果您想支持基于它们包含的数据而不是引用相等性来比较对象的用例,那么您可能希望查看的内容是deep object comparison

例如,这是利用deep-equal库在添加对象之前检查对象是否存在的一种方法:



!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.deepEqual=e()}}(function(){return function e(t,r,n){function o(u,i){if(!r[u]){if(!t[u]){var c="function"==typeof require&&require;if(!i&&c)return c(u,!0);if(f)return f(u,!0);var p=new Error("Cannot find module '"+u+"'");throw p.code="MODULE_NOT_FOUND",p}var l=r[u]={exports:{}};t[u][0].call(l.exports,function(e){var r=t[u][1][e];return o(r?r:e)},l,l.exports,e,t,r,n)}return r[u].exports}for(var f="function"==typeof require&&require,u=0;u<n.length;u++)o(n[u]);return o}({1:[function(e,t,r){function n(e){return null===e||void 0===e}function o(e){return!(!e||"object"!=typeof e||"number"!=typeof e.length)&&("function"==typeof e.copy&&"function"==typeof e.slice&&!(e.length>0&&"number"!=typeof e[0]))}function f(e,t,r){var f,l;if(n(e)||n(t))return!1;if(e.prototype!==t.prototype)return!1;if(c(e))return!!c(t)&&(e=u.call(e),t=u.call(t),p(e,t,r));if(o(e)){if(!o(t))return!1;if(e.length!==t.length)return!1;for(f=0;f<e.length;f++)if(e[f]!==t[f])return!1;return!0}try{var s=i(e),a=i(t)}catch(e){return!1}if(s.length!=a.length)return!1;for(s.sort(),a.sort(),f=s.length-1;f>=0;f--)if(s[f]!=a[f])return!1;for(f=s.length-1;f>=0;f--)if(l=s[f],!p(e[l],t[l],r))return!1;return typeof e==typeof t}var u=Array.prototype.slice,i=e("./lib/keys.js"),c=e("./lib/is_arguments.js"),p=t.exports=function(e,t,r){return r||(r={}),e===t||(e instanceof Date&&t instanceof Date?e.getTime()===t.getTime():!e||!t||"object"!=typeof e&&"object"!=typeof t?r.strict?e===t:e==t:f(e,t,r))}},{"./lib/is_arguments.js":2,"./lib/keys.js":3}],2:[function(e,t,r){function n(e){return"[object Arguments]"==Object.prototype.toString.call(e)}function o(e){return e&&"object"==typeof e&&"number"==typeof e.length&&Object.prototype.hasOwnProperty.call(e,"callee")&&!Object.prototype.propertyIsEnumerable.call(e,"callee")||!1}var f="[object Arguments]"==function(){return Object.prototype.toString.call(arguments)}();r=t.exports=f?n:o,r.supported=n,r.unsupported=o},{}],3:[function(e,t,r){function n(e){var t=[];for(var r in e)t.push(r);return t}r=t.exports="function"==typeof Object.keys?Object.keys:n,r.shim=n},{}]},{},[1])(1)});

function addToSet(s, value) {
  for (const curr of s.values()) {
    if (deepEqual(curr, value)) {
      return;
    }
  }
  s.add(value);
}

var mySet = new Set();

// first addition works
addToSet(mySet, {a:23});
console.log(...mySet.values());

// second addition works
addToSet(mySet, {b:14});
console.log(...mySet.values());

// addition of an object of the same structure is ignored
addToSet(mySet, {a:23});
console.log(...mySet.values());
&#13;
&#13;
&#13;

在这种情况下,重要的考虑因素是deepEqual函数的时间/空间复杂性。利用这种方法,将元素添加到集合的时间复杂度从线性增加到至少二次,即O(setSize * numberOfPropsInLargestObject)。由于大多数深层对象比较方法的版本是以递归方式编写的,假设它不是现代环境中的尾递归函数,这也会将空间复杂性从常数增加到线性。

答案 1 :(得分:3)

正如其他人所说,对象并不相同,所以它们似乎是重复的。如果您可以指定一个键,我假设它是a,b,c等,那么您可以使用地图。它也是可迭代的。

var myMap = new Map();
myMap.set('a',23);
myMap.set('b',123);
myMap.set('a',33);

console.log(myMap)

映射{&#34; a&#34; =&GT; 33,&#34; b&#34; =&GT; 123}(2)