Challenge Uncertainty and NSLog Help


#1

I am quite new to programming and after reading the other solutions to this challenge I’m feeling that I might be over-simplifying this task. I just followed the example in the book. Is what I did correct or am I missing the whole point? Everything seems to work except for a couple of NSLog formatting issues that are really bothering me.

Formatting Issue 1: I have spent an embarrassingly long amount of time trying to get my boolean values to print as a ‘yes’ or ‘no’ instead of a ‘0’ or ‘1’ in my NSLog results. Can somebody please tell me if/how this can be done?

Formatting Issue 2: Is there a way to print out my array values in NSLog without the parentheses and quotes?

Any/all help would be MUCH appreciated.

[code]#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{

@autoreleasepool {
    
    //Create an array to hold the zoo property list
    NSMutableArray *zoo = [[NSMutableArray alloc] init];
    
    //Create a dictionary to hold the animals to be added to the zoo
    NSMutableDictionary *animal;
    
    //Set Animal Food Arrays
    NSArray *lionFood = [NSArray arrayWithObjects: @"fluffy bunnies", @"begging dogs", @"undisciplined children", nil];
    
    NSArray *kinkajouFood = [NSArray arrayWithObjects:@"bananas", @"honey", @"rodents", @"tourist's fingers", nil];

    //Get Animal Description Data from local files
    NSData *mountainLionData = [NSData dataWithContentsOfFile:@"/Users/dd/Documents/mountainLion.rtf"];
    NSLog(@"The mountain lion file from the disk has %lu bytes", [mountainLionData length]);
    
    NSData *kinkajouData = [NSData dataWithContentsOfFile:@"/Users/dd/Documents/kinkajou.rtf"];
    NSLog(@"The kinkajou file fromt he disk has %lu bytes:", [kinkajouData length]);
    
    
    //Create an animal dictionary entry for a Mountain Lion
    animal = [NSMutableDictionary dictionary];
    [animal setObject:@"Mountain Lion"
               forKey:@"species"];
    [animal setObject:@"Kitty"
               forKey:@"name"];
    [animal setObject:[NSNumber numberWithInt:5]
               forKey:@"yearsAtZoo"];
    [animal setObject:[NSDate dateWithNaturalLanguageString:@"May 4, 2001"]  
               forKey:@"birthDate"];
    [animal setObject:[NSNumber numberWithBool:YES]
                                        forKey:@"endangered"];
    [animal setObject:[NSNumber numberWithFloat:250.7]
               forKey:@"weight"];
    [animal setObject:lionFood
               forKey:@"diet"];
    [animal setObject:mountainLionData
               forKey:@"aboutAnimal"];
    
    //Add the Mountain lion to the zoo
    [zoo addObject:animal];
    
    //Create an animal dictionary entry for a kinkajou
    animal = [NSMutableDictionary dictionary];
    [animal setObject:@"Kinkajou"
               forKey:@"species"];
    [animal setObject:@"Fingers"
               forKey:@"name"];
    [animal setObject:[NSNumber numberWithInt:3]
               forKey:@"yearsAtZoo"];
    [animal setObject:[NSDate dateWithNaturalLanguageString:@"November 10, 2007"]  
               forKey:@"birthDate"];
    [animal setObject:[NSNumber numberWithBool:NO]
               forKey:@"endangered"];
    [animal setObject:[NSNumber numberWithFloat:11.8]
               forKey:@"weight"];
    [animal setObject:kinkajouFood
               forKey:@"diet"];
    [animal setObject:kinkajouData
               forKey:@"aboutAnimal"];
    
    //Add the kinkajou to the zoo
    [zoo addObject:animal];
    
    
    //Write contents of list to a file
    [zoo writeToFile:@"/tmp/zoo.plist"
          atomically:YES];
     
    
    
     for (NSDictionary *d in zoo) {
         
         //Format the birthdate so it is easy to read
         NSDate *dateToFormat = [d objectForKey:@"birthDate"];
         NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
         [dateFormat setDateFormat:@"MMMM d, YYYY"];
         NSString *formattedDate = [dateFormat stringFromDate:dateToFormat];
         
         //Make the contents of the about files readable text
         NSString *asciiAboutAnimal = [[NSString alloc] 
                                          initWithData:[d objectForKey:@"aboutAnimal"]
                                          encoding:NSUTF8StringEncoding];

         //Log the contents of the list                                                                 
         NSLog(@"\n\nAnimal Description\n species: %@\n name: %@\n Years at zoo: %@\n Birthday: %@\n Weight: %@\n Endangered: %@\n Diet: %@\n Species Information: %@\n", [d objectForKey:@"species"], [d objectForKey:@"name"], [d objectForKey:@"yearsAtZoo"], formattedDate, [d objectForKey:@"weight"], [d objectForKey:@"endangered"], [d objectForKey:@"diet"], asciiAboutAnimal);
    }
    
   
    
}
return 0;

}[/code]

