es6 - Encontrar coincidencias entre varias matrices de JavaScript
foreach javascript (9)
Ahora que ha agregado un número indeterminado de matrices a la pregunta, aquí hay otro enfoque que recopila el recuento de cada elemento en un objeto y luego recopila los elementos que tienen el recuento máximo.
Ventajas de este enfoque:
- ~ 15 veces más rápido que las opciones de búsqueda de fuerza bruta (utilizadas por otras respuestas) si las matrices son más grandes
- No requiere calzas ES5 o ES5 (funciona con todos los navegadores)
- Completamente no destructivo (no cambia los datos fuente en absoluto)
- Maneja elementos duplicados en matrices fuente
- Maneja un número arbitrario de matrices de entrada
Y aquí está el código:
function containsAll(/* pass all arrays here */) {
var output = [];
var cntObj = {};
var array, item, cnt;
// for each array passed as an argument to the function
for (var i = 0; i < arguments.length; i++) {
array = arguments[i];
// for each element in the array
for (var j = 0; j < array.length; j++) {
item = "-" + array[j];
cnt = cntObj[item] || 0;
// if cnt is exactly the number of previous arrays,
// then increment by one so we count only one per array
if (cnt == i) {
cntObj[item] = cnt + 1;
}
}
}
// now collect all results that are in all arrays
for (item in cntObj) {
if (cntObj.hasOwnProperty(item) && cntObj[item] === arguments.length) {
output.push(item.substring(1));
}
}
return(output);
}
Demostración de trabajo: http://jsfiddle.net/jfriend00/52mAP/
FYI, esto no requiere ES5, por lo que funcionará en todos los navegadores sin una cuña.
En una prueba de rendimiento en 15 matrices cada 1000 de largo, esto fue más de 10 veces más rápido que el método de búsqueda utilizado en la respuesta no soy de esta jsperf: http://jsperf.com/in-all-arrays .
Aquí hay una versión que usa un Map
y un Set
ES6 para quitar impresiones y realizar un seguimiento de los recuentos. Esto tiene la ventaja de que el tipo de datos se conserva y puede ser cualquier cosa (ni siquiera tiene que tener una conversión de cadena natural, los datos pueden incluso ser objetos aunque los objetos se comparan por ser el mismo objeto exacto, sin tener el mismo propiedades / valores).
var arrays = [
[''valueOf'', ''toString'',''apple'', ''orange'', ''banana'', ''banana'', ''pear'', ''fish'', ''pancake'', ''taco'', ''pizza'', 1, 2, 999, 888],
[''valueOf'', ''toString'',''taco'', ''fish'', ''fish'', ''apple'', ''pizza'', 1, 999, 777, 999, 1],
[''valueOf'', ''toString'',''banana'', ''pizza'', ''fish'', ''apple'', ''apple'', 1, 2, 999, 666, 555]
];
// subclass for updating cnts
class MapCnt extends Map {
constructor(iterable) {
super(iterable);
}
cnt(iterable) {
// make sure items from the array are unique
let set = new Set(iterable);
// now update the cnt for each item in the set
for (let item of set) {
let cnt = this.get(item) || 0;
++cnt;
this.set(item, cnt);
}
}
}
function containsAll(...allArrays) {
let cntObj = new MapCnt();
for (array of allArrays) {
cntObj.cnt(array);
}
// now see how many items have the full cnt
let output = [];
for (var [item, cnt] of cntObj.entries()) {
if (cnt === allArrays.length) {
output.push(item);
}
}
return(output);
}
var result = containsAll.apply(this, arrays);
document.body.innerHTML = "<pre>[<br> " + result.join('',<br> '') + "<br>]</pre>";
Tengo varias matrices con valores de cadena y quiero compararlas y solo mantener los resultados coincidentes que son idénticos entre TODOS .
Dado este código de ejemplo:
var arr1 = [''apple'', ''orange'', ''banana'', ''pear'', ''fish'', ''pancake'', ''taco'', ''pizza''];
var arr2 = [''taco'', ''fish'', ''apple'', ''pizza''];
var arr3 = [''banana'', ''pizza'', ''fish'', ''apple''];
Me gustaría producir la siguiente matriz que contiene coincidencias de todas las matrices dadas:
[''apple'', ''fish'', ''pizza'']
Sé que puedo combinar todas las matrices con var newArr = arr1.concat(arr2, arr3);
pero eso solo dame una matriz con todo, más los duplicados. ¿Se puede hacer esto fácilmente sin necesitar la sobrecarga de bibliotecas como underscore.js?
(¡Genial, y ahora también tengo hambre!)
EDITAR Supongo que debería mencionar que podría haber una cantidad desconocida de matrices, solo estaba usando 3 como ejemplo.
Aquí va una solución de una sola línea. Puedes dividirlo en dos pasos de pensamiento:
- Calcular unión / intersección entre dos matrices
var arrA = [1,2,3,4,5];
var arrB = [4,5,10];
var innerJoin = arrA.filter(el=>arrB.includes(el));
console.log(`Intersection is: ${innerJoin}`);
- Reduzca el contenido: calcule la intersección entre la intersección acumulada y la siguiente matriz.
var arrays = [
[''apple'', ''orange'', ''banana'', ''pear'', ''fish'', ''pancake'', ''taco'', ''pizza''],
[''taco'', ''fish'', ''apple'', ''pizza''],
[''banana'', ''pizza'', ''fish'', ''apple'']
];
var join = arrays.reduce((join, current) => join.filter(el => current.includes(el)));
console.log(`Intersection is: ${join}`);
Asumiendo una matriz de matrices y comprobando a través de todas las matrices:
DEMO: http://jsfiddle.net/qUQHW/
var tmp = {};
for (i = 0; i < data.length; i++) {
for (j = 0; j < data[i].length; j++) {
if (!tmp[data[i][j]]) {
tmp[data[i][j]] = 0;
}
tmp[data[i][j]]++;
}
}
var results = $.map(tmp, function(val,key) {
return val == data.length ? key :null;
})
Esto debería funcionar para cualquier cantidad de matrices:
function intersection(arr1, arr2) {
var temp = [];
for (var i in arr1) {
var element = arr1[i];
if (arr2.indexOf(element) > -1) {
temp.push(element);
}
}
return temp;
}
function multi_intersect() {
var arrays = Array.prototype.slice.apply(arguments).slice(1);
var temp = arguments[0];
for (var i in arrays) {
temp = intersection(arrays[i], temp);
if (temp == []) {
break;
}
}
return temp;
}
var arr1 = [''apple'', ''orange'', ''banana'', ''pear'', ''fish'', ''pancake'', ''taco'', ''pizza''];
var arr2 = [''taco'', ''fish'', ''apple'', ''pizza''];
var arr3 = [''banana'', ''pizza'', ''fish'', ''apple''];
multi_intersect(arr1, arr2, arr3);
Esto es esencialmente una compilación de todas las respuestas reducidas:
// Intersect any number of arrays:
function intersect() {
// - Arguments -> traditional array,
// - First item ( arrays[0] ) = shortest to reduce iterations
var arrays = Array.prototype.slice.call(arguments).sort(function(a, b) {
return a.length - b.length;
});
// Use first array[0] as the base.
var a = arrays.shift();
var result = [];
for (var i = a.length; i--;) {
var val = a[i];
// Prevent duplicates
if (result.indexOf(val) < 0) {
// Seek
var found = true;
for (var ii = arrays.length; ii--;) {
if (arrays[ii].indexOf(val) < 0) {
found = false;
break;
}
}
if (found) {
result.push(val);
}
}
}
return result;
}
/*
// Slower, but smaller code-base:
function intersect (){
// - Arguments -> traditional array,
// - First item ( arrays[0] ) = shortest to reduce iterations
var arrays = Array.prototype.slice.call(arguments).sort(function(a, b) {
return a.length - b.length;
});
// Use first array[0] as the base.
var a = arrays.shift();
return a.filter(function (val, idx, aa) {
// Seek
for(var i=arrays.length; i--;){
if (arrays[i].indexOf(val) < 0) {
return false;
}
}
// Prevent duplicates
return aa.indexOf(val) === idx;
});
}
*/
var arr1 = [''apple'', ''orange'', ''banana'', ''pear'', ''fish'', ''pancake'', ''taco'', ''pizza''];
var arr2 = [''taco'', ''fish'', ''apple'', ''pizza'', ''apple'', ''apple''];
var arr3 = [''banana'', ''pizza'', ''fish'', ''apple'', ''fish''];
var arr1 = [''apple'', ''orange'', ''banana'', ''pear'', ''fish'', ''pancake'', ''taco'', ''pizza''];
var arr2 = [''taco'', ''fish'', ''apple'', ''pizza'', ''apple'', ''apple''];
var arr3 = [''banana'', ''pizza'', ''fish'', ''apple'', ''fish''];
var result = intersect(arr1, arr2, arr3);
// For fiddle output:
var elem = document.getElementById("result");
elem.innerHTML = JSON.stringify(result);
console.log(result);
<div id="result">Results</div>
Solo por el placer de hacerlo, otro enfoque de mano larga:
function getCommon(a) {
// default result is copy of first array
var result = a[0].slice();
var mem, arr, found = false;
// For each member of result, see if it''s in all other arrays
// Go backwards so can splice missing entries
var i = result.length;
while (i--) {
mem = result[i];
// Check in each array
for (var j=1, jLen=a.length; j<jLen; j++) {
arr = a[j];
found = false;
// For each member of arr and until found
var k = arr.length;
while (k-- && !found) {
// If found in this array, set found to true
if (mem == arr[k]) {
found = true;
}
}
// if word wasn''t found in this array, remove it from result and
// start on next member of result, skip remaining arrays.
if (!found) {
result.splice(i,1);
break;
}
}
}
return result;
}
var data = [
[''taco'', ''fish'', ''apple'', ''pizza'', ''mango'', ''pear''],
[''apple'', ''orange'', ''banana'', ''pear'', ''fish'', ''pancake'', ''taco'', ''pizza''],
[''banana'', ''pizza'', ''fish'', ''apple''],
[''banana'', ''pizza'', ''fish'', ''apple'', ''mango'', ''pear'']
];
Editar
Función para encontrar propiedades nunca enumerables basadas en thise en Object.prototype:
// Return an array of Object.prototype property names that are not enumerable
// even when added directly to an object.
// Can be helpful with IE as properties like toString are not enumerable even
// when added to an object.
function getNeverEnumerables() {
// List of Object.prototype property names plus a random name for testing
var spNames = ''constructor toString toLocaleString valueOf '' +
''hasOwnProperty isPrototypeOf propertyIsEnumerable foo'';
var spObj = {foo:'''', ''constructor'':'''', ''toString'':'''', ''toLocaleString'':'''', ''valueOf'':'''',
''hasOwnProperty'':'''', ''isPrototypeOf'':'''', ''propertyIsEnumerable'':''''};
var re = [];
// BUild list of enumerable names in spObj
for (var p in spObj) {
re.push(p);
}
// Remove enumerable names from spNames and turn into an array
re = new RegExp(''(^|//s)'' + re.join(''|'') + ''(//s|$)'',''g'');
return spNames.replace(re, '' '').replace(/(^/s+)|/s/s+|(/s+$)/g,'''').split('' '');
}
document.write(getNeverEnumerables().join(''<br>''));
Suponiendo que hay una matriz de matrices de las que queremos encontrar la intersección de, un enfoque de línea simple más simple podría ser
var arr = [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7]],
int = arr.reduce((p,c) => p.filter(e => c.includes(e)));
document.write("<pre>" + JSON.stringify(int) + "</pre>");
Un par de pensamientos: puede comparar solo los elementos en la matriz más corta y evitar duplicados en la matriz devuelta.
function arraysInCommon(arrays){
var i, common,
L= arrays.length, min= Infinity;
while(L){
if(arrays[--L].length<min){
min= arrays[L].length;
i= L;
}
}
common= arrays.splice(i, 1)[0];
return common.filter(function(itm, indx){
if(common.indexOf(itm)== indx){
return arrays.every(function(arr){
return arr.indexOf(itm)!= -1;
});
}
});
}
var arr1= [''apple'', ''orange'', ''banana'', ''pear'', ''fish'', ''pancake'', ''taco'', ''pizza''];
var arr2= [''taco'', ''fish'', ''apple'', ''pizza'', ''apple'',''apple''];
var arr3= [''banana'', ''pizza'', ''fish'', ''apple'',''fish''];
var allArrays = [arr1,arr2,arr3];
arraysInCommon(allArrays).sort();
valor devuelto: apple,fish,pizza
DEMO - http://jsfiddle.net/kMcud/
var result = arrays.shift().filter(function(v) {
return arrays.every(function(a) {
return a.indexOf(v) !== -1;
});
});
DEMO: http://jsfiddle.net/nWjcp/2/
Primero puedes ordenar la matriz externa para obtener la matriz más corta al principio ...
arrays.sort(function(a, b) {
return a.length - b.length;
});
Para completar, aquí hay una solución que trata con duplicados en las matrices. Utiliza .reduce()
lugar de .filter()
...
var result = arrays.shift().reduce(function(res, v) {
if (res.indexOf(v) === -1 && arrays.every(function(a) {
return a.indexOf(v) !== -1;
})) res.push(v);
return res;
}, []);