I'm declaring a variable just to limit the number of results filtering a NSDictionary. Is there any other way to do this avoiding the extra variable?
My code:
//Pick one part from each item
__block int partsCounter = 0;
NSSet *itemsParts = [self.deckDictionary keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) {
//filter with itemRef value
CollectiblePart* part = (CollectiblePart*)obj;
if([candidatesSet containsObject:[NSNumber numberWithInt: part.itemRef]]){
if(partsCounter < quantity)
{
partsCounter++;
return YES;
}else{
//stop the filtering
return (*stop = NO);
}
}else{
return NO;
}
}];
No, that's the best way to do it.
While the method allows the block to be exited mid way through, it has no idea why it would have to do that, and as such doesn't accommodate any aids in doing so.
In your semantics, you need to keep a tally to keep track if something exceeds a particular threshold. Having a variable to do that makes sense.
Related
I have an NSMutableArray of NSNumbers, I want to enumerate through all of them with Objective-C styled enumeration. Here's what I've done so far.
for ( NSNumber* number in array )
{
//some code
}
I want to be able to recognize the first object fast, I am able to do this of course,
if ( [array indexOfObject:number] == 0 )
{
//if it's the first object
}
Is there any way to do this faster? There's of course the old-fashioned C style way, and remove the object from array first, and then put it back after enumeration. Just want to know if there's a better technic.
You can try using a method that provides the index of the object currently being enumerated:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (idx == 0) {
// this is the first object
}
}];
Or if you simply want to access the first object of an array:
id obj = [array objectAtIndex:0];
or with the new Objective-C style/syntax:
id obj = array[0];
This solution is faster than accessing and comparing the first array element:
BOOL firstIteration = YES;
for (NSNumber *number in array) {
if (firstIteration) {
// Handle first iteration
firstIteration = NO;
}
// Do something
}
In fast enumeration you cant alter the array. So if you want to remove you have to use old style for(;;) loop.
To find the first object simply use [array objectAtIndex:0]
I've been trying to figure out a way of checking how many of a certain object are in an NSArray.
I've looked through the docs and I'm pretty sure there is no premade method for this. Also I can't find anything here on SO.
Do anybody know about a good way to do this? Because I seriously can't come up with anything.
In this specific case I have an array with strings (most cases several of each) and I want to count how many strings in the array that matches to whatever I ask for.
If this is a primary use of the data structure and order doesn't matter, consider switching to an NSCountedSet which is specifically for solving this problem efficiently.
If you need an ordered collection, and you don't have a huge set of objects, than the fast enumeration answers are the best approach.
If you want to know where the objects are, then use indexesOfObjectsPassingTest:.
If you have a huge number of object, I would look at indexesOfObjectsWithOptions:passingTest: with the NSEnumerationConcurrent option. This will allow you to search the array on multiple cores. (This is only possibly faster on a multi-core device, and even then is probably only faster if you have a very large collection. You should absolutely test before assuming that concurrent will be faster.) Even if you just need the final count, it may be faster for certain data sets to use this method and then use count on the final index set.
There actually is a method for this: - (NSIndexSet *)indexesOfObjectsPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:^(id obj, NSUInteger index, BOOL *stop) {
return [obj isEqualTo:myOtherObject];
}];
Sounds like a case for NSCountedSet, which does what you are after with its initWithArray: initializer:
// Example array of strings
NSArray *array = [NSArray arrayWithObjects:
#"Joe", #"Jane", #"Peter", #"Paul",
#"Joe", #"Peter", #"Paul",
#"Joe",
#"Jane", #"Peter",
nil];
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray: array];
// for-in will let you loop over the counted set
for (NSString *str in countedSet) {
NSLog(#"Count of %#: %ld", str, (long)[countedSet countForObject:str]);
}
One approach would be to iterate and check.
- (int)repeatsOf:(NSString *)repeater inArray:(NSArray *)array {
int count = 0;
for (NSString *item in array) {
if ([item isEqualToString:repeater]) {
count++;
}
}
return count;
}
You could try a simple loop. Suppose needle is your reference string and array is your NSArray of strings:
unsigned int n = 0;
for (NSString * str in array)
{
if ([needle isEqualToString:str])
{
++n;
}
}
Now n holds the count of strings in equal to needle.
You could define a function like this:
- (int)countStringsThatMatch:(NSString*)match inArray:(NSArray*)array
{
int matches = 0;
for (id string in array) {
if ([string isEqualToString:match]) {
matches++;
}
}
return matches;
}
And then use it like:
int count = [self countStringsThatMatch:#"someString" inArray:someArray];
- (NSUInteger) objectCountInArray:(NSArray *)array
matchingString:(NSString *)stringToMatch {
NSUInteger count = 0;
for (NSString *string in array) {
count += [string isEqualToString:stringToMatch] ? 1 : 0;
}
return count;
}
You can try to expand this to use a block that gets an object and returns a BOOL. Then you can use it to compare an array of whatever you want.
I can grab a random value from an array-like structure by retrieving a random index.
How can I grab a random value from an NSSet object that stores NSNumber objects? I couldn't find an instance method of NSSet that retrieves a random value.
In short, you can't directly retrieve a random object from an NSSet.
You either need to turn the set into an array -- into something that has an index that can be randomized -- by re-architecting your code to use an array or you could implement this using this bit of pseudo-code:
randomIndex = ...random-generator....(0 .. [set count]);
__block currentIndex = 0;
__block selectedObj = nil;
[set enumerateObjectsWithOptions:^(id obj, BOOL *stop) {
if (randomIndex == currentIndex) { selectedObj = obj; *stop = YES }
else currentIndex++;
}];
return selectedObj;
Yes -- it iterates the set, potentially the whole set, when grabbing the object. However, that iteration is pretty much what'll happen in the conversion to an NSArray anyway. As long as the set isn't that big and you aren't calling it that often, no big deal.
Whilst I like that #bbum answer will terminate early on some occasions due to the use of stop in the enumeration block.
For readability and ease of remembering what is going on when you revisit this code in the future I would go with his first suggestion of turn the set into an array
NSInteger randomIndex = ..random-generator....(0 .. [set count])
id obj = [set count] > 0 ? [[set allObjects] objectAtIndex:randomIndex] : nil;
I'm currently trying to get hold of my NSMutableArray's index depending on one of it's object's properties.
I have an xml-structure such as this:
<timeperiod>
<Day>
<Date>20120325</Date>
</Day>
<Day>
<Date>20120326</Date>
</Day>
</timeperiod>
Now, imagine that the xml-structure contains every day in a month.
Day and all of it's properties are added to an NSMutableArray.
And what i want is: To get the array's index depending on the date.
I'm imagining something like this in pseudo-code:
-(NSInteger)getIndexFromObjectProperty:(NSString *)property{
// Givf index from array where date is for example 20120310
//pseudo
Return [myClassObject.ObjectArray indexFromProperty:property];
}
I found this: Sorting NSMutableArray By Object's Property
This sorts from properties, but how to get an index from a property 0_0
I also found this: Function to get index from an array of objects having certain value of provided property
Which would help alot if the syntax worked out of the box :(
Any tips and/or pointers will be highly appreciated.
Thanks in advance.
EDIT I'm currently trying the answers given. Will get back with an update.
EDIT2: Office is throwing me out since they're locking the building down... i'll get back to you tomorrow..
You can use indexOfObjectPassingTest: method to test each object with a block returning a BOOL.
-(NSInteger)getIndexFromObjectProperty:(NSString *)property{
return [myArray indexOfObjectPassingTest:
^(id obj, NSUInteger idx, BOOL *stop) {
// Return true if obj has the date that you are looking for.
BOOL res;
if ([property isEqualTo:[obj dateProperty]]) {
res = YES;
*stop = YES;
} else {
res = NO;
}
return res;
}];
}
This really was not as hard as i thought it would be..
solved by doing the following...
-(NSInteger)returnIndexFromDateProperty:(NSString *)property{
NSInteger iIndex;
for (int i = 0; i < [myArray count]; i++){
if([property isEqualToString:[[myArray objectAtIndex:i]dateProperty]]){
iIndex = i;
}
}
return iIndex;
}
Thanks for all the other answers. They hepled alot during the process of thinking (not being bad).
You can use
- (NSUInteger)indexOfObjectPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
I'm not sure how your date format is, but as I understood it's an NSString, so I'd do it like this :
-(NSInteger)indexFromDate:(NSString *)date{
return [myClassObject.ObjectArray indexOfObjectPassingTest:
^(id obj, NSUInteger idx, BOOL *stop) {
return ([obj isEqualToString:myIdentifier]);
}];
}
You can do this with NSPredicate
[array indexOfObject:[[array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF.date == %#",date]] objectAtIndex:0]]
It's not clear from your post what structure your array will have after putting the XML data into it. Will it be an array of arrays? An array of dictionaries? Generally, to get an object from an array you use indexOfObject: or one of its variations.
I want to get the index of the current object when using fast enumeration, i.e.
for (MyClass *entry in savedArray) {
// What is the index of |entry| in |savedArray|?
}
Look at the API for NSArray and you will see the method
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
So give that one a try
[savedArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
//... Do your usual stuff here
obj // This is the current object
idx // This is the index of the current object
stop // Set this to true if you want to stop
}];
I suppose the most blunt solution to this would be to simply increment an index manually.
NSUInteger indexInSavedArray = 0;
for (MyClass *entry in savedArray) {
indexInSavedArray++;
}
Alternatively, you could just not use fast enumeration.
for (NSUInteger indexInSavedArray = 0; indexInSavedArray < savedArray.count; indexInSavedArray++) {
[savedArray objectAtIndex:indexInSavedArray];
}
This question has already been answered, but I thought I would add that counting iterations is actually the technique mentioned in the iOS Developer Library documentation:
NSArray *array = <#Get an array#>;
NSUInteger index = 0;
for (id element in array) {
NSLog(#"Element at index %u is: %#", index, element);
index++;
}
I was sure there would be a fancy trick, but I guess not. :)
If you want to access the index or return outside block here is a piece of code that can be useful. (considering the array is an array of NSString).
- (NSInteger) findElemenent:(NSString *)key inArray:(NSArray *)array
{
__block NSInteger index = -1;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isEqualToString:key]) {
*stop = YES;
index = idx;
}
}];
return index;
}
A simple observation: If you initialize the index to -1 and then put the ++index as the first line in the for loop, doesn't that cover all bases (provided someone doesn't slip code in front of the increment)?
I just had a pretty bad bug because I was doing this the way everyone else in here has suggested. That is, "create an index variable and increment it at the end of your loop".
I propose that this should be avoided and instead the following pattern should be followed:
int index = -1;
for (a in b) {
index++;
//Do stuff with `a`
}
The reason I recommend this odd pattern, is because if you use the continue; feature of fast enumeration, it will skip the final index++ line of code at the end of your loop, and your index count will be off! For this reason I recommend starting at -1 and incrementing before doing anything else.
As for people who said just use indexOfObject: this won't work with duplicate entries.