Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用Javascript数组计算集合差异的最快或最优雅的方法是什么?_Javascript_Arrays_Set Difference - Fatal编程技术网

使用Javascript数组计算集合差异的最快或最优雅的方法是什么?

使用Javascript数组计算集合差异的最快或最优雅的方法是什么?,javascript,arrays,set-difference,Javascript,Arrays,Set Difference,设A和B为两组。我正在寻找非常快速或优雅的方法来计算它们之间的集差(A-B或A\B,取决于您的偏好)。正如标题所说,这两个集合作为Javascript数组存储和操作 注: 壁虎特有的把戏没问题 我更愿意坚持使用本机函数(但如果轻量级库更快的话,我愿意使用它) 我已经看过了,但还没有测试过(见上一点) 编辑:我注意到一条关于包含重复元素的集合的评论。当我说“set”时,我指的是数学定义,这意味着(除其他外)它们不包含重复的元素。如果不知道这是否是最有效的,但可能是最短的 A = [1, 2,

A
B
为两组。我正在寻找非常快速或优雅的方法来计算它们之间的集差(
A-B
A\B
,取决于您的偏好)。正如标题所说,这两个集合作为Javascript数组存储和操作

注:

  • 壁虎特有的把戏没问题
  • 我更愿意坚持使用本机函数(但如果轻量级库更快的话,我愿意使用它)
  • 我已经看过了,但还没有测试过(见上一点)

编辑:我注意到一条关于包含重复元素的集合的评论。当我说“set”时,我指的是数学定义,这意味着(除其他外)它们不包含重复的元素。

如果不知道这是否是最有效的,但可能是最短的

A = [1, 2, 3, 4];
B = [1, 3, 4, 7];

diff = A.filter(function(x) { return B.indexOf(x) < 0 })

console.log(diff);

这是可行的,但我认为另一个要短得多,也很优雅

A = [1, 'a', 'b', 12];
B = ['a', 3, 4, 'b'];

diff_set = {
    ar : {},
    diff : Array(),
    remove_set : function(a) { ar = a; return this; },
    remove: function (el) {
        if(ar.indexOf(el)<0) this.diff.push(el);
    }
}

A.forEach(diff_set.remove_set(B).remove,diff_set);
C = diff_set.diff;
A=[1',A',b',12];
B=['a',3,4,'B'];
差异集={
ar:{},
diff:Array(),
remove_set:function(a){ar=a;返回this;},
移除:功能(el){

如果(ar.indexOf(el),您可以使用对象作为贴图,以避免对
a
的每个元素进行线性扫描,如:


非标准用于获取唯一的属性名称;如果所有元素都已具有唯一的字符串表示形式(如数字),则可以通过删除
toSource()来加快代码速度
调用。

结合Christoph的思想,假设数组和对象/散列(
每个
和朋友)上有两个非标准迭代方法,我们可以在线性时间内得到集合差、并集和交集,总共大约20行:

var setOPs = {
  minusAB : function (a, b) {
    var h = {};
    b.each(function (v) { h[v] = true; });
    return a.filter(function (v) { return !h.hasOwnProperty(v); });
  },
  unionAB : function (a, b) {
    var h = {}, f = function (v) { h[v] = true; };
    a.each(f);
    b.each(f);
    return myUtils.keys(h);
  },
  intersectAB : function (a, b) {
    var h = {};
    a.each(function (v) { h[v] = 1; });
    b.each(function (v) { h[v] = (h[v] || 0) + 1; });
    var fnSel = function (v, count) { return count > 1; };
    var fnVal = function (v, c) { return v; };
    return myUtils.select(h, fnSel, fnVal);
  }
};
这假设
每个
过滤器
都是为数组定义的,并且我们有两种实用方法:

  • myUtils.keys(散列)
    :返回 具有散列的键的数组

  • myUtils.select(散列、fnSelector、,
    fnEvaluator)
    :返回带有 调用
    fnEvaluator的结果
    在其中的键/值对上
    fnSelector
    返回true

select()
的灵感来源于普通的Lisp,只是将
filter()
map()
合并为一个(最好在
对象.prototype
上定义它们,但这样做会破坏jQuery,所以我决定使用静态实用方法)

性能:使用

var a = [], b = [];
for (var i = 100000; i--; ) {
  if (i % 2 !== 0) a.push(i);
  if (i % 3 !== 0) b.push(i);
}
给出两个集合,分别包含50000和66666个元素。使用这些值,A-B大约需要75毫秒,而union和INTERSION每个大约需要150毫秒。(Mac Safari 4.0,使用Javascript日期计时。)


我认为这对于20行代码来说是不错的回报。

我将散列数组B,然后保留数组A中不存在的值:

function getHash(array){
  // Hash an array into a set of properties
  //
  // params:
  //   array - (array) (!nil) the array to hash
  //
  // return: (object)
  //   hash object with one property set to true for each value in the array

  var hash = {};
  for (var i=0; i<array.length; i++){
    hash[ array[i] ] = true;
  }
  return hash;
}

function getDifference(a, b){
  // compute the difference a\b
  //
  // params:
  //   a - (array) (!nil) first array as a set of values (no duplicates)
  //   b - (array) (!nil) second array as a set of values (no duplicates)
  //
  // return: (array)
  //   the set of values (no duplicates) in array a and not in b, 
  //   listed in the same order as in array a.

  var hash = getHash(b);
  var diff = [];
  for (var i=0; i<a.length; i++){
    var value = a[i];
    if ( !hash[value]){
      diff.push(value);
    }
  }
  return diff;
}
函数getHash(数组){ //将数组散列为一组属性 // //参数: //array-(array)(!nil)要散列的数组 // //返回:(对象) //哈希对象,数组中的每个值都有一个属性设置为true var hash={};
对于(var i=0;i,使用jQuery,最短值为:

var A=[1,2,3,4];
VarB=[1,3,4,7];
var diff=$(A).非(B);
console.log(diff.toArray());
7年后,使用object非常简单(但仍然不如
A-B
紧凑),据报道,对于大型阵列,它比
indexOf
更快:

console.clear();
设a=新集合([1,2,3,4]);
设b=新集合([5,4,3,2]);
设a_减_b=新集([…a]。过滤器(x=>!b.has(x));
设b_减a=新集([…b].filter(x=>!a.has(x));
让a_相交于_b=新集([…a]。过滤器(x=>b.has(x));
console.log([…a_减去b])//{1}
console.log([…b_减去a])//{5}
log([…a_intersect_b])/{2,3,4}
使用(函数JS库)


至于快速方式,这并不是很优雅,但我已经运行了一些测试来确定。将一个数组作为对象加载要比大量处理快得多:

var t, a, b, c, objA;

    // Fill some arrays to compare
a = Array(30000).fill(0).map(function(v,i) {
    return i.toFixed();
});
b = Array(20000).fill(0).map(function(v,i) {
    return (i*2).toFixed();
});

    // Simple indexOf inside filter
t = Date.now();
c = b.filter(function(v) { return a.indexOf(v) < 0; });
console.log('completed indexOf in %j ms with result %j length', Date.now() - t, c.length);

    // Load `a` as Object `A` first to avoid indexOf in filter
t = Date.now();
objA = {};
a.forEach(function(v) { objA[v] = true; });
c = b.filter(function(v) { return !objA[v]; });
console.log('completed Object in %j ms with result %j length', Date.now() - t, c.length);

但是,这仅适用于字符串。如果您计划比较编号集,则需要使用parseFloat一些简单函数映射结果,借用@milan的答案:

const setDifference = (a, b) => new Set([...a].filter(x => !b.has(x)));
const setIntersection = (a, b) => new Set([...a].filter(x => b.has(x)));
const setUnion = (a, b) => new Set([...a, ...b]);
用法:

const a = new Set([1, 2]);
const b = new Set([2, 3]);

setDifference(a, b); // Set { 1 }
setIntersection(a, b); // Set { 2 }
setUnion(a, b); // Set { 1, 2, 3 }

看看这些解决方案中的一大群,它们在小的情况下很好。但是,当你把它们放大到一百万个项目时,时间复杂性开始变得愚蠢

 A.filter(v => B.includes(v))
这看起来像是一个O(N^2)解决方案。既然有一个O(N)解决方案,让我们使用它,如果您的JS运行时不是最新的,那么您可以轻松地修改为不是生成器

function *setMinus(A, B) {
  const set = new Set(A);

  for (const v of B) {
    if (!set.delete(v)) {
        yield v;
    }
  }

  for (const v of set.values()) {
    yield v;
  }
}

a = [1,2,3];
b = [2,3,4];

console.log(Array.from(setMinus(a, b)));

虽然这比许多其他解决方案要复杂一些,但当您有大的列表时,这将快得多。

如果您使用的是
Set
s,它可能非常简单且性能良好:

函数设置差异(a、b){
返回新的集合(Array.from(a).filter(item=>!b.has(item));
}

<> > <代码> SET//C++ >使用Hash函数*在引擎盖下,<代码>具有比<代码>索引> <代码>要快得多的速度(这是很重要的,如果你有100个以上的项目),

你使用的这个“设置差异”术语是什么?是C++还是别的什么?你的集合中有什么?(例如数字),计算集差可以非常快速和优雅地完成。如果你的集包含(比如)DOM元素,你将陷入一个缓慢的
indexOf
实现中。@Crescent:我的集包含数字-很抱歉没有指定。@Josh:这是数学中的标准集运算()@JoshStodola那是@MattBall不,我看到了。但是Josh的问题是有效的,没有答案,所以我回答:)这和我半小时前发布的算法完全一样ago@Christoph:你是对的…我没有注意到这一点。不过我觉得我的实现更容易理解
const setDifference = (a, b) => new Set([...a].filter(x => !b.has(x)));
const setIntersection = (a, b) => new Set([...a].filter(x => b.has(x)));
const setUnion = (a, b) => new Set([...a, ...b]);
const a = new Set([1, 2]);
const b = new Set([2, 3]);

setDifference(a, b); // Set { 1 }
setIntersection(a, b); // Set { 2 }
setUnion(a, b); // Set { 1, 2, 3 }
 A.filter(v => B.includes(v))
function *setMinus(A, B) {
  const set = new Set(A);

  for (const v of B) {
    if (!set.delete(v)) {
        yield v;
    }
  }

  for (const v of set.values()) {
    yield v;
  }
}

a = [1,2,3];
b = [2,3,4];

console.log(Array.from(setMinus(a, b)));