2010-12-15 24 views
7

Tengo dos matrices, una NSMutableArray y una NSArray. NSMutableArray es la "tienda", almacena los resultados de una fuente de NSArrays. Cada 5 minutos, entra un nuevo NSArray y los datos deben filtrarse y ordenarse.Combinar NSMutableArray con un NSArray, filtrando los duplicados

Ordenar por fecha es bastante fácil, así que pude obtener el NSArray ordenado por NSDate. La ordenación de la otra matriz no es necesaria, ya que solo causaría confusión con el usuario.

Lo que quiero hacer: el NSArray tiene una gran cantidad de objetos diferentes a los que todos responden - [nombre del objeto], devolviendo un NSString. El NSArray debe combinarse en NSMutableArray, agregando solo nuevos objetos.

La fusión en sí misma no es un problema, pero el rendimiento sí lo es. NSMutableArray puede contener hasta 3000 elementos, y NSArray puede contener hasta 250 elementos, aunque normalmente solo 5 o 6 de ellos deben fusionarse en NSMutableArray.

Entonces, mi pregunta es: ¿cómo se fusionan dos matrices en Objective-C, filtrando los duplicados, sin iterar (250 * 3000) veces?

Tom

Editado para aclarar algo
Los objetos "duplicar" son objetos que están duplicados para el usuario, pero no en el código. Tienen el mismo nombre, pero no la misma dirección.

Más aclaración: @"value" != @"value" // true

Respuesta

10

¿Es name una propiedad de los objetos que se almacenan en las matrices? De ser así, podría usar un NSPredicate bastante simple para filtrar el conjunto inmutable antes de agregar los resultados al mutable. He aquí un ejemplo:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NONE name == %@.name", mutableArray]; 
resultsArray = [immutableArray filteredArrayUsingPredicate:predicate]; 
[mutableArray addObjectsFromArray:immutableArray]; 
+0

Esta solución se ve bien, pero probablemente sea un poco menos comedora de rendimiento que simplemente iterar a través de todos los objetos manualmente. Actualmente estoy usando esto como una solución temporal, hasta que haya implementado una mejor manera de hacerlo. –

+2

Por alguna razón oscura, está planteando una excepción (iOS7): 'El lado izquierdo de un operador ALL o ANY debe ser un NSArray o un NSSet.' Funciona bien al invertir el orden de la cadena de predicados: @ "NONE% @. Name == name" –

0

editado para eliminar algunos estupidez (sobrado mucho, sin embargo)

un par de opciones:

  1. Eliminar todos los objetos coincidentes de la NSMutableArray usando removeObjectIdenticalTo . Esto requiere iterar a través de la matriz más pequeña, pero como nota son comúnmente pequeños. Entonces,

  2. añadir todos los elementos de la nueva matriz mediante addObjectsFromArray

O ... bueno, en realidad podría ser más rápido a su lugar:

  1. Iterar a través de la nueva matriz buscando coincidencias con indexOfObjectIdenticalTo, usando addObject para agregar objetos que no coincidan.

Costoso de cualquier manera, pero factible.

+0

Esto no va a hacer lo que necesito: los objetos no son idénticos, sólo el el valor de '- [nombre del objeto]' es –

0

¿Se puede usar NSSet y NSMutableSet en su lugar? Eso podría ayudar a lidiar con el problema de duplicados.

Editar:

Basado en sus comentarios, se podría utilizar un NSSet para comprobar la pertenencia de objetos de forma rápida, además de su matriz.Necesitaría un poco más de memoria, pero si no le importa eso, podría permitirle verificar muy rápido. Tendría su almacén de respaldo NSMutableArray, y luego un NSSet para realizar un seguimiento de la pertenencia a objetos. Usted mantendría la invariante que el NSMutableArray no contiene duplicados. Se podría usar un código como éste:

// Assume that arrayStore is an NSMutableArray * instance variable 
// Also, storeSet is an NSMutableSet * ivar 

- (void)addObjectsFromArray:(NSArray *)data 
{ 
    for (id item in data) { 
     if (![storeSet member:item]) { 
      // Will have to keep arrayStore sorted somehow 
      [arrayStore addObject:item]; 
      [storeSet addObject:item]; 
     } 
    } 
} 

Sólo tiene que recorrer el NSArray. No estoy seguro de cómo se implementa NSSet en la parte superior de mi cabeza, pero verificar la membresía no será una operación O (n) como lo es para una matriz no ordenada.

No es el método más eficiente, pero funciona bien con lo que ya tiene en su lugar, con modificaciones menores.

+0

NSSet es 1) Desordenado y 2) Solo funciona para agregar el mismo objeto dos veces. Dos objetos idénticos en diferentes direcciones no serán vistos por NSSet. –

+1

