Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Searching for an object index in NSArray using an inline block

I've seen a few examples around using NSArray indexOfObjectPassingTest, but I could not make them work (they would not return a valid index). So now I'm trying using an inline block. I have done it by typedef'ing a block, then setting it as a property, synthesizing it, and initializing it in the constructor. However that sort of renders the whole point mute, as I could easily create a method and use it instead (less typing, less effort).

What I'm trying to achieve is something along this:

Observations *obs = [self.myAppointment.OBSERVATIONS objectAtIndex: ^NSInteger (NSString *keyword){ 
    for (Observations *obs in self.myAppointment.OBSERVATIONS) {
        if ([obs.TIME isEqualToString:keyword] == YES) return (NSInteger)[self.myAppointment.OBSERVATIONS indexOfObject:obs];
    }
    return (NSInteger)-1;
}];

However Xcode simply won't have it. I've tried different variations, but declaring it inline seems to be a problem, which is weird, because as I've said, typedefing it, declaring, and synthesizing it works like this:

Observations *obs = [self.myAppointment.OBSERVATIONS objectAtIndex:findObs(keyword)];

Where findObs is again a defined block that does the same thing. Is this a syntax issue, or am I missing something else more important ?

like image 650
Ælex Avatar asked Jun 11 '12 12:06

Ælex


3 Answers

-objectAtIndex: takes an NSUInteger as a parameter, but you're passing it a block (denoted by the ^). Your second example calls findObs (which may be your block) with the keyword argument, passing the result of that call to -objectAtIndex:.

You probably want to combine -objectAtIndex: with -indexOfObjectPassingTest::

NSString *keyword = /* whatever */;
NSArray *array = self.myAppointment.OBSERVATIONS;
NSUInteger idx = [array indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop){ 
    Observations *obs = (Observations*)obj;
    return [obs.TIME  isEqualToString:keyword];
}];
if (idx != NSNotFound)
    Observations *obs = [array objectAtIndex:idx];
like image 134
一二三 Avatar answered Nov 09 '22 13:11

一二三


This is an example that returns the index of a string in an array of strings. It may be adapted to any kind of object.

NSString* myString = @"stringToFind";
NSUInteger objectIndex = [myStringArray indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
        return (*stop = ([obj isEqualToString:myString]));
    }];

To answer the original question precisely:

NSString *keyword = @"myKeyword";
NSUInteger index = [self.myAppointment.OBSERVATIONS indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) { 
    return (*stop = [(Observations*)obs.TIME  isEqualToString:keyword]);
}];
Observations *obs = (index!=NSNotFound) ? self.myAppointment.OBSERVATIONS[index] : NULL;

But it's quite strange to compare something called TIME with a keyword… ;)

like image 43
Moose Avatar answered Nov 09 '22 14:11

Moose


It's not necessary to typedef or synthesize anything to make your second example work--just return the block from a method, which would look like this:

-(NSUInteger(^)(NSArray *, NSString *))findObs {
    return ^(NSArray *array, NSString *keyword) {
        for (NSUInteger i = 0; i < [array count]; i++) {
            Observations *obs = [array objectAtIndex:i];
            if ([obs.TIME isEqualToString:keyword]) {
                return i;
            }
        }
        return NSNotFound;
    };
}

Observations *obs = [self.myAppointment.OBSERVATIONS objectAtIndex:[self findObs](keyword)];

Here are some good reasons for defining blocks as method return values, rather than inline.

like image 3
Christopher Pickslay Avatar answered Nov 09 '22 14:11

Christopher Pickslay