Getting an object from an NSSet
Asked Answered
U

8

97

If you can't get an object with objectAtIndex: from an NSSet then how do you retrieve objects?

Umbelliferous answered 30/9, 2010 at 0:7 Comment(1)
[[nsSetObjects allObjects] objectAtIndex: anyInteger]Goodyear
B
144

There are several use cases for a set. You could enumerate through (e.g. with enumerateObjectsUsingBlock or NSFastEnumeration), call containsObject to test for membership, use anyObject to get a member (not random), or convert it to an array (in no particular order) with allObjects.

A set is appropriate when you don't want duplicates, don't care about order, and want fast membership testing.

Beige answered 30/9, 2010 at 0:12 Comment(5)
You can also look up a known object by a potential duplicate by sending the set a member: message. If it returns nil, the set does not contain an object equal to the one you passed; if it returns an object pointer, then the pointer it returns is to the object already in the set. The objects in the set must implement hash and isEqual: for this to be useful.Hoi
@PeterHosey I don't think hash needs to be implemented; it would just go a lot faster if you did do that.Coxswain
@fumoboy007: For storage in a set or use as a key in a dictionary, yes it does. From the documentation of hash in the NSObject protocol: “If two objects are equal (as determined by the isEqual: method), they must have the same hash value.” Currently, NSObject's implementations of hash and isEqual: use the object's identity (address). If you override isEqual:, you're setting up the possibility of objects that are not identical but are equal—which, if you don't also override hash, will still have different hashes. That violates the requirement that equal objects have equal hashes.Hoi
@fumoboy007: Consider how a hash table works: The container has an array of some number of buckets, and uses each incoming object's hash to determine which bucket that object should be in. For lookups such as member:, the container will only look in that one bucket (which is why sets are so much faster than arrays at membership testing and dictionaries are so much faster than parallel arrays at key-value lookup). If the object being sought has the wrong hash, then the container will look in the wrong bucket, and not find a match.Hoi
@PeterHosey Oops…I mistakenly thought that the default implementation of -[NSObject hash] was 0. That explains a lot. =SCoxswain
W
32

NSSet doesn't have a method objectAtIndex:

Try calling allObjects which returns an NSArray of all the objects.

Winery answered 30/9, 2010 at 0:12 Comment(3)
do you have any idea if the array returned is ordered? in other words, if im adding objects to the set using "setByAddingObject", and i used "allObjects", are? the elements in the array ordered in the order I added the objects?Bunkum
just sort the resulting array with an NSSortPredicate and you'll be fineSlipcase
If you're only interested in one specific object, better use the filteredSetUsingPredicate approach.Viera
N
17

it is possible to use filteredSetUsingPredicate if you have some kind of unique identifier to select the object you need.

First create the predicate (assuming your unique id in the object is called "identifier" and it is an NSString):

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

And then choose the object using the predicate:

NSObject *myChosenObject = [mySet filteredSetUsingPredicate:myPredicate].anyObject;
Neuburger answered 4/8, 2013 at 14:20 Comment(0)
H
14

NSArray *myArray = [myNSSet allObjects];

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

replace NSUInteger with the index of your desired object.

Hydrogenize answered 4/3, 2013 at 6:20 Comment(1)
Should be: MyObject *object = [myArray objectAtIndex:(NSUInteger *)]Scrape
P
9

For Swift3 & iOS10 :

//your current set
let mySet : NSSet
//targetted index
let index : Int
//get object in set at index
let object = mySet.allObjects[index]
Paddlefish answered 28/11, 2016 at 10:52 Comment(0)
K
6

NSSet uses the method isEqual: (which the objects you put into that set must override, in addition, the hash method) to determine if an object is inside of it.

So, for example if you have a data model that defines its uniqueness by an id value (say the property is:

@property NSUInteger objectID;

then you'd implement isEqual: as

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

and you could implement 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
}

Then, you can get an object with that ID (as well as check for whether it's in the NSSet) with:

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

But yeah, the only way to get something out of a NSSet is by considering that which defines its uniqueness in the first place.

I haven't tested what's faster, but I avoid using enumeration because that might be linear whereas using the member: method would be much faster. That's one of the reasons to prefer the use of NSSet instead of NSArray.

Kollwitz answered 19/4, 2012 at 20:32 Comment(0)
G
0
for (id currentElement in mySet)
{
    // ** some actions with currentElement 
}
Gilolo answered 20/6, 2016 at 21:31 Comment(0)
B
0

Most of the time you don't care about getting one particular object from a set. You care about testing to see if a set contains an object. That's what sets are good for. When you want to see if an object is in a collection sets are much faster than arrays.

If you don't care about which object you get, use -anyObject which just gives you one object from the set, like putting your hand in a bag and grabbing something.

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

If you care about what object you get, use -member which gives you back the object, or nil if it's not in the set. You need to already have the object before you call it.

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

Here's some code you can run in Xcode to understand more

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);
}
Buchmanism answered 9/8, 2018 at 13:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.