Javascript 배열을 사용하여 집합 차이를 계산하는 가장 빠르고 우아한 방법은 무엇입니까?
하자 A
와 B
두 세트합니다. 나는 그들 사이 의 세트 차이 ( 또는 선호도에 따라) 를 계산하는 정말 빠르고 우아한 방법을 찾고 있습니다. 두 세트는 제목에서 알 수 있듯이 Javascript 배열로 저장 및 조작됩니다.A - B
A \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 객체를 사용하면 매우 쉽고 (하지만 여전히 pythons 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의 답변 에서 B
와 A
같이의 각 요소를 선형으로 스캔하지 않도록 할 수 있습니다 .
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;
}
The non-standard toSource()
method is used to get unique property names; if all elements already have unique string representations (as is the case with numbers), you can speed up the code by dropping the toSource()
invocations.
The shortest, using jQuery, is:
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>
I would hash the array B, then keep values from the array A not present in B:
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;
}
Incorporating the idea from Christoph and assuming a couple of non-standard iteration methods on arrays and objects/hashes (each
and friends), we can get set difference, union and intersection in linear time in about 20 lines total:
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);
}
};
This assumes that each
and filter
are defined for arrays, and that we have two utility methods:
myUtils.keys(hash)
: returns an array with the keys of the hashmyUtils.select(hash, fnSelector, fnEvaluator)
: returns an array with the results of callingfnEvaluator
on the key/value pairs for whichfnSelector
returns true.
The select()
is loosely inspired by Common Lisp, and is merely filter()
and map()
rolled into one. (It would be better to have them defined on Object.prototype
, but doing so wrecks havoc with jQuery, so I settled for static utility methods.)
Performance: Testing with
var a = [], b = [];
for (var i = 100000; i--; ) {
if (i % 2 !== 0) a.push(i);
if (i % 3 !== 0) b.push(i);
}
gives two sets with 50,000 and 66,666 elements. With these values A-B takes about 75ms, while union and intersection are about 150ms each. (Mac Safari 4.0, using Javascript Date for timing.)
I think that's decent payoff for 20 lines of code.
Using Underscore.js (Library for functional JS)
>>> var foo = [1,2,3]
>>> var bar = [1,2,4]
>>> _.difference(foo, bar);
[4]
Some simple functions, borrowing from @milan's answer:
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]);
Usage:
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 }
As for the fasted way, this isn't so elegant but I've run some tests to be sure. Loading one array as an object is far faster to process in large quantities:
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);
Results:
completed indexOf in 1219 ms with result 5000 length
completed Object in 8 ms with result 5000 length
However, this works with strings only. If you plan to compare numbered sets you'll want to map results with parseFloat.
This works, but I think another one is much more shorter, and elegant too
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;
'development' 카테고리의 다른 글
안드로이드에서 액션 바 뒤로 버튼을 재정의하는 방법은 무엇입니까? (0) | 2020.09.08 |
---|---|
CSS 만 사용하여 HTML 요소 사이에 공백 추가 (0) | 2020.09.08 |
Eclipse로 Tomcat 원격 디버깅 (0) | 2020.09.08 |
Xvfb에서 Selenium을 어떻게 실행합니까? (0) | 2020.09.08 |
View.setPadding은 px에서만 허용합니다. 어쨌든 dp에서 setPadding이 있습니까? (0) | 2020.09.08 |