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))
Related
I have a NSMutableArray with multiple repeated strings, I am trying to get last string index.
NSMutableArray *arrWithRepeatedStrings=[[NSMutableArray alloc]initWithObjects:#"iOS",#"apple",#"iOS",#"apple",#"apple",#"iOS", nil];
here iOS and apple are the repeated strings in my array. I think it's possible and am on the way to it. Can anyone help me.
You can get the index of last occurrence of string by enumerating in the reverse direction. Use the code below. You can change the matching string to #"iOS" if you want it's index and not #"apple".
NSMutableArray *arrWithRepeatedStrings=[[NSMutableArray alloc]initWithObjects:#"iOS",#"apple",#"iOS",#"apple",#"apple",#"iOS", nil];
NSInteger index = [arrWithRepeatedStrings indexOfObjectWithOptions:NSEnumerationReverse passingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
if([obj isEqualToString: #"apple"])
return YES;
else
return NO;
}];
I understand the question that you want the index of the last object that is more than once in the array. This is quite different from what #Gandalf's solution does, so here's my take:
NSInteger index = [array indexOfObjectWithOptions:NSEnumerationReverse
passingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
BOOL match = [arrWithRepeatedStrings indexOfObject:obj] != idx;
if (match) *stop = YES;
return match;
}];
Is there some native objective c method that's similar to Haskell's intersperse function?
Here's an example
Prelude> Data.List.intersperse ',' "abcdefg"
"a,b,c,d,e,f,g"
Or maybe some function that joins objects on another object? I know I can do it manually by looping over the collection, I'm just wondering if there's some functional built-in that serves this use case
I have some UIBarButtonItem objects,
[barButtonItem1,barButtonItem2,barButtonItem3]
I want to end up with:
[barButtonItem1,flexButtonItem,barButtonItem2,flexButtonItem,barButtonItem3]
A quick Google for such a function turned up an NSArray+FunctionalKit.h that implements the intersperse function you desire. Given that this exists, I'm tempted to say that there is no built-in function that does it. You could add this to your projects:
#interface NSArray (Intersperse)
- (NSArray *)intersperse:(id)object;
#end
#implementation NSArray (Intersperse)
- (NSArray *)intersperse:(id)object {
NSMutableArray *interspersed = [NSMutableArray array];
for (NSUInteger i = 0; i < [self count]; ++i) {
[interspersed addObject:[self objectAtIndex:i]];
if (i != [self count] - 1) [interspersed addObject:object];
}
return [NSArray arrayWithArray:interspersed];
}
#end
Usage:
NSArray *buttons = #[barButtonItem1,barButtonItem2,barButtonItem3];
NSArray *allButtons = [buttons intersperse:flexButtonItem];
The whole GitHub project is available.
This chunk of code is a method that creates an array for use by multiple other classes. Input is an array from a CoreData fetch, of type NSDictionaryResultType.
3 of the fields are strings that I need to break into arrays, thus the componentsSeparatedByString.
The resulting array, _dataProductionArray, works great --- BUT --- this chunk of code takes a FULL 5 SECONDS to process for about 32,000 records.
Any help pointing out glaring mistakes that are causing this slow performance would be greatly appreciated!!
NSMutableArray *dataArray = [NSMutableArray array];
int j = 0;
int maxNumMonths = 0;
for (id obj in _dictionaries) {
if ([_dictionaries[j] [#"month"] length] >0 ) {
// get production values
NSArray *aItems = [_dictionaries[j] [#"prodA"] componentsSeparatedByString:#","];
NSArray *bItems = [_dictionaries[j] [#"prodB"] componentsSeparatedByString:#","];
NSArray *monthItems = [_dictionaries[j] [#"month"] componentsSeparatedByString:#","];
NSMutableArray *productionAArray = [NSMutableArray array];
NSMutableArray *productionBArray = [NSMutableArray array];
int monthLoop = 1;
for (NSNumber *month in monthItems) {
if (monthLoop <= MONTHS_OF_PRODUCTION) {
if ([month intValue] == monthLoop) {
[productionAArray addObject:[aItems objectAtIndex:monthLoop-1]];
[productionBArray addObject:[bItems objectAtIndex:monthLoop-1]];
productionCount ++;
if (monthLoop > maxNumMonths)
maxNumMonths = monthLoop;
}
}
monthLoop++;
}
NSDictionary *arrayItem = #{#"name":_dictionaries[j] [#"name"],
#"type":[NSString stringWithFormat:#"%#",_dictionaries[j] [#"type"]],
#"height":[NSString stringWithFormat:#"%#",_dictionaries[j] [#"height"]],
#"aArray":productionAArray,
#"bArray":productionBArray,
};
[dataArray addObject:arrayItem];
}
j++;
}
_dataProductionArray = [NSArray arrayWithArray:dataArray];
I can see a few optimizations you could do in the loop, but I'm not sure how much these would help (especially if the compiler is doing them anyway). The root problem is that 32k is a lot of iterations.
Do you need all 32k results at once? You could get a dramatic improvement in user experience by doing this work lazily, as the UI demands the transformed record.
This approach would be to make dataProductionArray a mutable dictionary, indexed by an NSNumber index. Then, instead of ...
// replace this
self.dataProductionArray[128];
// with this
[self dataProductionAtIndex:#128];
That new getter method calls the code you wrote lazily, like this ...
- (id)dataProductionAtIndex:(NSNumber *)index {
// replace dataProductionArray with dataProductionDictionary
id result = self.dataProductionDictionary[index];
if (!result) {
result = [self getDataAt:index];
self.dataProductionDictionary[index] = result;
}
return result;
}
Then getDataAt: is a simple refactor of the code you posted, except instead of looping 32k elements, it does the work for just one index that gets passed in....
- (id)getDataAt:(NSNumber *)index {
int j = [index intValue];
// no loop, just skip to iteration j
NSArray *aItems = [_dictionaries[j] [#"prodA"] componentsSeparatedByString:#","];
NSArray *bItems = [_dictionaries[j] [#"prodB"] componentsSeparatedByString:#","];
// and so on, then at the end, don't save arrayItem, just return it
NSDictionary *arrayItem = #{#"name":_dictionaries[j] [#"name"],
#"type":[NSString stringWithFormat:#"%#",_dictionaries[j] [#"type"]],
#"height":[NSString stringWithFormat:#"%#",_dictionaries[j] [#"height"]],
#"aArray":productionAArray,
#"bArray":productionBArray,
};
return arrayItem;
}
PS - A mutable dictionary is a good data structure for lazy evaluation. The next level of sophistication is NSCache, which acts like a mutable dictionary and also manages memory (class ref here).
Your for loop is daft. Just write
for (NSDictionary* dict in _dictionaries)...
and use dict instead of _dictionaries [j]. One method call saved each time.
stringWithFormat: creates a new string each time. Can't you just add the item itself instead of turning it into a string?
Instead of extracting all the items into productionAArray and productionBArray, create an NSIndexSet, fill it in the loop -- or better yet using a block -- and create the arrays in one go.
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];
I have an array of UIView. I want to check if that array contains UIView with specific tag. If it does then I should get that view or else I should receive nil.
As of now I using following
// validCells is an array UIView
NSPredicate *p = [NSPredicate predicateWithBlock:^BOOL(id obj, NSDictionary *ignored){
return ((UIView *)obj).tag == i;
}];
UIView *cell = [[validCells filteredArrayUsingPredicate:p] lastObject];
This works fine but complexity is n^2. I was wondering if there is any other better way to do it.
Thanks.
I don't think the complexity of your method is O(n^2), it is more probably like O(n).
But there is no reason to create a temporary array if you just search for a specific
element. As #Josh said, you can do a simple enumeration.
If you want to be a bit more fancy, you can write it as
NSUInteger index = [validCells indexOfObjectPassingTest:^BOOL(UIView *view, NSUInteger idx, BOOL *stop) {
return view.tag == idx;
}];
if (index != NSNotFound) {
cell = validCells[index];
}