Obtener un objeto de un NSSet

93

Si no puede obtener un objeto con objectAtIndex: de un NSSet, ¿cómo recupera los objetos?

ninja del nodo
fuente
1
[[nsSetObjects allObjects] objectAtIndex: anyInteger]
Mubeen Qazi

Respuestas:

139

Hay varios casos de uso para un conjunto. Puede enumerar (por ejemplo, con enumerateObjectsUsingBlocko NSFastEnumeration), llamar containsObjectpara probar la membresía, usar anyObjectpara obtener un miembro (no aleatorio) o convertirlo en una matriz (sin ningún orden en particular) con allObjects.

Un conjunto es apropiado cuando no desea duplicados, no le importa el orden y desea pruebas rápidas de membresía.

Matthew Flaschen
fuente
7
También puede buscar un objeto conocido por un posible duplicado enviando un member:mensaje al conjunto . Si regresa nil, el conjunto no contiene un objeto igual al que pasó; si devuelve un puntero de objeto, entonces el puntero que devuelve es al objeto que ya está en el conjunto. Los objetos del conjunto deben implementar hashy isEqual:para que esto sea útil.
Peter Hosey
@PeterHosey No creo que hash las necesidades que deban aplicarse; iría mucho más rápido si lo hicieras.
fumoboy007
2
@ fumoboy007: Para almacenarlo en un conjunto o usarlo como clave en un diccionario, sí lo hace. De la documentación del hashprotocolo NSObject: "Si dos objetos son iguales (según lo determinado por el isEqual:método), deben tener el mismo valor hash". Actualmente, las implementaciones de NSObject hashy isEqual:utilizan la identidad (dirección) del objeto. Si anula isEqual:, está configurando la posibilidad de objetos que no son idénticos pero sí iguales, que, si no anula también hash, seguirán teniendo valores hash diferentes. Eso viola el requisito de que los objetos iguales tengan valores hash iguales.
Peter Hosey
1
@ fumoboy007: Considere cómo funciona una tabla hash: el contenedor tiene una matriz de una cierta cantidad de cubos y usa el hash de cada objeto entrante para determinar en qué cubo debe estar ese objeto. Para búsquedas como member:, el contenedor solo buscará en ese bucket (razón por la cual los conjuntos son mucho más rápidos que las matrices en las pruebas de membresía y los diccionarios son mucho más rápidos que las matrices paralelas en la búsqueda de valores clave). Si el objeto que se busca tiene el hash incorrecto, el contenedor buscará en el cubo incorrecto y no encontrará una coincidencia.
Peter Hosey
@PeterHosey Vaya ... Pensé erróneamente que la implementación predeterminada de -[NSObject hash]era 0. Eso explica muchas cosas. = S
fumoboy007
31

NSSet no tiene un método objectAtIndex:

Intente llamar a allObjects que devuelve un NSArray de todos los objetos.

Nadie en particular
fuente
¿Tiene alguna idea de si la matriz devuelta está ordenada? en otras palabras, si estoy agregando objetos al conjunto usando "setByAddingObject", y usé "allObjects", ¿verdad? los elementos de la matriz ordenados en el orden en que agregué los objetos?
iosMentalist
simplemente ordene la matriz resultante con un NSSortPredicate y estará bien
Jason
Si solo está interesado en un objeto específico, es mejor usar el enfoque filterSetUsingPredicate.
akw
17

Es posible usar filterSetUsingPredicate si tiene algún tipo de identificador único para seleccionar el objeto que necesita.

Primero cree el predicado (asumiendo que su identificación única en el objeto se llama "identificador" y es un NSString):

NSPredicate *myPredicate = [NSPredicate predicateWithFormat:@"identifier == %@", identifier];

Y luego elija el objeto usando el predicado:

NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;
Vitalii
fuente
13

NSArray *myArray = [myNSSet allObjects];

MyObject *object = [myArray objectAtIndex:(NSUInteger *)]

