2010-05-09 8 views
13

In jQuery, filter() reduziert Ihr Ergebnis auf diejenigen Elemente, die eine bestimmte Bedingung erfüllen.jQuery: Verwenden Sie filter(), aber arbeiten Sie mit beiden Ergebnissen

Dies teilt die Liste in zwei Teile. Arbeiten mit dem „Gut die Hälfte“ der Elemente ist einfach:

$("some selector").filter(function() { 
    // determine result... 
    return result; 
}).each(/* do something */); 

Aber wie kann ich mit der „anderen Hälfte“ Arbeit meiner Elemente, auch - aber ohne das Äquivalent von zu tun dies:

$("some selector").filter(function() { 
    // determine result... 
    return !result; 
}).each(/* do something else */); 

Grundsätzlich möchte ich zwei separate /* do something */ Teile zu einem einzigen Filter füttern. Eine für die, die übereinstimmen und eine für die anderen - ohne zweimal filtern zu müssen. Fehle ich eine jQuery-Funktion, die das tut?


P. S .: Ich denke, was ich tun konnte:

$("some selector").each(function() { 
    // determine result... 
    if (result) 
    /* do something */ 
    else 
    /* do something else */ 
}); 

Aber ich war etwas schöner zu hoffen.

+0

Ich denke, es wird 'each()' sein. – Matt

+2

http://jsfiddle.net/nZtRF/ Eine Sache, die vielleicht bemerkenswert ist, ist, dass mit der 'not()' Methode die Leistung exponentiell abnimmt, wenn die Anzahl der Elemente zunimmt. Für kleinere Mengen ist es trivial. Bei großen Mengen erzielen Sie mit einer 'if()' -Anweisung im Filter eine bessere Leistung. Siehe Test bei jfiddle Link oben, und Unordnung mit Mengen. – user113716

+0

@patrick: Sehr wahr. Das ist ein schwerwiegender Nachteil. : - \ – Tomalak

Antwort

16

Das Verfahren von Kobi in Plugin Form empfohlen:

$.fn.invert = function() { 
    return this.end().not(this); 
}; 

$('.foo').filter(':visible').hide().invert().show(); 

Beachten Sie, dass invert() kein neues Element auf dem Stapel jQuery hinzufügen, aber die letzte ersetzen:

$('.foo').filter(':visible').invert().end(); // this will yield $('.foo'), not $('.foo:visible') 

Edit: auf Tomalaks Vorschlag geändert prevObject zu end().

+0

Süß. :-) Ich mag das wirklich. – Tomalak

+0

Mein Code kann nicht angekettet werden, und das scheint sich sehr gut auszubreiten. +1 – Kobi

+0

Wäre 'end()' nicht besser als 'prevObject'? 'prevObject' bedeutet, dass ich in dem unmittelbar vorhergehenden Schritt filtern muss, während' end() 'sich auf die" neueste Filteroperation "gemäß den Dokumenten bezieht, was bedeutet, dass es mehrere Schritte zurück sein kann. – Tomalak

0

Interessante Frage. Ich sehe, Sie lehnen sich auf das, was ich vorschlagen würde:

$("some selector").each(function() { 
    if ($(this).is(SOMEFILTER)) { 
    // do something 
    } else { 
    // do something 
    } 
    // continue with things that apply to everything 
}); 
+0

Ich hatte auf eine Methode zur Verkettung von Methoden im jQuery-Stil gehofft; aber 'jedes()' würde technisch funktionieren. PS: Wenn das Filterergebnis nicht durch einen jQuery-Selektor bestimmbar ist, können Sie 'is()' leider nicht verwenden. – Tomalak

1

Sie könnten versuchen, Ihre Hand auf ein jQuery-Plugin zu schreiben, dies zu tun. Überprüfen Sie den Code der Filter Funktion, und kommen Sie mit etwas, das genauer was Sie wollen. Es könnte so etwas wie sein:

$("some selector").processList(predicate, successCallback, failureCallback); 

