2010-06-03 17 views
18

Estoy buscando un buen algoritmo para obtener todos los elementos en una matriz que no son elementos en otra matriz. Así que dado estas matrices:Algoritmo de Javascript para encontrar elementos en la matriz que no están en otra matriz

var x = ["a","b","c","t"]; 
var ​​​​​​​​​y = [​​​​​​​"d","a","t","e","g"]; 

Quiero terminar con esta matriz:

var z = ["d","e","g"]; 

estoy usando jQuery, para que pueda tomar ventaja de $.each() y $.inArray(). Esta es la solución que se me ocurrió, pero parece que debería haber una mejor manera.

// goal is to get rid of values in y if they exist in x 
var x = ["a","b","c","t"]; 
var y = ["d","a","t","e","g"]; 

var z = []; 
$.each(y, function(idx, value){ 
    if ($.inArray(value,x) == -1) { 
    z.push(value); 
    } 
}); 
​alert(z); // should be ["d","e","g"] 

Aquí está el code in action. ¿Algunas ideas?

Respuesta

8
var z = $.grep(y, function(el){return $.inArray(el, x) == -1}); 

Además, ese nombre de método es demasiado corto para su propio bien. Esperaría que signifique isElementInArray, no indexOf.

Para una demostración con objetos, ver http://jsfiddle.net/xBDz3/6/

+0

hmm, así mi situación tiene objetos en mis arreglos, no sólo cadenas simples. Puse cadenas en mi pregunta para simplificar las cosas. No estoy seguro de que tu solución funcione. – Tauren

+0

El nombre grep puede ser engañoso. Realmente no tiene nada que ver con cadenas. Simplemente toma un predicado. Otros idiomas llaman al mismo filtro. Hice una [demo] (http://jsfiddle.net/xBDz3/6/). –

+0

guau, después de una prueba rápida, parece que funciona. El comando grep es engañoso, ya que supongo que funcionaría en texto como el comando Unix. Haré algunas pruebas más. – Tauren

0

Haga copias clasificadas de las matrices primera. Si los elementos superiores son iguales, elimínelos. De lo contrario, elimine el elemento que sea menor y agréguelo a su matriz de resultados. Si una matriz está vacía, agregue el resto de la otra matriz al resultado y finalice. Puede iterar a través de las matrices ordenadas en lugar de eliminar elementos.

// assume x and y are sorted 
xi = 0; yi = 0; xc = x.length; yc = y.length; 
while (xi < xc && yi < yc) { 
    if (x[xi] == y[yi]) { 
    xi += 1; 
    yi += 1; 
    } else if (x[xi] < y[yi]) { 
    z.push(x[xi]); 
    xi += 1; 
    } else { 
    z.push(y[yi]); 
    yi += 1; 
    } 
} 
// add remainder of x and y to z. one or both will be empty. 
+0

@Drawnonward: gracias por su sugerencia. Entiendo lo que estás haciendo, pero quiero reducir mi código a algo tan simple y ligero como sea posible, con la esperanza de aprovechar el código jquery existente. Si encuentro un problema de rendimiento, consideraré su idea. – Tauren

+0

Bueno, no siempre significa rápido, pero lo tomé de esa manera. Disfrutar. – drawnonward

2

Maybe jLinq can help you?

Le permite ejecutar consultas como ésta contra los objetos JavaScript.

Por ejemplo:

var users = [ { name: "jacob", age: 25 }, { name: "bob" , age: 30 }] 
var additionalusers = [ { name: "jacob", age: 25 }, { name: "bill" , age: 25 }] 

var newusers = jLinq.from(users).except(additionalusers).select(); 

>>> newusers = [ { name: "bob" , age: 30 } ] 

Es un poco exagerado para usted en este momento, pero es una solución robusta que estaba contento de aprender.

Puede hacer cruces, uniones, manejar la lógica booleana y todo tipo de grandes bondades de estilo linq.

+0

biblioteca de gran apariencia, aún no la he visto. Probablemente aún sea demasiado para mis necesidades inmediatas, pero probablemente le daré un giro para otras cosas que debo hacer. ¡Gracias! – Tauren

12

Aquí es una alternativa usando underscore.js:

function inAButNotInB(A, B) { 
    return _.filter(A, function (a) { 
    return !_.contains(B, a); 
    }); 
} 
+1

Me he alejado de ser tan dependiente de jquery y estoy utilizando bibliotecas más pequeñas, como guiones bajos y lodash. Estas bibliotecas hacen que resolver problemas como la pregunta original sea mucho más fácil. ¡Gracias por incluir una solución basada en guiones bajos! – Tauren

2

Ésta es una respuesta tardía, pero que no utiliza bibliotecas de lo que algunos pueden encontrar útil.

/** 
* Returns a non-destructive Array of elements that are not found in 
* any of the parameter arrays. 
* 
* @param {...Array} var_args Arrays to compare. 
*/ 
Array.prototype.uniqueFrom = function() { 
    if (!arguments.length) 
    return []; 
    var a1 = this.slice(0); // Start with a copy 

    for (var n=0; n < arguments.length; n++) { 
    var a2 = arguments[n]; 
    if (!(a2 instanceof Array)) 
     throw new TypeError('argument ['+n+'] must be Array'); 

    for(var i=0; i<a2.length; i++) { 
     var index = a1.indexOf(a2[i]); 
     if (index > -1) { 
     a1.splice(index, 1); 
     } 
    } 
    } 
    return a1; 
} 

Ejemplo:

var sheetUsers = ['[email protected]','[email protected]','[email protected]']; 
var siteViewers = ['[email protected]','[email protected]','[email protected]']; 
var viewersToAdd = sheetUsers.uniqueFrom(siteViewers); // [[email protected]] 
var viewersToRemove = siteViewers.uniqueFrom(sheetUsers); // [[email protected]] 
42

respuesta tardía con el nuevo javascript ECMA5:

var x = ["a","b","c","t"]; 
var y = ["d","a","t","e","g"]; 

myArray = y.filter(function(el) { 
    return x.indexOf(el) < 0; 
}); 
+0

¿Alguna idea sobre la complejidad algorítmica/escalabilidad de esto? – mikecsh

7

en ES6 simplemente

var x = ["a", "b", "c", "t"]; 
 
var y = ["d", "a", "t", "e", "g"]; 
 

 
console.log(y.filter(e => !x.includes(e)));

(otra opción es y.filter(e => x.indexOf(e)===-1))

Cuestiones relacionadas