2012-11-28 10 views
6

NSDictionary ha objectForKey ma è case-sentive per le chiavi. C'è No funzione disponibile comeNSDictionary oggetto insensibile al casoForKey:

- (id)objectForKey:(id)aKey options:(id) options; 

dove nelle opzioni è possibile passare "NSCaseInsensitiveSearch"

Per ottenere risultati chiave da NSDictionary che è una insesitive caso è possibile utilizzare il seguente codice scritto sotto.

+0

Giù voti accolti con adeguata giustificazione. – andyPaul

+0

È una domanda che ha risposto e condiviso. – andyPaul

+0

Ma, quando faccio una domanda, vedo un'opzione Rispondi alla tua stessa domanda. Quindi, questo è quello che ho fatto. A proposito, il tuo commento sull'ottenere reputazione è piuttosto sarcastico. Vedo molti stili di codifica, puoi anche provare a rispondere come ha fatto @Ramy. Per favore, specifica il tuo blog, così posso porre queste domande laggiù. – andyPaul

risposta

8

Questo non è incluso per un paio di motivi:

  1. NSDictionary usa uguaglianza hash, e per praticamente un buon algoritmo di hashing, ogni variazione nella stringa sorgente produce un hash diverso.

  2. Ancora più importante, Le chiavi NSDictionary non sono stringhe. Qualsiasi oggetto conforme a NSCopying può essere una chiave di dizionario e questo include molto più delle stringhe. Come sarebbe un confronto insensibile alle maiuscole e minuscole di un NSNumber con un NSBezierPath?

Molte delle risposte qui offrono soluzioni che equivalgono a trasformare il dizionario in un array e iterarlo su di esso. Funziona, e se hai solo bisogno di questo come una tantum, va bene. Ma quella soluzione è piuttosto brutta e ha caratteristiche di cattiva prestazione. Se questo fosse qualcosa di cui avevo bisogno (diciamo, abbastanza per creare una categoria NSDictionary), vorrei risolverlo correttamente, a livello di struttura dei dati.

Ciò che si desidera è una classe che racchiude un NSDictionary, consente solo stringhe per le chiavi e automaticamente le lettere minuscole come vengono fornite (e possibilmente anche memorizza la chiave originale se è necessaria una mappatura a due vie). Questo sarebbe abbastanza semplice da implementare ed è un design molto più pulito. È troppo pesante per una tantum, ma se questo è qualcosa che stai facendo molto, penso che valga la pena di fare in modo pulito.

+0

perfetto, questa è davvero una bella risposta. Sono d'accordo sul fatto che objectForKey non sia sempre NSString, ma che è più comunemente usato. Penso che questo accada quando viene chiamato objectForKey, il sistema calcola l'hash per la chiave passata alla funzione, quindi esegue un ciclo for per abbinare l'hash con l'hash disponibile e restituire il valore quando viene trovata una corrispondenza. per favore correggimi se sbaglio. – andyPaul

+0

