2009-05-25 17 views
7

NSArray ha metodi utili per trovare gli oggetti per gli indici specificatiGet NSIndexSet da NSArray

// To find objects by indexes 
- (id)objectAtIndex:(NSUInteger)index 
- (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes 

// To find index by object 
- (NSUInteger)indexOfObject:(id)anObject 

Tuttavia, voglio ottenere NSIndexSet (più indici) per determinati oggetti. Qualcosa di simile:

- (NSIndexSet *)indexesOfObjects:(NSArray *)objects 

Questo metodo non esiste per NSArray. Mi sto perdendo qualcosa? Qualcuno conosce un altro metodo standard? Altrimenti devo scrivere questo come un metodo di categoria.

risposta

6

Potrebbe essere utile per la sua attuazione mediante una serie di specificare gli oggetti da trovare, come ad esempio:

- (NSIndexSet *) indicesOfObjectsInSet: (NSSet *) set 
{ 
    if ([set count] == 0) 
     return ([NSIndexSet indexSet]); 

    NSMutableIndexSet * indices = [NSMutableIndexSet indexSet]; 

    NSUInteger index = 0; 
    for (id obj in self) 
    { 
     if ([set containsObject: obj]) 
      [indices addIndex: index]; 

     index++; 
    } 

    return ([[indices copy] autorelease]); 
} 

Ciò richiede la visita ogni oggetto nella matrice, ma almeno solo lo fa una volta e fa uso di enumerazione rapida mentre lo fa. L'utilizzo di un NSSet e il test di ogni oggetto nell'array rispetto a quel set è anche molto più veloce del test per l'inclusione in un array.

C'è un potenziale di ottimizzazione qui, ma sarebbe svolta nel caso in cui un singolo oggetto viene memorizzato nella matrice di ricezione più volte:

if ([set containsObject: obj]) 
{ 
    [indices addIndex: index]; 
    if ([indices count] == [set count]) 
     break; 
} 

In questo modo se si esegue la scansione di una 20'000-item array per due oggetti e sono entrambi all'interno dei primi dieci, sarete in grado di evitare la scansione degli altri 19'990 oggetti nell'array. Come ho detto, però, questo non aiuta se l'array contiene duplicati, perché si fermerà non appena troverà 2 indici (anche se entrambi puntano allo stesso oggetto).

Detto questo, sono d'accordo con il commento di Mike sopra. È probabile che ti stai preparando per un po 'di dolore, l'ottimizzazione-tempo. Potrebbe valere la pena di pensare a diversi tipi di dati; per esempio, mentre NSArray sembra la scelta più logica per un semplice contenitore piatto, se non hai realmente bisogno delle informazioni per l'ordinazione, è preferibile utilizzare un NSSet; questo ha il vantaggio aggiunto che non memorizzerà lo stesso oggetto (calcolato usando -isEqual:) due volte. Se si desidera tenere traccia dei duplicati, ma non è necessario ordinare, è possibile utilizzare NSCountedSet, che si comporta come NSSet tranne che tiene traccia di quante volte ogni oggetto è stato aggiunto/rimosso senza effettivamente memorizzare i duplicati.

+2

+1 Solo una nota minore: sia "indici" che "indici" sono corretti in inglese, ma Cocoa usa sempre "indici", quindi è meglio attenersi a quella terminologia, almeno per il nome del metodo. –

1

È necessario implementare la propria categoria, per quanto posso vedere.

+2

Tuttavia, notare che il desiderio di questo metodo è un segno forte di un difetto di progettazione. -indexOfObject: funziona cercando ogni oggetto nell'array e diventa quindi piuttosto lento per un array di grandi dimensioni o per più ricerche. Ripensa le tue strutture dati per qualcosa di più sensato. –

13

Le versioni NSArray più recenti (OSX 10.6 e iOS 4) forniscono il metodo indexesOfObjectsPassingTest:.

NSIndexSet *indexesOfObjects = [[array1 indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { 
    return [array2 containsObject:obj]; 
}];