I'm using the MKPlacemark class to populate a label with location specifics. When calling the AdministrativeArea property, the entire name of the US state is returned (e.g. West Virginia). Is there a way to return ONLY the initials (e.g. WV)?
Apple's docs for that property suggest that there's no real definition for what it can contain. Your best bet is probably to create a function to map from the full state name to the 2 letter code, and pass the result of the property through it before display. I would default to the original string if you don't get a match.
-(NSString *)codeFromState:(NSString *)state {
NSArray *map = [NSArray arrayWithObjects:#"Alabama",#"AL", #"Alaska",#"AK", ... #"Wyoming", #"WY", nil];
for (int i = 0; i <[map count]; i+=2) {
if ([state compare:[map objectAtIndex:i]] == NSOrderedSame) {
return [map objectAtIndex:i+1];
}
}
return state;
}
Related
I have an array of NSDictionary. Each array item has a key named "Name". Now I want to remove duplicate entries based on this name value.
This work perfectly:
aMyArray = [aMyArray valueForKeyPath:#"#distinctUnionOfObjects.Name"];
The problem are that result array still contains duplicates string, based on the case. Ex: [#"Franck", "franck"]
How can I remove these duplicate?
Thanks
You could try to do this
// in your class implementation
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[self class]]) {
return NO;
}
typeof(self) obj = (typeof(self))object;
return ([self.Name caseInsensitiveCompare:obj.Name] == NSOrderedSame);
}
- (NSUInteger)hash
{
return [[self.Name lowercaseString] hash];
}
// and then
NSSet *distinctObjects = [[NSSet alloc] initWithArray:array];
NSArray *result = distinctObjects.allObjects;
Alternatively you could customise this KVC collection operator by swizzling valueForKeyPath: to parse your custom DSL, possibly winding up with something like
aMyArray = [aMyArray valueForKeyPath:#"#distinctUnionOfObjects[caseInsensitive].Name"];
which doesn't seem to be a good idea for me, but it certainly a viable solution to your problem.
How do I refactor similar methods for the following (Objective C)?
- (void)insertNewSong:(Song *)newSong forArtist:(Artist *)artist {
NSMutableArray *newSongList = [[artist songs] mutableCopy];
BOOL hasInserted = NO;
for (int i = 0; i < [[artist songs] count]; i++) {
Song *existingSong = [[artist songs] objectAtIndex:i];
if ([[newSong title] caseInsensitiveCompare:[existingSong title]] == NSOrderedAscending) {
[newSongList insertObject:newSong atIndex:i];
hasInserted = YES;
break;
}
}
if (hasInserted == NO) {
[newSongList addObject:newSong];
}
artist.songs = newSongList;
}
- (void)insertNewArtistToSongList:(Artist *)newArtist {
BOOL hasInserted = NO;
for (int i = 0; i < [_artists count]; i++) {
Artist *existingArtist = [_artists objectAtIndex:i];
if ([[newArtist name] caseInsensitiveCompare:[existingArtist name]] == NSOrderedAscending) {
[_artists insertObject:newArtist atIndex:i];
hasInserted = YES;
break;
}
}
if (hasInserted == NO) {
[_artists addObject:newArtist];
}
}
For the insertNewSong method, a NSMutableArray [artist songs] containing each Song object is used.
For the insertNewArtist method, a NSMutableArray instance variable _artists containing each Artist Object is used.
Both methods insert an object into an NSMutableArray by comparing the text property of the input object against the text property found within the arrays.
Currently the above methods contain some duplication but is easy to understand (in my case). I was thinking whether there might be a way of simplifying it into a more general method, and does not hurt readability?
There is no general rule, but here are some general rules:
Sometimes it makes sense to combine code like this, sometimes not. Lots of pluses/minuses.
Sometimes it's best to abstract PART of the operation, and leave the other part custom.
Generally, if you have a lot of "if thingA then do this, else that" logic, you've done it wrong (or should not do it at all).
It's best when you can write a single routine and just pass in different parameters (that aren't simply Boolean switches) to differentiate the multiple cases.
It's hard.
And, as a general rule, I don't try too hard to abstract until I have the third instance of nearly the same logic.
(Generally speaking.)
I have a cocos2d game setup which uses basic inheritance setup which is similar to this;
> Property (Base class)
> - Office : Property
> - Warehouse : Property
> - Bank : Property
All the properties live in an array listOfProperties and I am trying to print out each property in a NSLog, but I am not sure how to do it.
For example,
// Create an office
Office *o = [Office alloc] init];
[o setName:#"Some office"];
[city.listOfProperties addObject:o];
[o release];
// Debug output all the properties in the game
// city.listOfProperties in an array of Property objects
for (Property *prop in city.listOfProperties) {
// I want to print out all the different properties here
if ([prop isKindOfClass:[Office class]]==YES)
{
NSLog(#"Office");
NSLog(#"office.name = %#", prop.name); // Prop name does not work
}
} // next
The problem is that some properties do not share the same attributes. For example, an Office might have "floors" but a Warehouse has "capacity".
What I'm needing is to print out all the different properties, but I am not sure how to change the focus from the pointer prop to a pointer for the specific class (ie: Office).
I need them all to live in listOfProperties so that I can use it later on in a CCMenu and want to avoid splitting them up into seperate arrays which will be very hard for me to manage.
Is there a way to do this?
Thanks
Do typecasting like this.
for (Property *prop in city.listOfProperties) {
if ([prop isKindOfClass:[Office class]])
{
Office* officeObject = (Office*) prop;
NSLog(#"office.name = %#", officeObject.name);
}
if ([prop isKindOfClass:[Warehouse class]])
{
Warehouse* WarehouseObject = (Warehouse*) prop;
NSLog(#"Warehouse.name = %#", WarehouseObject.name);
}
if ([prop isKindOfClass:[Bank class]])
{
Bank* BankObject = (Bank*) prop;
NSLog(#"Bank.name = %#", BankObject.name);
}
}
You can have whatever variables you want to log. using name for example.
I have set up my simple Xcode project with a table that is binded to an array controller. It works fine if the array controller is full of entities with a string attribute. However I want to change the attribute to a BOOL and have the table show the string "true" or "false" based on the BOOL.
I have overrided the following two methods from NSFormatter:
-(NSString*) stringForObjectValue:(id)object {
//what is the object?
NSLog(#"object is: %#", object);
if(![object isKindOfClass: [ NSString class ] ] ) {
return nil;
}
//i'm tired....just output hello in the table!!
NSString *returnStr = [[NSString alloc] initWithFormat:#"hello"];
return returnStr;
}
-(BOOL)getObjectValue: (id*)object forString:string errorDescription:(NSString**)error {
if( object ) {
return YES;
}
return NO;
}
So the table gets populated with "hello" if the attribute is a string however if I switch it to a boolean, then the table gets populated with lots of blank spaces.
I don't know if this helps but on the line where I'm outputting the object, it outputs __NSCFString if the attribute is a string and "Text Cell" if I switch the attribute to a boolean. This is something else I don't understand.
Ok, it's not 100% clear what you're trying to do from the code, but first things first - BOOL is not an object, it's basically 0 or 1, so to place BOOL values into an array, you're probably best off using NSNumber:
NSNumber *boolValue = [NSNumber numberWithBool:YES];
and placing these into your array. Now you want to change your method:
-(NSString*) stringForObjectValue:(id)object {
NSNumber *number = (NSNumber *)object;
if ([number boolValue] == YES)
return #"true";
else
return #"false";
}
There's a few things here - for example, you want to avoid passing around id references if you can (if you know all your objects in the NSArray are NSNumber, you shouldn't need to).
I have the following test case in my iOS application :
-(void) testTwoDefaultUsersExist
{
NSString * expected;
NSString * actual;
expected = #"John Smith";
actual = [[[userService getAllUsers]objectAtIndex:0] fullName];
STAssertEqualObjects(expected, actual, #"Not equal");
expected = #"Dave Brown";
actual = [[[userService getAllUsers]objectAtIndex:1] fullName];
STAssertEqualObjects(expected, actual, #"Not equal");
}
The above just checks that my call to [userService getAllUsers] returns 2 User objects, one with a name of John Smith, the other with Dave Brown. This appears to work fine for this scenario, but I have other cases where that ordering may change, so John may be placed in index 1 rather than 0
Question : How can I assert that the NSMutableArray, being returned from the call to [userService getAllUsers] contains those 2 objects, regardless of ordering?
Can you not simply use the NSArray method -containsObject:? An NSMutableArray is still an NSArray, so you can do:
NSArray * expected = [NSArray arrayWithObjects:#"John Smith", #"Dave Brown", nil];
NSArray * actual = [[userService getAllUsers] valueForKey:#"fullName"];
for(NSString * name in expected) {
STAssertTrue([actual containsObject:name], #"Missing name");
}
Note the (ab)use of -valueForKey: to transform an array of user objects into an array of NSString objects, making the -containsObject: call simpler. This will only work if your user object is key-value coding compliant for the fullName property.
NSMutableArray always contains the elements as you insert them to the array
You can iterate over the elements that you insert and test if they're at the NSArray using:
- (NSUInteger)indexOfObject:(id)anObject
If the object is not found it returns NSNotFound, that can be used with the Unit Test Framework that you choice
Greetings
Assert on equality like this
NSAssert1([userService getAllUsers].count == 2, #"SomeDescription", nil);
If you want to search an array for the existence of some strings, use the following function
- (BOOL) containsAllNames:(NSArray*)arrToSearch namesToSearch:(NSArray*)arr
{
BOOL containsAll = YES;
for (NSString *name in arr) {
BOOL containsCurrent = NO;
for (NSString *nameToSearch in arrToSearch) {
if ([name isEqualToString:nameToSearch]) {
containsCurrent = YES;
break;
}
}
if (!containsCurrent) {
containsAll = NO;
}
}
return containsAll;
}
Call it like
NSArray *toSearch = [NSArray arrayWithObjects:#"John Smith", #"Dave Brown", nil];
[self containsAllNames:YourArray namesToSearch:toSearch];