@andyPaul: È un po 'più complicato di così. Questo algoritmo richiederebbe un tempo lineare per trovare un valore per una chiave (ad esempio ogni voce aggiuntiva nel dizionario aggiunge una certa quantità di sovraccarico che richiede in media più tempo per abbinare chiavi e valori), ma NSDictionary rimane veloce anche con centinaia di migliaia di oggetti. Ma l'idea centrale, che funziona confrontando gli hash, è esattamente giusta. (Non sto facendo il vago solo per essere difficile, a proposito. NSDictionary non è una classe, ma molte classi diverse con diverse implementazioni che sembrano tutte uguali dall'esterno.) – Chuck

+0

Il minuscolo non è la trasformazione corretta - è necessario a caso piega. – alastair

1

Nel codice scritto di seguito, cerco un tasto effettivo per un tasto di immissione. Quindi, se input key = @ "naMe", quindi la chiave effettiva = @ "nome".

NSDictionary *dic=[NSDictionary dictionaryWithObjectsAndKeys:@"John",@"Name",@"123456",@"empId", nil]; 


NSString *[email protected]"naMe"; 
NSString *name=[dic objectForKey:key]; 

if(name==nil){ 
    NSPredicate *searchPred=[NSPredicate predicateWithFormat:@"self LIKE[cd] %@",key]; 
    NSArray *searchedKeys=[[dic allKeys] filteredArrayUsingPredicate:searchPred]; 

    if(searchedKeys.count>0){ 
     name=[dic objectForKey:[searchedKeys objectAtIndex:0]]; 

    } 
} 

NSLog(@"Name = %@",name); 
+0

Puoi anche fare così. http://stackoverflow.com/questions/6135000/case-insensitive-search-in-nsmutabledictionary –

+0

Questo è complicato e lento; inizia con la costruzione di un array contenente tutte le chiavi nel dizionario 'O (n)', quindi deve analizzare il 'NSPredicate' e filtrare l'array risultante. – alastair

12

È necessario aggiungere Categoria di NSDictionary classe con questa funzionalità

- (id)objectForCaseInsensitiveKey:(NSString *)key { 
    NSArray *allKeys = [self allKeys]; 
    for (NSString *str in allKeys) { 
     if ([key caseInsensitiveCompare:str] == NSOrderedSame) { 
      return [self objectForKey:str]; 
     } 
    } 
    return nil; 
} 
+0

Questa è una buona idea, di creare una categoria. Ma, perché hai bisogno di scrivere un ciclo for. Si può usare la funzione prontamente disponibile che usa NSPredicate. – andyPaul

+0

Sì, hai ragione. @andyPaul – Siddiq

+8

...e hai appena superato la complessità della ricerca da 'O (log (N)))' a solo 'O (N)' – ivanzoid

0

Molte risposte sono corrette, ma ecco un altro esempio:

NSDictionary* dict= @{ @"hello" : @"Hey" }; 
    NSArray* keys= [dict allKeys]; 
    NSUInteger index=[keys indexOfObjectPassingTest: ^BOOL (id obj, NSUInteger index, BOOL* stop) 
    { 
     if([obj caseInsensitiveCompare: @"Hello"]==NSOrderedSame) 
     { 
      *stop= YES; 
      return YES; 
     } 
     else 
     { 
      return NO; 
     } 
    }]; 

Personalmente trovo questo metodo più facile, ma ognuno ha il suo stile di programmazione.

EDIT

Una soluzione meno leggibile, ma più breve:

NSDictionary* dict= @{ @"hello" : @"Hey" }; 
    NSArray* keys= [dict allKeys]; 
    NSUInteger index=[keys indexOfObjectPassingTest: ^BOOL (id obj, NSUInteger index, BOOL* stop) 
    { 
     return *stop= [obj caseInsensitiveCompare: @"Hello"]==NSOrderedSame ; 

    }]; 
+0

guarda il numero di linee che il codice che hai scritto prende. Ma sì, puoi scrivere l'intero blocco in una riga, ma sarà molto difficile da capire. – andyPaul

+0

Ci sono molte linee, ma la quantità di codice è molto piccola. Non vorrei tornare [obj caseInsensitiveCompare: @ "Hello"] == NSOrderedSame solo perché prima di restituire il valore devo impostare * stop su YES, in modo che la singola riga sia come dici tu, difficilmente leggibile. –

+0

fantastico, ho trovato molti stili di codifica per la mia risposta. – andyPaul

0

se sono solo la memorizzazione in, e il recupero da, il NSDictionary in un unico luogo (forse due o tre), è possibile utilizzare

[myString lowercaseString]

in entrambi. Le risposte più rigorose sono utili se l'oggetto dizionario è utilizzato su tutto il codice.

+0

Questa è in realtà una risposta molto migliore rispetto alla maggior parte di quelli più lunghi sopra. L'unico errore è che il minuscolo non funzionerà come previsto in tutti i casi (dovresti davvero piegare la stringa al posto del caso). – alastair

1

La risposta corretta è che è necessario utilizzare i tasti piegati sul caso come tasti del dizionario. Questo non equivale a convertirli in maiuscolo o minuscolo e non distruggerà la ricerca del caso medio O inserendo la complessità.

Sfortunatamente, Cocoa non sembra avere un metodo appropriato NSString per piegare a caso una stringa, ma Core Foundation ha CFStringFold() che è possibile utilizzare a tale scopo. Scriviamo una breve funzione per fare il lavoro necessario:

NSString *foldedString(NSString *s, NSLocale *locale) 
{ 
    CFMutableStringRef ret = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, 
                (__bridge CFStringRef)s); 
    CFStringNormalize(ret, kCFStringNormalizationFormD); 
    CFStringFold(ret, kCFCompareCaseInsensitive, (__bridge CFLocaleRef)locale); 
    return (__bridge_transfer NSString *)ret; 
} 

Nota che il locale argomento è importante. Se si specifica NULL, verrà visualizzata la locale di sistema corrente. Ciò andrà bene nella maggior parte dei casi, ma gli utenti turchi potrebbero essere sorpresi che "I" corrisponda a "i" anziché a "ı". Si pertanto si desidera passare [NSLocale currentLocale] e se si sta salvando i risultati si anche si desidera salvare l'identificatore di impostazioni internazionali e creare l'impostazione internazionale da quello.

Così, quando si aggiunge al dizionario, è ora necessario fare

[dict setObject:obj forKey:foldedString(myKey, locale)]; 

e per cercare di nuovo

[dict objectForKey:foldedString(myKey, locale)]; 

Un'ultima osservazione è che si potrebbe desiderare di archiviare il caso-piegato i tasti accanto ai valori originali, quindi non devi piegarli su ogni accesso al dizionario.