Searching an NSMutableArray of Objects to find a string

Discussion in 'iOS Programming' started by IDMah, Sep 28, 2011.

  1. IDMah, Sep 28, 2011
    Last edited: Sep 28, 2011

    IDMah macrumors 6502

    IDMah

    Joined:
    May 13, 2011
    #1
    Hi all. I can't figure this out.

    I have an object let's call it foo.
    foo has:

    Code:
    NSString name;
    NSInteger footSize;
    NSInteger noseSize;
    
    and I've create a:
    
     NSMutableArray myFoo;
    
     added a bunch of foo(s) to:
    
     [myFoo addObject:moreFoo]; // like so //
    
    Now I'd like to search through myFoo for someone name: @"Geoff"
    and record it's index.  [myfoo objectAtIndex: X] ; 
    
    I thought I could use indexOfObject: 
    
    but what is the syntax: 
    
    NSInteger findIndex;
    findIndex = [myFoo indexOfObject:@"Geoff"]; // Didn't work //
    findIndex = [[myFoo indexOfObject:@"Geoff"] name]; 
    // returned a warning about NSUInteger  //
    
    can I even use "indexOfObject:" ??
     
    I also tried:
    NSInteger cardFound = 0;
    NSInteger fromIndex = 0;
    
    for (foo *inDex in myFoo) {
    		NSLog(@"started");
    		if ([[inDex name] isEqualToString: @"Geoff"]) {
    			cardFound = fromIndex;
    			NSLog(@"card found at: %@",cardFound);
    			break;
    		}
                    fromIndex++;
    }
    and that gives me an exception on the compare and exits..

    thanks any help appreciated.

    Ian
     
  2. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #2
    Post the crash-log or stack-trace from the exception.
     
  3. jiminaus macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #3
    Do as chown33 asks.

    But additionally, you won't be able to indexOfObject: with an NSString unless you override isEqual: in foo to handle being given an NSString argument.

    Alternatively you can use indexOfObjectPassingTest: and in the block do the comparison between a foo's name and an NSString.
     
  4. forum user macrumors regular

    Joined:
    Aug 28, 2008
    #4
    @IDMah
    Use NSPredicate on the array. Build a predicate and use it to search for your string

    - Olaf
     
  5. IDMah thread starter macrumors 6502

    IDMah

    Joined:
    May 13, 2011
    #5
    thanks.

    Thanks gang.

    I looked again and the exception was a stupid NSLog mistake.
    I was 'printing' a NSInteger with "%@" so it exited. over tired brains don't work too well.

    I'm using 3.2.5 now and not ready to switch to Lion so "indexOfObjectPassingTest:" is out, but will remember it when I switch over.
    cuz it looks easier than overriding..

    unsure about "NSPredicate" since I don't what to filter, I just want to look up.
    where @"Geoff" is in my array. Couldn't see/understand how it would help me find that out.

    My "Brute force" method seems to do the job for now.. so might stick to that, since it's all my little brain can understand right now, and I'm probably going to restructure how the 'Data' is stored to make my life easier.. It's been difficult trying to un-think/ translate how I would do things in C.

    thanks.
     
  6. jiminaus macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #6
    indexOfObjectPassingTest: was introduced in Snow Leopard (10.6) not Lion (10.7), together with blocks. XCode support for blocks was introduced in XCode 3.2.
     
  7. forum user macrumors regular

    Joined:
    Aug 28, 2008
    #7
    my bad, overlooked that you wanted the position of the string
     
  8. IDMah, Sep 30, 2011
    Last edited: Sep 30, 2011

    IDMah thread starter macrumors 6502

    IDMah

    Joined:
    May 13, 2011
    #8
    Blocked !

    jiminaus didn't know that thanks.

    So I'm trying Blocks
    Code:
    NSInteger tryIndx = 255;
    	
    NSString *matchName = @"Geoff";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF 
                                        contains[cd] %@", matchName];
    
    	tryIndx = [myFoo indexOfObjectPassingTest:^(id obj, NSUInteger idx,
                           BOOL *stop) {
    		          return [predicate evaluateWithObject:obj];
    	               }];
    	
    	NSLog(@" index of Object found At: %i",tryIndx );
    
    
    but got this...

    Code:
    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can't use in/contains operator with collection <Foo: 0x4e06450> (not a collection)'
    How do I indicate that I want to look in the Array myFoo of objects foo for the element (member): name?

    thanks

    ps is this actually a lot faster than the for loop?
    Pss. Sorry this is my first real Xcode app.

    Edit: Ahhh Got it .. cool

    Code:
    tryIndx = [  rowOne indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
    		return [predicate evaluateWithObject:[obj image]];
    
     
  9. jiminaus macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #9
    Just for illustration, you can do it without predicates as well. I don't know about the performance implications of using or not using NSPredicate, you'd need to profile.

    In it's simplest case:
    Code:
    tryIndex 
      = [myFoo indexOfObjectPassingTest:
            ^(id obj, NSUInteger idx, BOOL *stop)
            {
               [[obj image] isEqualToString:matchName];
            }
        ];
    
    But because you've coded a case- and accent-insensitive substring match, the equivalent to your predicate is bit more.

    Code:
    tryIndex 
      = [myFoo indexOfObjectPassingTest:
            ^(id obj, NSUInteger idx, BOOL *stop)
            {
               NSRange r
                  = [[obj image] 
                        rangeOfString:matchName
                              options:NSCaseInsensitiveSearch
                                      | NSDiacriticInsensitiveSearch
                    ];
               return r.location != NSNotFound;
            }
         ];
    
    BTW If you want to handle international strings, add the NSWidthInsenesitiveSearch option as well for your Japanese users.
     
  10. Sykte macrumors regular

    Joined:
    Aug 26, 2010
    #10

    It's not only about being faster, it's about code readability and the amount of coded needed, less code = more time for testing. :) I have found in many cases blocks are faster. I'm sure this has to do with them being copied directly onto the stack. However I suggest profiling your app and checking both ways if performance is a necessity.
     
  11. IDMah thread starter macrumors 6502

    IDMah

    Joined:
    May 13, 2011
    #11
    NSPredicate range ??

    Wow NSPredicate is pretty awesome, thanks everyone..

    I've jumbled all my 2d arrays into one contiguous mass.
    with Virtual boundaries, i.e if (indexX < boxSizeMaxX) then
    We are in compartment two. etc.

    But how would I search using NSPredicate from say:

    Array from elements [0-40]
    and Array elements [50-75]
    ... etc.

    Does it, could it, save me a lot of processing time vs. a while loop?

    thanks
    Ian
     

Share This Page