[javascript] Javascript 배열을 사용하여 집합 차이를 계산하는 가장 빠르고 우아한 방법은 무엇입니까?

하자 AB두 세트합니다. 나는 그들 사이 의 세트 차이 ( 또는 선호도에 따라) 를 계산하는 정말 빠르고 우아한 방법을 찾고 있습니다. 두 세트는 제목에서 알 수 있듯이 Javascript 배열로 저장 및 조작됩니다.A - BA \B

노트:

  • Gecko 특정 트릭은 괜찮습니다.
  • 나는 기본 기능을 고수하는 것을 선호합니다 (하지만 더 빠르면 가벼운 라이브러리에 열려 있습니다)
  • 나는 보았지만 테스트하지는 않았지만 JS.Set (이전 요점 참조)

편집 : 중복 요소가 포함 된 세트에 대한 주석을 발견했습니다. 내가 “set”이라고 말할 때 나는 (다른 것들 중에서) 중복 요소를 포함하지 않는다는 것을 의미하는 수학적 정의를 의미합니다.



답변

이것이 가장 효과적인지 모르겠지만 아마도 가장 짧은

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

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

console.log(diff);

ES6로 업데이트 :

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

diff = A.filter(x => !B.includes(x) );

console.log(diff);


답변

글쎄, 7 년 후, ES6의 Set 객체를 사용하면 매우 쉽고 (하지만 여전히 파이썬 만큼 콤팩트하지는 않습니다 A - B) indexOf큰 배열 보다 빠르다고 합니다.

console.clear();
let a = new Set([1, 2, 3, 4]);
let b = new Set([5, 4, 3, 2]);


let a_minus_b = new Set([...a].filter(x => !b.has(x)));
let b_minus_a = new Set([...b].filter(x => !a.has(x)));
let a_intersect_b = new Set([...a].filter(x => b.has(x)));

console.log([...a_minus_b]) // {1}
console.log([...b_minus_a]) // {5}
console.log([...a_intersect_b]) // {2,3,4}


답변

객체를지도로 사용하여 user187291의 답변 에서 BA같이의 각 요소를 선형으로 스캔하지 않도록 할 수 있습니다 .

function setMinus(A, B) {
    var map = {}, C = [];

    for(var i = B.length; i--; )
        map[B[i].toSource()] = null; // any other value would do

    for(var i = A.length; i--; ) {
        if(!map.hasOwnProperty(A[i].toSource()))
            C.push(A[i]);
    }

    return C;
}

비표준 toSource()방법 은 고유 한 속성 이름을 가져 오는 데 사용됩니다. 모든 요소에 이미 고유 한 문자열 표현이있는 경우 (숫자의 경우처럼) toSource()호출 을 삭제하여 코드 속도를 높일 수 있습니다 .


답변

jQuery를 사용하는 가장 짧은 방법은 다음과 같습니다.

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

var diff = $(A).not(B);

console.log(diff.toArray());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


답변

배열 B를 해시 한 다음 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;
}


답변

Christoph의 아이디어를 통합하고 배열 및 객체 / 해시 ( each및 친구)에 대한 몇 가지 비표준 반복 방법을 가정하면 총 약 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);
  }
};

이것은 그 가정 eachfilter배열에 대해 정의하고 있으며, 우리는 두 개의 유틸리티 방법을 가지고 :

  • myUtils.keys(hash): 해시의 키가있는 배열을 반환합니다.

  • myUtils.select(hash, fnSelector,
    fnEvaluator)
    : true fnEvaluator
    fnSelector반환 하는 키 / 값 쌍을 호출 한 결과가있는 배열 을
    반환합니다.

select()느슨하게 커먼 리스프에서 영감을, 그리고 단지입니다 filter()map()하나에 굴렀다. (에서 정의하는 Object.prototype것이 좋지만 그렇게하면 jQuery가 혼란스러워서 정적 유틸리티 메서드에 정착했습니다.)

성능 : 테스트

var a = [], b = [];
for (var i = 100000; i--; ) {
  if (i % 2 !== 0) a.push(i);
  if (i % 3 !== 0) b.push(i);
}

50,000 및 66,666 요소가있는 두 세트를 제공합니다. 이 값으로 AB는 약 75ms가 걸리고 결합과 교차는 각각 약 150ms가 걸립니다. (Mac Safari 4.0, 타이밍에 Javascript Date 사용)

20 줄의 코드에 대한 적절한 보상이라고 생각합니다.


답변

사용 Underscore.js를 (기능 JS위한 라이브러리)

>>> var foo = [1,2,3]
>>> var bar = [1,2,4]
>>> _.difference(foo, bar);
[4]