Pretty simple question. I'm working in Objective C (cocos2d) and I'm trying to count the number of a sprites of a certain class are present on the current layer being displayed. For example, I have a class named Seal which is a subclass of of CCNode and in my current layer I want to count how many instances of type Seal are present.
I know how to count the number of children of the layer by doing
int numberChildren = [[self children] count];
which correctly returns the number of children on the layer. But I just want the number of Seals on my layer. How could I do this? Thanks =)
You can do this using predicate functions, like:
NSArray * nodes = [self children];
NSIndexSet * sealSet = [nodes indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)
{
return [obj isKindOfClass:[Seal class]];
}];
NSArray * sealArray = [nodes objectsAtIndexes:sealSet];
NSUInteger numberOfSeals = [sealArray count];
EDIT:
Actually you don't have to store seals in a new array, you can count them simply:
NSUInteger numberOfSeals = [sealSet count];
You can try below code which doesn't use an array and hence less memory foot print--
NSInteger sealCounter = 0;
for(id item in [self children])
if([item isKindOfClass:[Seal class])
sealCounter++; //After the for loop ends you can know how many Seals you have
But if you want to run some actions only on Seals after you count them then storing the items in an array would help you like this:
NSMutableArray *sealArray;
for(id item in [self children])
if([item isKindOfClass:[Seal class])
[sealArray addObject:(Seal *)item];//This will hold only seals and you can get the count by simply doing [sealArray count];
Related
I need to do something similar to python's enumerate() function with an NSArray in iOS (I have to build NSIndexPath objects as well as examine the object).
I don't see a built in method for doing something like this (i.e. no NSArray equivalent of NSDictionary's enumerateKeysAndObjectsUsingBlock: method). Which leaves me with two general approaches I can think of.
for (NSUInteger index = 0; index < mySequence.count; index++) {
MyElementType *element = mySequence[index];
//
// code that works with both index and element
//
}
or
NSUInteger index = 0;
for (MyElementType *element in mySequence) {
//
// code that works with both index and element
//
index++;
}
Is there a good reason to prefer on or the other? Or is there a third approach that is better than either of these?
There is following API present in NSArray:
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))
I've a main array (arrayEvents) contains list of events with fields like, start_date, event_id, event_name, many events can be possible on same date but different times. So I'm taking only unique dates (arrayDates) to check and compare it with the current date (currentDate its range from 1st day of month to last day of month) to show event on particular dates. I'm done up to here. Now my problem is that, I need to update arrayEvents for later comparisons so I am applying the following logic ...but I am in the middle of the sea and don't know where to go!
for(int i=0;i<[arrayDates count];i++) {
NSDate *obj = [arrayDates objectAtIndex:i];
if([obj isEqualToDate:currentDate]) {
//This date has some events, we've done up to here.
//Now taking indexes of same objects (that's same dates objects) from the arrayEvents
NSIndexSet *indexesOfObjects = [arrayDates indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [arrayEvents containsObject:obj];
}];
if([indexesOfObjects count] > 1) {
//We found some objects into arrayEvents, now I have to update
//those objects into arrayEvents.
//How to achieve this?
//My plan was to iterate each indexes and make update on arrayEvents
//but I can't do this, as I don't have access to each index from NSIndexSet.
}
}
}
[[arrayEvents objectsAtIndexes:indexesOfObjects]
setValue:[arrayDates objectAtIndex:i] forKey:#"filter_date"];
isn't worked.
Reference, How to indicate index of NSIndexSet object? but doesn't help me.
You can try doing something like this:
NSMutableDictionary *dict=[[NSMutableDictionary alloc]initWithDictionary:[arrayEvents objectAtIndex:indexPath.row]];
[dict setValue:[arrayDates objectAtIndex:i] forKey:#"filter_date"];
[arrayEvents replaceObjectAtIndex:indexPath.row withObject:dict];
or you can use
replaceObjectsAtIndexes:(NSIndexSet *) withObjects:(NSArray *)a
which ever suits your convenience
How would I take an array with long list of numbers that contains duplicates, so for instance:
NSArray *array = [NSArray arrayWithObjects:#"45", #"60", #"100",#"100", #"100", #"60"nil];
Just imagine that this is a HUGE list of random numbers. Now I'm sure that I have to use something like NSSet for this, but i'm not sure how to execute this. Also, once we identify the duplicates I'm guessing that I would then add those numbers to an array, and then call
[array count];
Any ideas?
NSCountedSet *set = [[NSCountedSet alloc] initWithArray:array];
int duplicates = 0;
for (id object in set) {
if ([set countForObject:object] > 1) {
duplicates++;
}
}
This will calculate how many elements have a duplicate.
A sidenote, that array contains a bunch of strings, no numbers...
Anyway, if the goal is to get just the count you could use the following single line to get it.
NSUInteger diff = [array count] - [[array valueForKeyPath:#"#distinctUnionOfObjects.self"] count];
This uses KVC (Key-Value Coding) to get all distinct objects (that is ones without a dupe) counts them and gets the difference from the original count.
NSCountedSet is perfect for what you want to do.
NSArray *array = [NSArray arrayWithObjects:#"45", #"60", #"100",#"100", #"100", #"60",nil];
NSCountedSet *countedSet = [NSCountedSet setWithArray:array];
__block NSUInteger totalNumberOfDuplicates = 0;
[countedSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
NSUInteger duplicateCountForObject = [countedSet countForObject:obj];
if (duplicateCountForObject > 1)
totalNumberOfDuplicates += duplicateCountForObject;
NSLog(#"%# appears %ld times", obj, duplicateCountForObject);
}];
NSLog(#"Total number of duplicates is %ld", totalNumberOfDuplicates);
produces:
45 appears 1 times
60 appears 2 times
100 appears 3 times
Total number of duplicates is 5
Use filteredArrayUsingPredicate this use a predicate with your condition and return an array with the objects you need.
NSArray* arr=[[NSArray alloc] initWithObjects:#"10",#"11",#"10",#"2", nil];
NSLog(#"%d",[[arr filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF == '2'"]] count]);
I have an array of arrays. The contained array's first elements are all NSDate objects. I would like to sort the array containing the arrays in order from most recent to least. For some reason, the below sorting algorithm results in an infinite loop. Can anyone help me out? Thank you.
Best...SL
//array is the array containing all of the other arrays(that have NSDates as their first elements)
//temp is the new array being added to the end of the array, to later be sorted into the correct position.
[array addObject:temp];
NSMutableArray *tempArray;
for (int i=0; i<[array count]; i++)
{
NSDate *session1, *session2;
session1 = [[array objectAtIndex:i] objectAtIndex:0];
session2 = [[array objectAtIndex:[array count]-1] objectAtIndex:0];
if([session1 compare:session2] == NSOrderedDescending)
{
tempArray = [array objectAtIndex:i];
[array insertObject:[array objectAtIndex:[array count]-1] atIndex:i];
[array insertObject:tempArray atIndex:[array count]-1];
}
}
This results in an infinite loop because, in every step, you're inserting two more values into the array. Thus your array is growing faster than you are traversing it. I'm assuming you meant to swap the values.
In any case, a much simpler and more efficient sort is to use the built-in sorting capabilities:
// NSArray *sortedArray, with the unsorted 'array' pulled from some other instance
sortedArray = [array sortedArrayUsingComparator:^(id a, id b) {
return [[b objectAtIndex:0] compare:[a objectAtIndex:0]];
}];
If array is mutable and you want to sort it in place:
[array sortUsingComparator:^(id a, id b) {
return [b[0] compare:a[0]];
}];
If array is immutable or you want to leave it alone and make a sorted copy:
NSArray *sortedArray = [array sortedArrayUsingComparator:^(id a, id b) {
return [b[0] compare:a[0]];
}];
I have an array of myObjects called arrayToFilter. One (element?) of myObject is an array of bezierpaths. I am comparing the bezierpath at a particular index (thispath) to a second path, and making filteredArray composed of only those objects in which the paths overlapped a certain amount (20%). I used indexedOfObjectsPassingTest to like this:
NSIndexSet * index = [[arrayToFilter objectsAtIndexes:index] indexesOfObjectsPassingTest:^BOOL (id obj, NSUInteger idx, BOOL *stop){
MyObject * anobject = obj;
UIBezierPath * thispath = [anobject.allPaths objectAtIndex:i];
NSInteger overlap = [self percentPathBoxOverlap:path: thispath];
return overlap>20;
}];
if ([index count] !=0){
filteredArray = [arrayToFilter objectsAtIndexes:index] ;
}
This works fine. But what i'd like to do is have filteredArray come out sorted with those object with the higher value for overlap coming out first. since overlap is calculated on the fly based on the current path and thispath, i don't know how to use any of the sorted array methods.
You can start off by creating an array of dictionaries containing both path and the overlap data. This will require some modification to your current approach where you search and extract over filter.
NSMutableArray * searchResults = [NSMutableArray array];
[arrayToSearch enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
MyObject * anobject = obj;
UIBezierPath * thispath = [anobject.allPaths objectAtIndex:i];
NSInteger overlap = [self percentPathBoxOverlap:path: thispath];
if ( overlap > 20 ) {
NSMutableDictionary * dictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:anObject, #"object", [NSNumber numberWithInteger:overlap], #"overlap", nil];
[searchResults addObject:dictionary];
}
}];
Now you can sort this array using the overlap key of the dictionaries.
NSSortDescriptor * descriptor = [NSSortDescriptor sortDescriptorWithKey:#"overlap" ascending:NO];
NSArray * sortedArray = [searchResults sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]];
Now sortedArray will have the sorted list of path and overlap information.
You'll have to sort the array first, then call indexesOfObjectsPassingTest for the sorted indices. sortedArrayUsingComparator: is one of the easier methods of sorting an array, it takes a block just like the indexesOfObjectsPassingTest method.
NSArray arrayToFilter = [originalArray sortedArrayUsingComparator: ^(id a, id b)
{
if (a.someValue > b.someValue) return NSOrderedAscending;
if (a.someValue < b.someValue) return NSOrderedDescending;
return NSOrderedSame;
}];
Then you can perform your existing filtering on the arrayToFilter