Dann sind Sie in drei Rückrufe passieren würde: eine, die ein Objekt auswertet, um zu sehen, ob es die Filterauswahl passt (Sie können auch einen Selektor-String oder dergleichen annehmen könnte); eine, die Objekte behandelt, die mit der Auswahl übereinstimmen, und eine, die Objekte behandelt, die nicht übereinstimmen.

+0

Ja, so etwas habe ich mir vorgestellt. :) Wird nicht zu kompliziert sein, aber ich wollte das Rad nicht neu erfinden. – Tomalak

+0

Ja, tut mir leid. Ich bin mir nicht sicher, ob so etwas schon existiert. Wenn Sie Hilfe benötigen, herauszufinden, wie man das Plugin schreibt, lassen Sie es uns wissen =) – RMorrisey

+0

Danke. ^^ Ich denke, das Plugin, das dazu in der Lage wäre, ist ein Kinderspiel. Ich versuche nur sicherzustellen, dass ich nicht etwas cleveres verpasse, das bereits in jQuery eingebaut ist. – Tomalak

1

Ich weiß nicht, ob dies irgendwelche schöner, aber filter() verwenden Sie so etwas wie tun könnte:

var $others = $(); 

var $filtered = $('div').filter(function() { 
    if(! your filter test) { 
     $others.push(this); 
    } else { 
     return true; 
    } 
}); 

alert($others.length); 
alert($filtered.length); 

EDIT:

Zuerst versuchte ich es mit einem leeren jQuery Satz beginnen $(), und dann mit add(), um es mit den Nicht-Filter-Ergebnissen zu füllen, konnte es aber nicht funktionieren.

EDIT:

aktualisiert Push-to verwenden, um direkt auf ein leeres Objekt jQuery wie Tomalak vorgeschlagen.

+0

Hm, das wäre eine Möglichkeit, es zu tun. Nicht gerade glatt, aber machbar. :) +1 (PS: jQuery ist ein Array, Sie können 'push()' direkt darauf verwenden) – Tomalak

+0

Danke, ja, es wäre ein bisschen schöner, wenn ich einen besseren Weg kennen würde, um die "anderen" jQuery-Sets zu füllen Der Filter. – user113716

+0

@Tomalak - habe gerade Ihren Kommentar zur Verwendung von 'push()' direkt auf dem jQuery-Objekt bemerkt. Macht Sinn, also habe ich meine Antwort aktualisiert. Vielen Dank. – user113716

10

Normalerweise verwende ich not dafür - es kann eine Reihe von Elementen nehmen und sie aus der Auswahl entfernen, Sie mit dem Komplement zu verlassen:

var all = $("some selector"); 
var filtered = all.filter(function() { 
    // determine result... 
    return result; 
}); 
var others = all.not(filtered); 
+0

Nice one. Mir war nicht bewusst, dass 'not()' auch so funktioniert. +1 – Tomalak

+0

+1 - Besser als meins. Danke für den Tipp. – user113716

+0

Sehr elegant in der Tat. – Bora

1
$.fn.if = function(cond, ontrue, onfalse) { 
    this.each(function() { 
    if (cond.apply(this)) ontrue.apply(this); 
    else onfalse.apply(this); 
    }); 
}; 

$('some selector').if(function() { 
    // determine result 
}, function() { 
    // do something 
}, function() { 
    // do something else 
}); 

ich nicht sicher bin, ist viel mehr lesbar, als wenn man jedes Mal manuell ein if eingibt.

+0

Sie müssten den Index als Argument für 'cond.apply()' hinzufügen, da jQuery selbst den aktuellen Index den Filterfunktionen zuführt. Und natürlich müssten Sie für die Argumente "cond, ontrue, onfalse" Typprüfungen hinzufügen. Ansonsten dachte ich daran. Sobald Sie 5 Methoden in jQuery verketten, leidet die Lesbarkeit sowieso. ;) – Tomalak

Verwandte Themen