reemplace NSUInteger con el índice de su objeto deseado.

krischu
fuente
1
Debe ser: MyObject * object = [myArray objectAtIndex: (NSUInteger *)]
John D.
8

Para Swift3 y iOS10:

//your current set
let mySet : NSSet
//targetted index
let index : Int
//get object in set at index
let object = mySet.allObjects[index]
Kevin ABRIOUX
fuente
6

NSSet usa el método isEqual: (que los objetos que coloque en ese conjunto deben anular, además, el método hash) para determinar si un objeto está dentro de él.

Entonces, por ejemplo, si tiene un modelo de datos que define su singularidad mediante un valor de id (digamos que la propiedad es:

@property NSUInteger objectID;

entonces implementaría isEqual: as

- (BOOL)isEqual:(id)object
{
  return (self.objectID == [object objectID]);
}

y podrías implementar hash:

- (NSUInteger)hash
{
 return self.objectID;  // to be honest, I just do what Apple tells me to here
                       // because I've forgotten how Sets are implemented under the hood
}

Luego, puede obtener un objeto con esa ID (así como verificar si está en NSSet) con:

MyObject *testObject = [[MyObject alloc] init];
testObject.objectID = 5; // for example.  

// I presume your object has more properties which you don't need to set here 
// because it's objectID that defines uniqueness (see isEqual: above)

MyObject *existingObject = [mySet member: testObject];

// now you've either got it or existingObject is nil

Pero sí, la única forma de obtener algo de un NSSet es considerando lo que define su singularidad en primer lugar.

No he probado qué es más rápido, pero evito usar la enumeración porque podría ser lineal, mientras que usar el método member: sería mucho más rápido. Esa es una de las razones para preferir el uso de NSSet en lugar de NSArray.

herradura7
fuente
0
for (id currentElement in mySet)
{
    // ** some actions with currentElement 
}
Savchenko Dmitry
fuente
0

La mayoría de las veces no le importa obtener un objeto en particular de un conjunto. Le interesa probar para ver si un conjunto contiene un objeto. Para eso sirven los sets. Cuando desea ver si un objeto está en una colección, los conjuntos son mucho más rápidos que las matrices.

Si no le importa el objeto que obtenga, use el -anyObjectque solo le dé un objeto del conjunto, como poner la mano en una bolsa y agarrar algo.

Dog *aDog = [dogs anyObject]; // dogs is an NSSet of Dog objects

Si le importa el objeto que obtiene, use el -memberque le devuelva el objeto, o nil si no está en el conjunto. Necesita tener el objeto antes de llamarlo.

Dog *spot = [Dog dogWithName:@"Spot"];
// ...
Dog *aDog = [dogs member:spot]; // Returns the same object as above

Aquí hay un código que puede ejecutar en Xcode para comprender más

NSString *one = @"One";
NSString *two = @"Two";
NSString *three = @"Three";

NSSet *set = [NSSet setWithObjects:one, two, three, nil];

// Can't use Objective-C literals to create a set.
// Incompatible pointer types initializing 'NSSet *' with an expression of type 'NSArray *'
//  NSSet *set = @[one, two, three];

NSLog(@"Set: %@", set);
// Prints looking just like an array but is actually not in any order
//Set: {(
//     One,
//     Two,
//     Three
//     )}

// Get a random object
NSString *random = [set anyObject];
NSLog(@"Random: %@", random); // Random: One

// Iterate through objects. Again, although it prints in order, the order is a lie
for (NSString *aString in set) {
    NSLog(@"A String: %@", aString);
}

// Get an array from the set
NSArray *array = [set allObjects];
NSLog(@"Array: %@", array);

// Check for an object
if ([set containsObject:two]) {
    NSLog(@"Set contains two");
}

// Check whether a set contains an object and return that object if it does (nil if not)
NSString *aTwo = [set member:two];
if (aTwo) {
    NSLog(@"Set contains: %@", aTwo);
}
rey nevan
fuente