Включивши ідею від Крістофа і передбачаючи пару нестандартних методів ітерації на масивах та об'єктах / хешах ( 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
повертає true.
Це 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 рядків коду.