NSLog Output

[code]2012-08-03 20:25:03.050 PropertyListChap26Challenge[4787:403] The mountain lion file from the disk has 2083 bytes
2012-08-03 20:25:03.053 PropertyListChap26Challenge[4787:403] The kinkajou file fromt he disk has 1706 bytes:
2012-08-03 20:25:03.107 PropertyListChap26Challenge[4787:403]

Animal Description
species: Mountain Lion
name: Kitty
Years at zoo: 5
Birthday: May 4, 2001
Weight: 250.7
Endangered: 1
Diet: (
“fluffy bunnies”,
“begging dogs”,
“undisciplined children”
)
Species Information: {\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf470
{\fonttbl\f0\fswiss\fcharset0 ArialMT;}
{\colortbl;\red255\green255\blue255;\red38\green38\blue38;}
\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\sa200

\f0\fs24 \cf2 This powerful predator roams the Americas, where it is also known as a puma, cougar, and catamount. This big cat of many names is also found in many habitats, from Florida swamps to Canadian forests.
Mountain lions like to prey on deer, though they also eat smaller animals such as coyotes, porcupines, and raccoons. They usually hunt at night or during the gloaming hours of dawn and dusk. These cats employ a blend of stealth and power, stalking their prey until an opportunity arrives to pounce, then going for the back of the neck with a fatal bite. They will hide large carcasses and feed on them for several days.
Mountain lions once roamed nearly all of the United States. They were prized by hunters and despised by farmers and ranchers who suffered livestock losses at their hands. Subsequently, by the dawn of the 20th century, mountain lions were eliminated from nearly all of their range in the Midwest and Eastern U.S.'97though the endangered Florida panther survives.
Today, whitetail deer populations have rebounded over much of the mountain lion’s former range and a few animals have appeared in more eastern states such as Missouri and Arkansas. Some biologists believe that these big cats could eventually recolonize much of their Midwest and Eastern range’97if humans allow them to do so. In most western U.S. states and Canadian provinces, populations are considered sustainable enough to allow managed sport hunting.
Mountain lions require a lot of room’97only a few cats can survive in a 30-square-mile (78-square-kilometer) range. They are solitary and shy animals, seldom seen by humans. While they do occasionally attack people’97usually children or solitary adults’97statistics show that, on average, there are only four attacks and one human fatality each year in all of the U.S. and Canada.}
2012-08-03 20:25:03.109 PropertyListChap26Challenge[4787:403]

Animal Description
species: Kinkajou
name: Fingers
Years at zoo: 3
Birthday: November 10, 2007
Weight: 11.8
Endangered: 0
Diet: (
bananas,
honey,
“small rodents”,
“tourist’s fingers”
)
Species Information: {\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf470
{\fonttbl\f0\fswiss\fcharset0 ArialMT;}
{\colortbl;\red255\green255\blue255;\red38\green38\blue38;}
\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\sa200

\f0\fs24 \cf2 Kinkajous live in the tropical forests of Central and South America, where they spend most of their time in the trees. They are able to turn their feet backwards to run easily in either direction along branches or up and down trunks. The kinkajou also has a prehensile (gripping) tail that it uses much like another arm. Kinkajous often hang from this incredible tail, which also aids their balance and serves as a cozy blanket while the animal sleeps high in the canopy.
Though many of its features and traits sound like those of a primate, the kinkajou is actually related to the raccoon.
Kinkajous are sometimes called honey bears because they raid bees’ nests. They use their long, skinny tongues to slurp honey from a hive, and also to remove insects like termites from their nests. Kinkajous also eat fruit and small mammals, which they snare with their nimble front paws and sharp claws. They roam and eat at night, and return each morning to sleep in previously used tree holes.
Kinkajous form treetop groups and share social interactions such as reciprocal grooming. They are vocal animals’97though seldom seen, they are often heard screeching and barking in the tropical forest canopy.
Female kinkajous give birth to one offspring in spring or summer. The baby is born with its eyes shut and cannot see for a month. It develops quickly, however, and by the end of the second month, it is already able to hang upside down from its tail.}[/code]


#2

Making it to chapter 26 is quite an accomplishment for a new programmer. Congratulations, and welcome.

Question 1: there’s no built-in way to display a boolean as a yes/no value. Internally, a boolean is just a 1 or a 0, as I’m sure you’ve learned by playing with NSLog.

Here’s what I would suggest as a workaround (warning: didn’t try to compile this):

NSLog(@"...Endangered: %@...", [[d objectForKey:@"endangered"]boolValue] == YES ? @"Yes" : @"No");

I don’t recall whether this concept has been tackled yet in the book, but the ? : construct is like an inline if/else structure.

So, if the boolean value you stored under the key “endangered” is a true value, replace %@ with the string “Yes”, otherwise “No”.

Will tackle Question 2 in another response, need to mull on this a bit.


