Включив идею Кристофа и допустив пару нестандартных методов итерации для массивов и объектов / хэшей ( 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);
}
};
Это предполагает, что each
и filter
определены для массивов, и что у нас есть два служебных метода:
myUtils.keys(hash)
: возвращает массив с ключами хеша
myUtils.select(hash, fnSelector,
fnEvaluator)
: возвращает массив с результатами вызова fnEvaluator
пар ключ / значение, для которых
fnSelector
возвращается истина.
select()
Свободно вдохновлен Common Lisp, и просто , 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 занимает около 75 мс, а объединение и пересечение - около 150 мс каждое. (Mac Safari 4.0 с использованием даты Javascript для измерения времени.)
Я считаю, что за 20 строк кода это приличная плата.