2010-06-03 9 views
18

Poszukuję dobrego algorytmu, aby uzyskać wszystkie elementy w jednej tablicy, które nie są elementami w innej tablicy. Wziąwszy pod uwagę te tablice:Algorytm JavaScript do znajdowania elementów w tablicy, które nie są w innej tablicy

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

chcę skończyć z tej tablicy:

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

używam jQuery, więc mogę wykorzystać $.each() i $.inArray(). Oto rozwiązanie, które wymyśliłem, ale wygląda na to, że powinien istnieć lepszy sposób.

// 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"] 

Oto code in action. Jakieś pomysły?

Odpowiedz

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

Również ta nazwa metody jest zbyt krótka dla jej własnego dobra. Oczekuję, że będzie to oznaczać isElementInArray, a nie indexOf.

Dla demo z przedmiotami, zobacz http://jsfiddle.net/xBDz3/6/

+0

hmm, moja sytuacja ma obiekty w moich tablicach, a nie tylko proste ciągi. Wstawiam ciągi w moim pytaniu, aby uprościć rzeczy. Nie jestem pewien, czy Twoje rozwiązanie zadziała. – Tauren

+0

Nazwa grep może wprowadzać w błąd. To tak naprawdę nie ma nic wspólnego ze stringami. Po prostu bierze predykat. Inne języki wywołują ten sam filtr. Zrobiłem [demo] (http://jsfiddle.net/xBDz3/6/). –

+0

Wow, po szybkim teście wygląda na to, że działa. Polecenie grep wprowadza w błąd, ponieważ zakładam, że działa ono na tekście takim jak polecenie unix. Zrobię więcej testów. – Tauren

0

Bądź posortowane kopie tablic pierwszy. Jeśli górne elementy są równe, usuń je oba. W przeciwnym razie usuń element, który jest mniejszy i dodaj go do tablicy wyników. Jeśli jedna tablica jest pusta, dodaj resztę drugiej tablicy do wyniku i zakończ. Możesz dokonywać iteracji poprzez posortowane tablice zamiast usuwać elementy.

// 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: dzięki za twoją sugestię. Rozumiem, co robisz, ale chcę zmniejszyć mój kod do czegoś tak prostego i lekkiego jak to tylko możliwe, mając nadzieję, że wykorzystam istniejący kod jquery. Jeśli napotkasz problem z wydajnością, rozważę twój pomysł. – Tauren

+0

Dobra nie zawsze znaczy szybko, ale ja tak to zrobiłem. Cieszyć się. – drawnonward

2

Maybe jLinq can help you?

To pozwala na uruchamianie kwerend tak przed javascript obiektów.

Na przykład:

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 } ] 

Jest to przesada trochę dla Ciebie w tej chwili, ale to niezawodne rozwiązanie, że jestem zadowolony, aby dowiedzieć się o.

Potrafi wykonywać przecięcia, związki, obsługiwać logikę logiczną i wszelkiego rodzaju dobroć w stylu linq.

+0

świetnie wyglądająca biblioteka, jeszcze jej nie widziałem. Wciąż prawdopodobnie trochę za moje bezpośrednie potrzeby, ale prawdopodobnie dam mu wir na inne rzeczy, które muszę zrobić. Dzięki! – Tauren

12

Oto alternatywa korzystania underscore.js:

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

Oddaliłem się od bycia tak zależnym od jquery i korzystam z mniejszych bibliotek, takich jak podkreślenie i lodash. Biblioteki te znacznie ułatwiają rozwiązywanie problemów, takich jak oryginalne pytanie. Dzięki za uwzględnienie rozwiązania opartego na podkreśleniu! – Tauren

2

Jest to późna odpowiedź, ale nie korzysta z bibliotek więc niektóre mogą okazać się pomocne.

/** 
* 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; 
} 

przykład:

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

godzinach odpowiedź z nowym ECMA5 JavaScript:

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

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

Jakiś pomysł na algorytmiczną złożoność/skalowalność tego? – mikecsh

7

w ES6 prostu

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

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

(inna opcja to y.filter(e => x.indexOf(e)===-1))

Powiązane problemy