Как рассчитать пересечение нескольких массивов в JavaScript? А что означает [равно: функция]?
Я знаю этот вопрос, простейший код для пересечения массива, но все решения предполагают, что количество массивов равно двум, который не может быть уверен в моем случае.
У меня есть divs на странице с данными, которые содержат массивы. Я хочу найти значения, общие для всех массивов. Я не знаю, сколько div/массивов у меня будет заблаговременно. Каков наилучший способ вычисления значений, общих для всех массивов?
var array1 = ["Lorem", "ipsum", "dolor"];
var array2 = ["Lorem", "ipsum", "quick", "brown", "foo"];
var array3 = ["Jumps", "Over", "Lazy", "Lorem"];
var array4 = [1337, 420, 666, "Lorem"];
//Result should be ["Lorem"];
Я нашел другое решение в другом месте, используя Underscore.js.
var arrayOfArrays = [[4234, 2323, 43], [1323, 43, 1313], [23, 34, 43]];
_.intersection.apply(_, arrayOfArrays)
//Result is [43]
Я тестировал это с помощью простых фиктивных данных в конце и, похоже, работал. Но по какой-то причине некоторые из массивов, которые я создаю, которые содержат простые строки, также автоматически включают добавленное значение, "equals: function":
["Dummy1", "Dummy2", "Dummy3", equals: function]
И всякий раз, когда я использую метод пересечения Underscore.js, в массиве массивов я всегда получаю [equals: function] в инструментах dev, а не - если "Dummy3" является общим для всех массивов - [ "Dummy3" ].
Итак, TL; DR есть другое решение для пересечения массива, которое подойдет моему делу? И может ли кто-нибудь объяснить, что означает [equals: function] здесь? Когда я разворачиваю элемент в инструментах dev, он создает пустой массив и список доступных методов на массивах (поп, push, shift и т.д.), Но все эти методы исчезают, а функция equals: выделена.
Ответы
Ответ 1
Я написал вспомогательную функцию для этого:
function intersection() {
var result = [];
var lists;
if(arguments.length === 1) {
lists = arguments[0];
} else {
lists = arguments;
}
for(var i = 0; i < lists.length; i++) {
var currentList = lists[i];
for(var y = 0; y < currentList.length; y++) {
var currentValue = currentList[y];
if(result.indexOf(currentValue) === -1) {
var existsInAll = true;
for(var x = 0; x < lists.length; x++) {
if(lists[x].indexOf(currentValue) === -1) {
existsInAll = false;
break;
}
}
if(existsInAll) {
result.push(currentValue);
}
}
}
}
return result;
}
Используйте это так:
intersection(array1, array2, array3, array4); //["Lorem"]
Или вот так:
intersection([array1, array2, array3, array4]); //["Lorem"]
Полный код здесь
ОБНОВЛЕНИЕ 1
Немного меньшая реализация здесь с использованием filter
Ответ 2
Вы можете просто использовать Array#reduce
с Array#filter
Array#includes
и Array#includes
.
var array1 = ["Lorem", "ipsum", "dolor"],
array2 = ["Lorem", "ipsum", "quick", "brown", "foo"],
array3 = ["Jumps", "Over", "Lazy", "Lorem"],
array4 = [1337, 420, 666, "Lorem"],
data = [array1, array2, array3, array4],
result = data.reduce((a, b) => a.filter(c => b.includes(c)));
console.log(result);
Ответ 3
Это можно сделать довольно лаконично, если вы хотите использовать некоторую рекурсию и новый синтаксис ES2015:
const array1 = ["Lorem", "ipsum", "dolor"];
const array2 = ["Lorem", "ipsum", "quick", "brown", "foo"];
const array3 = ["Jumps", "Over", "Lazy", "Lorem"];
const array4 = [1337, 420, 666, "Lorem"];
const arrayOfArrays = [[4234, 2323, 43], [1323, 43, 1313], [23, 34, 43]];
// Filter xs where, for a given x, there exists some y in ys where y === x.
const intersect2 = (xs,ys) => xs.filter(x => ys.some(y => y === x));
// When there is only one array left, return it (the termination condition
// of the recursion). Otherwise first find the intersection of the first
// two arrays (intersect2), then repeat the whole process for that result
// combined with the remaining arrays (intersect). Thus the number of arrays
// passed as arguments to intersect is reduced by one each time, until
// there is only one array remaining.
const intersect = (xs,ys,...rest) => ys === undefined ? xs : intersect(intersect2(xs,ys),...rest);
console.log(intersect(array1, array2, array3, array4));
console.log(intersect(...arrayOfArrays));
// Alternatively, in old money,
var intersect2ES5 = function (xs, ys) {
return xs.filter(function (x) {
return ys.some(function (y) {
return y === x;
});
});
};
// Changed slightly from above, to take a single array of arrays,
// which matches the underscore.js approach in the Q., and is better anyhow.
var intersectES5 = function (zss) {
var xs = zss[0];
var ys = zss[1];
var rest = zss.slice(2);
if (ys === undefined) {
return xs;
}
return intersectES5([intersect2ES5(xs, ys)].concat(rest));
};
console.log(intersectES5([array1, array2, array3, array4]));
console.log(intersectES5(arrayOfArrays));
Ответ 4
Используя комбинацию идей от нескольких участников и новейшую доброту ES6, я пришел к
const array1 = ["Lorem", "ipsum", "dolor"];
const array2 = ["Lorem", "ipsum", "quick", "brown", "foo"];
const array3 = ["Jumps", "Over", "Lazy", "Lorem"];
const array4 = [1337, 420, 666, "Lorem"];
Array.prototype.intersect = function intersect(a, ...b) {
const c = function (a, b) {
b = new Set(b);
return a.filter((a) => b.has(a));
};
return undefined === a ? this : intersect.call(c(this, a), ...b);
};
console.log(array1.intersect(array2, array3, array4));
// ["Lorem"]
Ответ 5
Ваш код с _lodash работает нормально.
Как вы можете сказать в this скрипт:
этот код:
var arrayOfArrays = [[4234, 2323, 43], [1323, 43, 1313], [23, 34, 43]];
var a = _.intersection.apply(_, arrayOfArrays);
console.log(a);
console.log(a.length);
Будет выводиться:
[42]
1
Возможно, вы видите
равно: функция
потому что вы используете отладчик.
Попробуйте просто напечатать массив с помощью console.log
, вы получите только 42.
Ответ 6
Для всех, кого это смущает в будущем,
_.intersection.apply(_, arrayOfArrays)
На самом деле это самый элегантный способ сделать это. Но:
var arrayOfArrays = [[43, 34343, 23232], [43, 314159, 343], [43, 243]];
arrayOfArrays = _.intersection.apply(_, arrayOfArrays);
Не получится! Необходимо сделать
var differentVariableName = _.intersection.apply(_,arrayOfArrays);
Ответ 7
Lodash pure:
_.keys(_.pickBy(_.groupBy(_.flatten(arrays)), function (e) {return e.length > 1}))
Лодаш с простым js:
var elements = {}, duplicates = {};
_.each(arrays, function (array) {
_.each(array, function (element) {
if (!elements[element]) {
elements[element] = true;
} else {
duplicates[element] = true;
}
});
});
_.keys(duplicates);
Ответ 8
Небольшое рекурсивное решение "разделяй и властвуй", которое не зависит от es6 или какой-либо библиотеки.
Он принимает массив массивов, что делает код короче и позволяет передавать аргументы с помощью map.
function intersection(a) {
if (a.length > 2)
return intersection([intersection(a.slice(0, a.length / 2)), intersection(a.slice(a.length / 2))]);
if (a.length == 1)
return a[0];
return a[0].filter(function(item) {
return a[1].indexOf(item) !== -1;
});
}
var list1 = [ 'a', 'b', 'c' ];
var list2 = [ 'd', 'b', 'e' ];
var list3 = [ 'f', 'b', 'e' ];
console.log(intersection([list1, list2, list3]));
Ответ 9
Ответ Arg0n великолепен, но на всякий случай, если кто-то ищет пересечение массивов объектов, я немного изменил ответ Arg0n, чтобы разобраться с ним.
function getIntersectedData() {
var lists = [[{a:1,b:2},{a:2,b:2}],[{a:1,b:2},{a:3,b:3}],[{a:1,b:2},{a:4,b:4}]];
var result = [];
for (var i = 0; i < lists.length; i++) {
var currentList = lists[i];
for (var y = 0; y < currentList.length; y++) {
var currentValue = currentList[y];
if (customIndexOf(result,currentValue)) {
var existsInAll = true;
for (var x = 0; x < lists.length; x++) {
if(customIndexOf(lists[x],currentValue)){
existsInAll = false;
break;
}
}
if (existsInAll) {
result.push(currentValue);
}
}
}
return result;
}
}
function customIndexOf(array,value){
var notFind = true;
array.forEach(function(element){
if(element.a === value.a){
notFind = false;
}
});
return notFind;
}
Ответ 10
Мне удается сделать это с помощью reduce
вызова:
var intersected = intersect([[1, 2, 3], [2, 3, 4], [3, 4, 5]]);
console.log(intersected); // [3]
function intersect(arrays) {
if (0 === arrays.length) {
return [];
}
return arrays.reduce((intersection, array) => {
return intersection.filter(intersectedItem => array.some(item => intersectedItem === item));
}, arrays[0]);
}