#3

[quote]Formatting Issue 1: I have spent an embarrassingly long amount of time trying to get my boolean values to print as a ‘yes’ or ‘no’ instead of a ‘0’ or ‘1’ in my NSLog results. Can somebody please tell me if/how this can be done?
[/quote]
There isn’t any easy way, such as a format descriptor in NSLog or printf, to do it; however you can write a method or a block to do it:

//  main.m
#import <Foundation/Foundation.h>

// use a class method
@interface MyUtil
+ (const char*)stringFromBool:(BOOL)v;
@end

// use a free function
const char *StringFromBool (BOOL);

// ------------------------------------------------------
#pragma mark -

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        BOOL y = YES, n = NO;
        // use the binary operator
        NSLog (@"(0)---> %s", y ? "Yes" : "No");
        NSLog (@"(0)---> %s", n ? "Yes" : "No");

        NSLog (@"(1)---> %s", [MyUtil stringFromBool:y]);
        NSLog (@"(1)---> %s", [MyUtil stringFromBool:n]);
        NSLog (@"(2)---> %s", StringFromBool (y));
        NSLog (@"(2)---> %s", StringFromBool (y));
        
        // use a cryptic block
        NSLog (@"(3)---> %s", ^(BOOL v) {return v ? "Yes" : "No";} (y));
        NSLog (@"(3)---> %s", ^(BOOL v) {return v ? "Yes" : "No";} (n));
        
        // use a block - my favourite
        const char* (^boolToStr)(BOOL) = ^ (BOOL v) {
            return (const char*)(v ? "Yes" : "No");
        };
        
        NSLog (@"(4)---> %s", boolToStr (y));
        NSLog (@"(4)---> %s", boolToStr (n));
    }
    return 0;
}

// ------------------------------------------------------
#pragma mark -

@implementation MyUtil
+ (const char*)stringFromBool:(BOOL)v
{
    return StringFromBool(v);
}

const char *StringFromBool (BOOL v)
{
    return v ? "Yes" : "No";   
}

@end

Yes - use a for loop to iterate through members of the array and print them:

NSArray *hexSymbols = [NSArray arrayWithObjects:@"A", @"B", @"C", @"D", @"E", @"F", nil];
for (NSObject *hexSymbol in hexSymbols)
{
    NSLog (@"%@", hexSymbol);
}

#4

Ok, so after some experimentation, here’s the fun way to handle formatting question #2. ibex10’s approach is perfectly sound, but this was more entertaining to explore.

First, let’s review what’s happening with NSLog. You’re using the %@ placeholder, which means that each argument is being treated as an object; specifically, it means that each object is being passed the description message.

So at the moment, you’re at the mercy of whatever the Objective-C library wants to do with each type’s built-in description method.

Objective-C allows developers to override functionality in other classes by way of categories. Thus, you can overrule Apple and provide your own array string representation.

(Note that this approach won’t work for your boolean formatting problem because the number object isn’t actually of type NSNumber, but a private subtype.)


@interface NSArray (MyCategory)

- (NSString *)description;

@end

@implementation NSArray (MyCategory)

- (NSString *)description
{
    NSMutableString *desc = [NSMutableString string];
    [self enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
        [desc appendString:[NSString stringWithFormat:@"%@, ",
                            [object description]]];
    }];
    return [desc length] > 2 ? [desc substringToIndex:[desc length] - 2] : desc;
}

@end

Throwing that into your current code will provide a comma-separated, horizontal list of objects from the array.

Disclaimer: the above advice should not be treated as sound, professional development recommendations. Your mileage may vary, and your co-workers may throw you under a bus. Literally.


#5

Macintux -

I had been SO close to your solution. I just hadn’t added ‘boolValue’ after my dictionary value. It makes sense that you would need this now that I see it. (Maybe I am starting to understand this after all . . . at least just a little bit.) Your solution compiled without a problem and now I am seeing ‘Yes’/‘No’ values.

MUCH HAPPINESS!

Thank you


#6

ibex10 -

Your code got me SO close on the array printing issue. With your code, as written, each item was printing on a separate line. I created a string with the individual values separated by commas and printed the string. I couldn’t have gotten there without your input. Thank you, thank you!

[code] //Create a String object to hold a nicely formatted food string
NSMutableString *formattedFoodList = [[NSMutableString alloc] init];

         NSArray *hexSymbols = [d objectForKey:@"diet"];
         for (NSObject *hexSymbol in hexSymbols)
         {
             [formattedFoodList appendFormat:@"%@",hexSymbol];
             [formattedFoodList appendFormat:@", "];
         }
         [formattedFoodList deleteCharactersInRange:NSMakeRange([formattedFoodList length]-2, 2)];
         [/code]

Now at the risk of being thrown under a bus, I am going to attempt to decipher macintux’s solution to this problem. Thankfully, I live/work in the suburbs where there are not many buses under which to be thrown!