2009-05-25 13 views
7

NSArray tiene métodos útiles para encontrar objetos para los índices especificadosNSIndexSet de NSArray

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

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

Sin embargo, quiero conseguir NSIndexSet (varios índices) para los objetos dados. Algo así como:

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

Este método no existe para NSArray. ¿Me estoy perdiendo de algo? ¿Alguien sabe otro método estándar? De lo contrario, tengo que escribir esto como un método de categoría.

Respuesta

6

Podría ser útil para ponerlo en práctica utilizando un conjunto para especificar los objetos para encontrar, tales como:

- (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]); 
} 

Para ello es necesario visitar cada objeto de la matriz, pero por lo menos sólo lo hace una vez y hace uso de enumeración rápida mientras lo hace. El uso de un NSSet y la prueba de cada objeto en la matriz contra ese conjunto también es mucho más rápido que la prueba de inclusión en una matriz.

Hay una optimización potencial aquí, pero se rompería en el caso en que un solo objeto se almacena en la matriz de recepción varias veces:

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

De esta forma si está escaneando un elemento 20'000 una matriz para dos objetos y ambos están dentro de los primeros diez, podrá evitar escanear los otros 19'990 objetos en la matriz. Como dije, eso no ayuda si la matriz contiene duplicados, porque se detendrá tan pronto como se encuentren 2 índices (incluso si ambos apuntan al mismo objeto).

Habiendo dicho eso, estoy de acuerdo con el comentario anterior de Mike. Lo más probable es que estés preparándote para algún dolor, ven al tiempo de optimización. Puede valer la pena pensar en diferentes tipos de datos; por ejemplo, aunque NSArray parece la opción más lógica para un contenedor plano simple, si en realidad no necesita la información de pedido, es mejor usar un NSSet; esto tiene la ventaja adicional de que no almacenará el mismo objeto (calculado usando -isEqual:) dos veces. Si desea realizar un seguimiento de los duplicados, pero no necesita ordenarlos, puede usar NSCountedSet, que se comporta como NSSet, excepto que realiza un seguimiento de cuántas veces se han agregado/eliminado cada objeto sin almacenar realmente los duplicados.

+2

+1 Solo una pequeña nota: tanto los "índices" como los "índices" son correctos en inglés, pero Cocoa siempre usa "índices", por lo que es mejor mantener esa terminología, al menos para el nombre del método. –

1

Tienes que implementar tu propia categoría, hasta donde puedo ver.

+2

Tenga en cuenta que querer este método es una fuerte señal de un defecto de diseño. -indexOfObject: funciona buscando a través de cada objeto en la matriz y, por lo tanto, se vuelve bastante lento para una matriz grande o para múltiples búsquedas. Repensa tus estructuras de datos para algo más sensato. –

13

Las versiones más nuevas de NSArray (OSX 10.6 y iOS 4) proporcionan el método indexesOfObjectsPassingTest:.

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