Enumerate NSArray starting at center / middle of array searching both ways - ios

How can I enumerate an array starting at the center of the array?

#implementation NSArray (Extensions)
- (void)enumerateFromCenterGoBothWaysUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
{
NSMutableArray *copy = [self mutableCopy];
BOOL shouldStop = NO;
while([copy count] > 0 && shouldStop == NO)
{
NSUInteger index = [copy count] / 2;
id obj = copy[index];
[copy removeObject:obj];
block(obj, index, &shouldStop);
}
}
#end

Related

I have a class I want some method transfer to an array's object

Like the title said: I have a ObjcClass
I want some thing can be reused ,
because the class may have
-(void)test1:xxx -(void)test2:xxx argu:yyy
I don't want to do that
[dispatchArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[obj test2:xxx argu:yyy];
}];
example:
- (void)test:(NSString *)argument1 {
NSArray *dispatchArray = #[];//If the array is initialized with multiple objects
//I want each object to call the "test:" method unlike the following
// [dispatchArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// [obj performSelector:#selector(test:) withObject:argument1];
// // or [obj test:argument1];
// }];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[_services enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
if ([obj respondsToSelector:_cmd]) {
[obj application:application didFinishLaunchingWithOptions:launchOptions];
}
}];
return YES;
}
like this ,UIApplicationDelegate has a number of method ,I don't want write [obj application:application didFinishLaunchingWithOptions:launchOptions]; or [obj applicationWillResignActive:application]; at every method,On the contrary I hope that method like [obj respondsToSelector:_cmd] ,that I can propose as a general method like [obj invokeWithMethod:_cmd arguments:_VA_LIST];
Whether these methods can be optimized,because they do the same thing to different method
The methods you app delegate has been implemented, you should implement as before. To the method in UIApplicationDelegate protocol which your app delegate did not implement, you can use message forwarding to achieve your target. Override the message forwarding methods of your app delegate as below:
- (BOOL)respondsToSelector:(SEL)aSelector {
struct objc_method_description desc = protocol_getMethodDescription(objc_getProtocol("UIApplicationDelegate"), aSelector, NO, YES);
if (desc.name != nil) {
return YES;
}
return [super respondsToSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL selector = [anInvocation selector];
struct objc_method_description desc = protocol_getMethodDescription(objc_getProtocol("UIApplicationDelegate"), selector, NO, YES);
if (desc.name != nil) {
[_services enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj respondsToSelector:selector]) {
[anInvocation invokeWithTarget:obj];
}
}];
}
}
Get the return values:
NSMutableArray *returnValues = [NSMutableArray array];
[_services enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
id returnValue = [NSNull null];
if ([obj respondsToSelector:selector]) {
[anInvocation invokeWithTarget:obj];
const char *returnType = anInvocation.methodSignature.methodReturnType;
if( !strcmp(returnType, #encode(void)) ){
//If the return value is `void`, just set returnValue = [NSNull null]
} else if( !strcmp(returnType, #encode(id)) ){
// if the return type is derived data types(`id`)
[anInvocation getReturnValue:&returnValue];
}else{
//if the return value is basicdata type
NSUInteger length = [anInvocation.methodSignature methodReturnLength];
void *buffer = (void *)malloc(length);
[anInvocation getReturnValue:buffer];
if( !strcmp(returnType, #encode(BOOL)) ) {
returnValue = [NSNumber numberWithBool:*((BOOL*)buffer)];
} else if( !strcmp(returnType, #encode(NSInteger)) ){
returnValue = [NSNumber numberWithInteger:*((NSInteger*)buffer)];
}
returnValue = [NSValue valueWithBytes:buffer objCType:returnType];
}
}
// If the `obj` can not responds to selector, or the return value is void(nil), we set the `returnValue = [NSNull null]`
[returnValues addObject:returnValue];
}]
Looks like you just want to loop through the objects in the array. This is not very Type safe. All objects must provide a "test" method. If they're all the same Class that would be better than using NSObject.
for (NSObject *obj in dispatchArray) {
[obj performSelector:#selector(test:) withObject:argument1];
}

SKSpriteNode return null name

Why the created name of a sprite isn't saved or even returned ?
I add several objects (SKSpriteNode) in the init of the Scene
-(id)initWithSize:(CGSize)size {
NSArray *oxyObjects = [self.oxygens objectsNamed:#"oxy"];
for (NSDictionary *enemyObj in oxyObjects) {
SKSpriteNode *oxyNode = [SKSpriteNode spriteNodeWithImageNamed:#"oxygen"];
NSString *valeurX=enemyObj[#"x"];
float x = [valeurX floatValue];
NSString *valeurY=enemyObj[#"y"];
float y = [valeurY floatValue];
CGPoint oxyPosition = CGPointMake(x, y);
oxyNode.position = oxyPosition;
oxyNode.name = #"ballOxygen";
NSLog(#"oxy %#",oxyNode);
[self.map addChild:oxyNode];
}
The log give me this with the correct name for the sprite
oxy name:'ballOxygen' texture:[ 'oxygen#2x.png' (24 x 24)] position:{454, 99} size:{12, 12} rotation:0.00
To check collision, I tried to use
[[self children] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
SKNode *node = (SKNode *)obj;
NSLog(#"obj : %#", obj);
Or this
NSArray *nodes = self.children;
for(SKNode * node in nodes){
SKSpriteNode *obj = (SKSpriteNode *) node;
NSLog(#"obj : %#", obj);
But It return always a null name
obj : name:'(null)' texture:[ 'Bird1#2x.png' (8 x 24)] position:{100, 100} size:{17, 12} rotation:0.00
My best guess:
You are adding oxyNode to self.map but you are enumerating self.children where you probably should be enumerating self.map.children.
Try this:
[self.map.children enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
SKNode *node = (SKNode *)obj;
NSLog(#"obj : %# (%p)", obj, obj);
}];

Application crashed in enumerateObjectsUsingBlock

My application crashed at following codes:
NSMutableArray* array = [dict objectForKey:key];
if (array)
{
__block BOOL find = NO;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString* word = (NSString*)obj;
if ([word isEqualToString:#"jerry"]) {
*stop = YES;
find = YES;
}
}];
And the crash information is:
1 CoreFoundation 0x2ecd30f2 __53-[__NSArrayM enumerateObjectsWithOptions:usingBlock:]_block_invoke (in CoreFoundation) + 90
2 CoreFoundation 0x2ecd3024 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] (in CoreFoundation) + 232
I can't figure it out, anyone knows how to solve this problem?
try to use:
[word isEqualToString:#"jerry"]
Try this:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString* word = (NSString*)obj;
if ([word isEqualToString:#"jerry"]) {
stop = YES;
find = YES;
}
You are comparing strings in the wrong way.
Try this
NSMutableArray* array = [NSMutableArray arrayWithArray:[dict objectForKey:key]];
if (array)
{
__block BOOL find = NO;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString* word = (NSString*)obj;
if ([word isEqualToString:#"jerry"]) {
*stop = YES;
find = YES;
}
}];
My money is on your array turning out to not actually be an array. When you create and set your array from the object in dict, check to see that [dict objectForKey:key] is actually an array.
NSMutableArray* array = [dict objectForKey:key];
NSLog(#"class of array: %#", [[dict objectForKey:key] class]);

Remove object from NSMutableArray?

I have an array with following elements in ViewDidLoad method
inputArray = [NSMutableArray arrayWithObjects:#"car", #"bus", #"helicopter", #"cruiz", #"bike", #"jeep", nil];
I have another UITextField for searching the elements .So once i type some thing in UITextField i want to check whether that string is present in "inputArray" or not.If it is not matching with elements in inputArray then remove the corresponding elements from inputArray .
for (NSString* item in inputArray)
{
if ([item rangeOfString:s].location == NSNotFound)
{
[inputArray removeObjectIdenticalTo:item];//--> Shows Exception
NSLog(#"Contains :%#",containsAnother);
}
}
but this code shows exception , something related to "removeobject:"
Exception :
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSCFConstantString rangeOfString:options:range:locale:]: nil argument'
*** First throw call stack:
`
In fast enumeration you can NOT modify the collection.
The enumerator object becomes constant and immutable.
If you want to do updation on the array
You should like this :
NSMutableArray *inputArray = [NSMutableArray arrayWithObjects:#"car", #"bus", #"helicopter", #"cruiz", #"bike", #"jeep", nil];
NSString *s=#"bus";
for (int i=inputArray.count-1; i>-1; i--) {
NSString *item = [inputArray objectAtIndex:i];
if ([item rangeOfString:s].location == NSNotFound) {
[inputArray removeObject:item];
}
}
EDIT:
The above works similar as this :
NSArray *array=[inputArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF CONTAINS[c] %#",s]];
You can use the following code
for (int i=0;i<[inputArray count]; i++) {
NSString *item = [inputArray objectAtIndex:i];
if ([item rangeOfString:s].location == NSNotFound) {
[inputArray removeObject:item];
i--;
}
}
That needs to be an NSMutableArray. You can't modify an NSArray once created (except to start all over).
Change this:
inputArray = [NSArray arrayWithObjects:#"car", #"bus", #"helicopter", #"cruiz", #"bike", #"jeep", nil];
to this:
inputArray = [NSMutableArray arrayWithObjects:#"car", #"bus", #"helicopter", #"cruiz", #"bike", #"jeep", nil];
and also change the property to NSMutableArray also:
#property(nonatomic, strong) NSMutableArray *inputArray;
The s in your question is probably nil. So your are getting the exception.Please check that out.
Use following Code. (This Code is use for filter Array base on input string/text of UITextField )
Take Two NSMutableArray and add one array to another array in ViewDidLoad method such like,
self.listOfTemArray = [[NSMutableArray alloc] init]; // array no - 1
self.ItemOfMainArray = [[NSMutableArray alloc] initWithObjects:#"YorArrayList", nil]; // array no - 2
[self.listOfTemArray addObjectsFromArray:self.ItemOfMainArray]; // add 2array to 1 array
And Write following delegate Method of UISearchBar
- (BOOL) textFieldDidChange:(UITextField *)textField
{
NSString *name = #"";
NSString *firstLetter = #"";
if (self.listOfTemArray.count > 0)
[self.listOfTemArray removeAllObjects];
if ([searchText length] > 0)
{
for (int i = 0; i < [self.ItemOfMainArray count] ; i = i+1)
{
name = [self.ItemOfMainArray objectAtIndex:i];
if (name.length >= searchText.length)
{
firstLetter = [name substringWithRange:NSMakeRange(0, [searchText length])];
//NSLog(#"%#",firstLetter);
if( [firstLetter caseInsensitiveCompare:searchText] == NSOrderedSame )
{
// strings are equal except for possibly case
[self.listOfTemArray addObject: [self.ItemOfMainArray objectAtIndex:i]];
NSLog(#"=========> %#",self.listOfTemArray);
}
}
}
}
else
{
[self.listOfTemArray addObjectsFromArray:self.ItemOfMainArray ];
}
[self.tblView reloadData];
}
}
Output Show in your Console.
As others have said you can't mutate an array while it is being enumerated. The easiest way to do what you want and keep the convenience of fast enumeration is to copy the array.
for (NSString* item in [inputArray copy]) {
...
}
This is a clean solution that I like to use. You define a NSArray category to extend it and create a map method. This method creates a new NSArray based on what you return within your block:
#interface NSArray (BlockExtensions)
/*!
Invokes block once for each element of self, returning a new array containing the
values returned by the block.
*/
- (NSArray *)map:(id (^)(id obj))block;
#end
#implementation NSArray (BlockExtensions)
- (NSArray *)map:(id (^)(id obj))block
{
return [self mapWithOptions:0 usingBlock:^id(id obj, NSUInteger idx) {
return block(obj);
}];
}
- (NSArray *)mapWithOptions:(NSEnumerationOptions)options usingBlock:(id (^)(id obj, NSUInteger idx))block
{
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]];
[self enumerateObjectsWithOptions:options usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id newobj = block? block(obj, idx) : obj;
if (newobj)
[array addObject:newobj];
}];
return array;
}
#end
The block will be called once for every item in your original array, passing this object as a parameter:
NSArray *newArray = [inputArray map:^id(NSString *item) {
if ([item rangeOfString:s].location == NSNotFound) {
return item;
}
return nil;
}];
newArray will contain your filtered out items!
+1 to Anoop for pointing out that you can use filteredArrayUsingPredicate. Thus, if you wanted to create a new array based upon the matches in inputArray, you could also use something like:
NSArray *matchingArray = [inputArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF contains[c] %#", s]];
Alternatively, given that inputArray is a NSMutableArray you can simply filter the array with this single line:
[inputArray filterUsingPredicate:[NSPredicate predicateWithFormat:#"SELF contains[c] %#", s]];
Or, if you like blocks:
[inputArray filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return ([evaluatedObject rangeOfString:s].location != NSNotFound);
}]];

Case-Insensitive array search

I am trying to search an NSMutableArray for a string. I am using containsObject: currently but it looks like this is case sensitive. I need to search for all combinations of the given string (trip). Any ideas would be greatly appreciated.
if ([self.theArray containsObject:trip]) {
}
Not that hard:
BOOL found = NO;
for (NSString* str in self.theArray) {
if ([str caseInsensitiveCompare:trip] == NSOrderedSame) {
found = YES;
break;
}
}
Create a category for NSArray and add this function in there.
- (BOOL)containsStringCaseInsensitive:(NSString *)key {
key = [key lowercaseString];
for (int i = ([self count] - 1); i >= 0; i--) {
NSObject * obj = [self objectAtIndex:i];
if ([obj isKindOfClass:[NSString class]]) {
NSString * strInArray = [(NSString *)obj lowercaseString];
if ([key isEqualToString:strInArray]) {
return YES;
}
}
}
return NO;
}
How about a block:
__block trip = #"blah";
if (NSNotFound!=[self.theArray indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop)
{
if (NSOrderedSame==[(NSString *)obj caseInsensitiveCompare:trip])
{
stop=YES;
return YES;
}
return NO;
}])
{
NSLog(#"It's a MATCH!");
}

Resources