2010-05-09 8 views
13

En jQuery, filter() reduce el resultado a aquellos elementos que cumplen una condición determinada.jQuery: utilice el filtro(), pero trabaje con ambos resultados

Esto divide la lista en dos partes. Trabajar con el "buen medio" de los elementos es fácil:

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

Pero cómo puedo trabajar con la "otra mitad" de mis elementos, también - pero sin hacer el equivalente de este:

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

Básicamente, me gustaría alimentar dos piezas separadas /* do something */ a un solo filtro. Una para aquellos que coinciden, y otra para los demás, sin tener que filtrar dos veces. ¿Me falta una función jQuery que hace esto?


P.S .: supongo que podía hacer:

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

pero esperaba algo mejor.

+0

Creo que será 'each()'. – Matt

+2

http://jsfiddle.net/nZtRF/ Una cosa que vale la pena señalar es que con el método 'not()', el rendimiento se degrada exponencialmente a medida que aumenta el número de elementos. Para cantidades más pequeñas, es trivial. Para grandes cantidades, obtendrá un mejor rendimiento utilizando una declaración 'if()' en el filtro. Consulte la prueba en el enlace jsfiddle que se encuentra arriba y ligue con las cantidades. – user113716

+0

@patrick: Muy cierto. Eso es un serio inconveniente. : - \ – Tomalak

Respuesta

16

El método recomendado por Kobi en forma de plugin:

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

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

Tenga en cuenta que invert() no va a añadir un nuevo elemento a la pila jQuery pero reemplace la última:

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

Editar: cambiado prevObject a end() a sugerencia de Tomalak.

+0

Dulce. :-) Me gusta mucho. – Tomalak

+0

Mi código no puede ser encadenado, y esto parece solucionarlo muy bien. +1 – Kobi

+0

¿No sería 'end()' mejor que 'prevObject'? 'prevObject' significa que debo filtrar en el paso inmediatamente anterior, mientras que' end() 'se refiere a la" operación de filtrado más reciente "de acuerdo con los documentos, lo que significa que puede estar varios pasos atrás. – Tomalak

0

Interesante pregunta. Veo que se inclina hacia lo que yo iba a sugerir:

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

Esperaba una solución de encadenamiento de métodos más jQuery; pero 'each()' funcionaría, técnicamente. PD: Si el resultado del filtro no es determinable por un selector de jQuery, desafortunadamente no puedes usar 'is()'. – Tomalak

1

que te pueden probar su mano en escribir un plugin de jQuery para hacer esto. Compruebe el código de la función de filtro , y proponga algo que haga más exactamente lo que desea. Podría ser algo como:

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

allí tendría que pasar en tres devoluciones de llamada: una que evalúa un objeto para ver si coincide con la selección del filtro (también se puede aceptar una cadena de selección, o similares); uno que maneja objetos que coinciden con la selección, y otro que maneja objetos que no coinciden.

+0

Sí, algo así es lo que tengo en mente. :) No será demasiado complicado, pero no quería volver a inventar la rueda. – Tomalak

+0

Sí, lo siento. No estoy seguro si algo así existe, ya. Si necesita ayuda para averiguar cómo escribir el complemento, avísenos =) – RMorrisey

+0

Gracias. ^^ Creo que el complemento que podría hacer esto es obvio. Solo estoy tratando de asegurarme de no perderme algo inteligente que ya está integrado en jQuery. – Tomalak

1

No sé si esto es más amable, pero utilizando filter() que podía hacer algo como:

var $others = $(); 

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

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

EDIT:

Al principio lo intenté comenzar con una serie de jQuery vacía $(), y luego usar add() para poblarlo con los resultados que no sean de filtro, pero no podría hacerlo funcionar.

EDIT:

actualizado para utilizar empuje directamente en un objeto jQuery vacío según lo sugerido por Tomalak.

+0

Hm, esa sería una forma de hacerlo. No es exactamente suave, pero viable. :) +1 (PD: jQuery es una matriz, puede usar 'push()' en ella directamente) – Tomalak

+0

Gracias, sí, sería un poco mejor si supiera una mejor manera de poblar el jQuery de 'otros' establecido en el filtro. – user113716

+0

@Tomalak: acabo de notar su comentario sobre el uso de 'push()' directamente en el objeto jQuery. Tiene sentido, así que actualicé mi respuesta. Gracias. – user113716

10

lo general el uso not para esto - que puede tomar una serie de elementos y sacarlos de su selección, dejándole con el complemento:

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

Agradable. No sabía que 'not()' funciona de esta manera también. +1 – Tomalak

+0

+1 - Mejor que el mío. Gracias por el consejo. – user113716

+0

Muy elegante de hecho. – 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 
}); 

No estoy seguro de que es mucho más menos legible que poner un si dentro de cada uno manualmente.

+0

Debería agregar el índice como argumento a 'cond.apply()' ya que jQuery mismo alimenta el índice actual para filtrar funciones. Y tendrías que agregar controles de tipo para los argumentos 'cond, ontrue, onfalse', obviamente. Aparte de eso, esto es lo que tenía en mente. Una vez que encadena 5 métodos en jQuery, la legibilidad sufre de todos modos. ;) – Tomalak

Cuestiones relacionadas