@Tom van der Woerdt: establece el uso de 'isEqual' para comparar objetos, por lo que si su clase anula' isEqual', puede comparar basándose en algo que no sea la ubicación de la memoria. Además, un conjunto no está ordenado, pero puede convertir un conjunto en un conjunto y ordenarlo cuando sea necesario (a menos que necesite que se ordene todo el tiempo). – mipadi

+0

Oh, no sabía sobre la parte 'isEqual', suena bien. Sin embargo, sí, deben ser ordenados todo el tiempo. –

0

Probablemente comenzaría creando una nueva matriz mutable que contenga los contenidos de su NSMutableArray y NSArray. Luego, clasifique la nueva matriz según la propiedad del nombre y luego ejecute la matriz una vez, solo extraiga los elementos únicos.

+0

Creo que reduciría ligeramente (!) La cantidad de cálculos necesarios, sí, pero NSMutableArray debería seguir siendo el mismo y solo agregar nuevos objetos. –

+0

En realidad, la reducción en el número de cálculos sería bastante dramática. De 3000 * 250 = 750,000 cálculos, llegaría a ~ 40,000 ... eso es casi una mejora de dos órdenes de magnitud. El método predicado es más limpio, pero dudo que sea mucho más rápido. Probablemente puedas mejorar un poco las cosas si escribes tu propio algoritmo de ordenamiento que podría eliminar un elemento de consideración siempre que comparase dos elementos que eran equivalentes. – ericg

0

Hay probablemente muchas maneras de mejorar drásticamente el rendimiento, pero para ser capaz de sugerir, que realmente necesitamos saber más acerca de lo que los objetos en las matrices "son": ¿Qué representan ? ¿Cómo están siendo utilizados? (Por ejemplo, son los elementos de la matriz de la tienda que se muestra en una vista de tabla?)

NSMutableDictionary, NSMutableSet, etc. podría combinarse con NSMutableArray para organizar e implementar el modelo de una manera eficiente.

Por ejemplo, supongamos que sabemos que el objeto representa a una persona: MDPerson. Una persona tiene un sexo, una fecha de nacimiento, un nombre, una identificación única y un conjunto de atributos que pueden cambiar. Dado este nivel superior de comprensión de lo que representa el objeto, sabemos que 2 personas son iguales solo si sus identificadores únicos son los mismos (en otras palabras, 2 personas diferentes pueden tener el mismo nombre, sexo y fecha de nacimiento). Digamos que su principal NSMutableArray se compone de una lista de 3000 personas. La matriz entrante se compone de 500 personas que ya están en el principal NSMutableArray. Algunas de estas instancias de 500 personas pueden tener atributos "actualizados", lo que significa que su instancia en la matriz principal debe actualizarse con esa información.

Dado que la comprensión, está claro que la lista principal debe implementarse como NSMutableDictionary en lugar de NSMutableArray. En el diccionario, la identificación única de la persona sería la clave, y su instancia de persona sería el valor de la clave. A continuación, puede recorrer la matriz de entrada de 500 personas sólo una vez:

// main dictionary is called personIDsAndPersons 

for (MDPerson *person in incomingPersons) { 
     MDPerson *existingPerson = [personIDsAndPersons objectForKey:[person uniqueID]]; 
     // if nil, the person doesn't exist 
     if (existingPerson) { 
      // update the existing person's attributes 
      [existingPerson setUniqueAttributes:[person uniqueAttributes]]; 
     } 
} 

Una vez más, sin saber más de los detalles o tener un entendimiento más elevado nivel de lo que los objetos son, en realidad sólo estamos fotografías en la oscuridad .

Menciona que 2 elementos son los mismos si tienen el mismo nombre. Entonces, ¿eso significa que cada elemento en la matriz principal de 3000 objetos tiene cada uno un nombre único? De ser así, podría usar un NSMutableDictionary para permitir el acceso a los objetos de manera eficiente al tener las claves en el diccionario como el nombre y los valores como la instancia del objeto.Luego puede usar un NSMutableArray separado que se usa meramente con fines de visualización: permite una organización ordenada y ordenada de los mismos objetos que se almacenan en el NSMutableDictionary. Recuerde que cuando agrega un objeto a una matriz o un diccionario, normalmente no está creando una nueva copia, solo está reteniendo el objeto existente.

+0

Todos los elementos son objetos inmutables, todos ellos subclasificados de una clase de "elemento" que requiere que todas las subclases implementen un método de "nombre" para permitir la verificación de duplicados. Y sí, están todos en una especie de vista de tabla. –

5

¿Qué tal esto:

[mutable removeObjectsInArray:newArray]; 
[mutable addObjectsFromArray:newArray]; 

No es el más gordo, pero es fácil de implementar :)

+0

Solo funciona si los objetos reales son los mismos. No funcionará aquí, porque una propiedad debe ser igual para tener un duplicado. –

Cuestiones relacionadas