Amazon.com Widgets Exercise 15 5 Question
Welcome, Guest. Please login or register.
Did you miss your activation email?
May 19, 2013, 08:47:48 AM
Home Help Search chat Login Register   
News: Read this please.The Great Kangaroo Escape Looking for reviews of the 4th ed on Amazon!   Twitter:  @skochan
                     

+  Official Forum for Programming in Objective-C (the iPhone Programming Language) - Stephen Kochan
|-+  Programming in Objective-C, 4th edition
| |-+  Chapter 15
| | |-+  Exercise 15 5 Question
Pages: [1]   Go Down
Print
Author Topic: Exercise 15 5 Question  (Read 1074 times)
flatlander
Newbie
*
Posts: 4






« on: March 16, 2012, 07:16:12 AM »

The question indicates there is a way of searching all fields of an address card for a match to your search string.

I've been pondering this and am unsure of the best route to proceed, the thought had crossed my mind to edit the description method so it returned the field names, but i'm not sure this is a good way to go.

the better thought is to do some sort of lookup on the address card object to return the variables within it then do a fast enumeration thru these and apply each to your rangeOfString method.

any hints?

thanks, David
Logged
jahay
Newbie
*
Posts: 1


Email




« Reply #1 on: March 17, 2012, 10:51:14 PM »

I added a method to the AddressCard class that returns an array containing its instance variables (which are NSString objects), I then iterate through each of the objects in the array.  The downside is that if the AddressCard is ever expanded to include additional instance vars, the method would need to manually be updated so the returned array includes the additional objects as well.  I was not sure if there was a way to automate the whole task...

AddressCard method:
Code: (Objective-C)
-(NSArray *) fields {
    NSArray *cardFields = [NSArray arrayWithObjects:name, email, phoneNumber, nil];
    return cardFields;
}

AddressBook method:
Code: (Objective-C)
-(NSMutableArray *) lookupFromAllFields:(NSString *) searchString {
    NSMutableArray *matches = [NSMutableArray array];
   
    for(AddressCard *card in book){
        NSArray *cardFields = [card fields];
       
        // iterate through each field
        NSIndexSet *matchingCardFields = [cardFields indexesOfObjectsPassingTest:
                                      ^(id currentField, NSUInteger idx, BOOL *stop){
                                          NSRange result = [currentField rangeOfString:searchString options:NSCaseInsensitiveSearch];
                                          if(result.location != NSNotFound) {
                                              return YES;
                                          }
                                          else
                                              return NO;
                                      }];
        // See if we found any matches in the current cards fields, if yes, add current card into matches array
        if([matchingCardFields count] > 0)
            [matches addObject:card];
    }
   
    // See if we found any matches, if yes, return matches array
    if([matches count] > 0)
        return matches;
    else
        return nil;
   
}
Logged
Eloise
Newbie
*
Posts: 8






« Reply #2 on: May 19, 2012, 01:38:17 AM »

I have a partial answer... can't work out the last bit yet. But courtesy of searching a bit, and working it out if you put this function into AddressCard.m and #import "objc/runtime.h" in the AddressCard.h file you can get an array of all the properties.
Code: (Objective-C)
- (NSMutableArray *)properties
{
    NSMutableArray *props=[NSMutableArray array];
    unsigned int outCount, i;
    objc_property_t *properties=class_copyPropertyList([self class], &outCount);
    for(i=0; i<outCount; i++)
    {
        objc_property_t property=properties[i];
        const char *propName = property_getName(property);
        if(propName)
        {
            NSString *propertyName=[NSString stringWithUTF8String:propName];
            [props addObject:propertyName];
        }
    }
    return  props;
}

What I can't yet do is abstract this into a nice class I can pass other classes too, nor can I work out how to search the card for the returned properties. I'm guessing instead of extracting the objective_c_property_t and converting it to a string etc. I have to work with that directly to search that field, but I can't concentrate any more right now.
Logged
Eloise
Newbie
*
Posts: 8






« Reply #3 on: May 19, 2012, 03:51:57 AM »

Using:
Code: (Objective-C)
- (NSMutableArray *)properties
{
    id AddressCardClass=objc_getClass("AddressCard");
    NSMutableArray *props=[NSMutableArray array];
    props=[PropertyUtility classPropertiesList:AddressCardClass];
    return props;
}
along with
Code: (Objective-C)
#import "objc/runtime.h"
#import "propertyUtility.h"

and:
Code: (Objective-C)
#import "propertyUtility.h"
#import "objc/runtime.h"

@implementation PropertyUtility

+(NSMutableArray *)classPropertiesList:(Class)clss
{
    if(clss == NULL)
    {
        return  nil;
    }
    NSMutableArray *props=[NSMutableArray array];
    unsigned int outCount, i;
    objc_property_t *properties=class_copyPropertyList(clss, &outCount);
    for(i=0; i<outCount; i++)
    {
        objc_property_t property=properties[i];
        const char *propName = property_getName(property);
        if(propName)
        {
            NSString *propertyName=[NSString stringWithUTF8String:propName];
            [props addObject:propertyName];
        }
    }
    return  props;
}


@end

Lets you have a property utility that returns an array of the various field names and works ok.

Still can't make the jump from there to being able to search the fields though.
Logged
Eloise
Newbie
*
Posts: 8






« Reply #4 on: May 19, 2012, 08:18:27 AM »

OK, got it.

With this in the main.m
Code: (Objective-C)
AddressBook *mySearch = [[AddressBook alloc] initWithName:@"Search Items"];
mySearch = [myBook lookupAllInAllFields:@"a"];
[mySearch list];
(in addition to all the card initialisation stuff of course)

and this in AddressBook.m
Code: (Objective-C)
-(AddressBook *)lookupAllInAllFields: (NSString *)searchTerm
{
    AddressBook *matches=[[AddressBook alloc] initWithName:@"matches"];
    NSRange subRange;
    NSString *testVal;
    for(AddressCard *card in book)
    {
        unsigned int outCount, i;
        objc_property_t *properties=class_copyPropertyList([card class], &outCount);
        for(i=0; i<outCount; i++)
        {
            objc_property_t property=properties[i];
            testVal=[NSString stringWithUTF8String:property_getName(property)];
            if(testVal)
            {
                subRange=[[card valueForKey:testVal] rangeOfString:searchTerm options:NSCaseInsensitiveSearch];
                if(subRange.location != NSNotFound)
                {
                    BOOL add=YES;
                    for(AddressCard *found in [matches book])
                        if([[found name]isEqualToString:[card name]])
                            add=NO;
                    if(add)
                        [matches addCard:card];
                }
                
            }
        }
    }
    return matches;
}

You get the right results from it.

I'm pretty sure it can be tided up (a lot!). For example, as per an earlier post, I've got a way to get the names of properties out as a utility class. That could be tidied up I'm sure. There's a semi-ugly hack enumerating through the matches book to stop it adding duplicate values if you search for common things (j shows up in the sample code in both name and email for example for some people).

But at least it works. The valueForKey and stringWithUTF8String bits are critical here.
Logged
Pages: [1]   Go Up
Print
Jump to:  



Login with username, password and session length

Powered by MySQL Powered by PHP Powered by SMF 1.1.11 | SMF © 2006-2009, Simple Machines LLC Valid XHTML 1.0! Valid CSS!
Entire forum contents (c) 2009 classroomM.com. All rights reserved.