2011-04-06 14 views
6

que tiene una serie de diccionarios, similar a la siguiente:NSPredicate para que coincida con "cualquier entrada en un NSDatabase con el valor que contiene una cadena"

 
(
     { 
      Black = "?"; 
      Date = "????.??.??"; 
      Result = "*"; 
      SourceDate = "2007.10.24"; 
      White = "Mating pattern #1"; 
     }, 
     { 
      Black = "?"; 
      Date = "????.??.??"; 
      Result = "*"; 
      SourceDate = "2008.10.24"; 
      White = "About this Publication"; 
     } 
) 

Quiero ofrecer al usuario la capacidad de búsqueda de texto dentro de los campos "Blanco" y "Negro", o dentro de cualquier campo. Tengo una NSPredicate para hacer sólo los campos específicos:


    predicate = [NSPredicate 
        predicateWithFormat:@"self.Black contains[cd] %@ or self.White contains[cd] %@", 
         searchText, searchText]; 
    [filteredGames addObjectsFromArray:[games filteredArrayUsingPredicate:predicate]]; 

No puedo pensar en cómo formular un predicado que me va a devolver los diccionarios para los que cualquiera de los objetos dentro de coincidir con el texto. es decir, podría buscar "2007" y devolvería el primer diccionario pero no el segundo. Intenté "self. *", Que realmente no esperaba que funcionara y también "ANY self.allValues", con lo que tenía más esperanzas. En realidad, no sé de antemano cuáles serán las claves, por lo tanto, necesito algo menos específico.

¿Alguna sugerencia?

Respuesta

15

Si todos los diccionarios tienen el mismo conjunto de llaves, entonces se podría hacer algo bastante simple:

NSArray *keys = ...; //the list of keys that all of the dictionaries contain 
NSMutableArray *subpredicates = [NSMutableArray array]; 
for (NSString *key in keys) { 
    NSPredicate *subpredicate = [NSPredicate predicateWithFormat:@"%K contains[cd] %@", key, searchText]; 
    [subpredicates addObject:subpredicate]; 
} 
NSPredicate *filter = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; 

entonces usted puede utilizar para filtrar su filterNSArray (usando -filteredArrayUsingPredicate).

Si, por el contrario, tiene una serie de diccionarios arbitrarias que todos tienen diferentes claves, que había necesidad de algo un poco más perversa:

NSPredicate *filter = [NSPredicate predicateWithFormat:@"SUBQUERY(FUNCTION(SELF, 'allKeys'), $k, SELF[$k] contains[cd] %@)[email protected] > 0", searchText]; 

Un poco acerca de lo que está haciendo esto:

  • FUNCTION(SELF, 'allKeys') - esto se ejecutará en -allKeysSELF (un NSDictionary) y devolver una NSArray de todas las teclas en el diccionario
  • SUBQUERY(allKeys, $k, SELF[$k] contains[cd] %@) - Esto iterará sobre cada elemento en allKeys, con cada artículo sucesivo colocado en la variable $k. Para cada artículo, se ejecutará SELF[$k] contains %@. Esto básicamente terminará haciendo: [theDictionary objectForKey:$k] contains[cd] %@. Si esto devuelve YES, el elemento $k se agregará en una nueva matriz.
  • SUBQUERY(...)[email protected] > 0 - después de encontrar todas las claves que corresponden a los valores que contienen su texto de búsqueda, verificamos y vemos si hubo alguna. Si hubiera (es decir, el tamaño de la matriz es mayor que 0), entonces el diccionario general será parte de la matriz filtrada final.

Recomiendo ir con la primera aproximación, si es posible. SUBQUERY y FUNCTION son un poco arcanas, y la primera es mucho más fácil de entender.


Y aquí hay otra forma, que casi tenía en su pregunta. En lugar de hacer ANY SELF.allValues contains[cd] %@, puede hacer ANY FUNCTION(SELF, 'allValues') contains[cd] %@.Esto es equivalente a mi locura SUBQUERY, pero mucho más simple. Felicitaciones a ti por pensar en usar ANY (normalmente me olvido de que existe).

EDITAR

La razón SELF.allValues no funciona, es que esto se interpreta como una ruta de acceso clave, y -[NSDictionary valueForKey:]is supposed to be the same as-[NSDictionary objectForKey:]. El problema aquí es que si prefijas la clave con @, luego lo reenvía a [super valueForKey:], que hará haciendo lo que estás esperando. Por lo que podría realmente hacer:

ANY [email protected] contains[cd] %@ 

O simplemente:

ANY @allValues contains[cd] %@ 

, siendo este punto (y es el mejor y más simple enfoque).

+0

Hola, gracias por la respuesta en profundidad :-) El conjunto de encabezados no será necesariamente el mismo entre los diccionarios, por lo que no podría usar el primer método que mencionaste. Los otros se veían bien, pero he tenido un intento con el último, por lo que puedo decir, recibo una excepción si busco algo que filtre. es decir, si busco una sola letra que esté presente en todos los diccionarios, funciona, cualquier otra cosa y falla, obtengo: *** Aplicación de terminación debido a la excepción no detectada 'NSInvalidArgumentException', razón: 'No se puede usar in/contains operador con la colección 0 (no una colección) ' – mcknut

+0

@mcknut ¿Puede poner más información en la pregunta sobre los datos en los diccionarios, y también un ejemplo específico de cómo arroja esa excepción? –

+0

la excepción se da si busco "!" que sería bastante raro en los diccionarios. De hecho, estoy pensando que "lo estoy haciendo mal" y que debería pasar a usar Core Data. En ese escenario, almacenaría las claves y valores en su propia entidad con una referencia de regreso a la entidad principal y luego buscaría las entidades de clave y valor para buscar coincidencias, creo que sería lo mejor. – mcknut

0

si desea que coincida con un objeto en el diccionario de la Usted puede hacer esto mediante el uso de un bucle, pero puede ser una tarea que consume tiempo -

for(int i=0; i< [array count]; i++) 
{ 

    NSDictionary *dic = [array objectAtIndex:i]; 

    if([[dic objectForKey:@"Black"] isEqualToString:@"2007"]) 
    { 
    //here is the match 
    } 
} 
+0

Hola, gracias pero realmente esperaba evitar hacer esto. – mcknut

Cuestiones relacionadas