My "saveData" dictionary looks like this:
{
Adresser = {
"completed_status" = {
finished = 4;
total = 5;
};
...
}
And my "dOrder" dictionary looks like this
{
id = 1924;
name = Adresser;
order = 0;
}
And the snippet of code
NSDictionary * saveData = [self dbReadFormData:_objectID];
for (NSDictionary * dOrder in dictFormSections[#"order"]) {
NSString * segName = dOrder[#"name"];
NSLog(#"finished: %#", saveData[segName][#"completed_status"][#"finished"]);
NSLog(#"total: %#", saveData[segName][#"completed_status"][#"total"]);
NSString * vFinished = [saveData objectForKey:saveData[segName][#"completed_status"][#"finished"]];
NSString * vTotal = [saveData objectForKey:saveData[segName][#"completed_status"][#"total"]];
NSString * sSection = [NSString stringWithFormat:#"%# - %# / %#", segName, vFinished, vTotal];
[rx insertSegmentWithTitle:sSection atIndex:rx.numberOfSegments animated:NO];
}
The NSLog's return the correct values. (4 and 5) But the NSStrings are nil.
What am I doing wrong?
Thank you!
As mentioned by #Arc676, you have :
NSLog(#"finished: %#", saveData[segName][#"completed_status"][#"finished"]);
and
NSString * vFinished = [saveData objectForKey:saveData[segName][#"completed_status"][#"finished"]];
However, they are not the same. Shouldn't vFinished be like:
NSString* vFinished = saveData[segName][#"completed_status"][#"finished"];
Related
I am replacing my obsolete ABAdressBook code with the current CNContact framework in Objective C. I could sort out most of it, except for the Home City part, so lets focus on that.
Currently I have this code:
-(NSArray *)getLandAddressesForContactIOS6:(ABRecordRef)recordRef {
ABMultiValueRef addresses = ABRecordCopyValue(recordRef, kABPersonAddressProperty);
NSMutableArray *formattedAddressesResponse = [NSMutableArray array];
for(CFIndex i = 0; i < ABMultiValueGetCount(addresses); i++) {
NSString *label = (__bridge NSString *)ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(addresses, i));
NSDictionary *addressComponents = (__bridge NSDictionary*)ABMultiValueCopyValueAtIndex(addresses, i);
NSString *street = [addressComponents objectForKey:(NSString *)kABPersonAddressStreetKey];
NSString *city = [addressComponents objectForKey:(NSString *)kABPersonAddressCityKey];
NSString *formattedAddress = ABCreateStringWithAddressDictionary(addressComponents, YES);
NSMutableDictionary *currentAddressResponse = [NSMutableDictionary dictionaryWithObjectsAndKeys:
label, #"type",
label, #"label",
nil];
if (street != nil) {
[currentAddressResponse setObject:street forKey:#"street"];
}
if (city != nil) {
[currentAddressResponse setObject:city forKey:#"city"];
}
if (formattedAddress != nil) {
[currentAddressResponse setObject:formattedAddress forKey:#"formattedAddress"];
}
[formattedAddressesResponse addObject:currentAddressResponse];
}
return formattedAddressesResponse;
}
That code is deprecated for iOS 9+ so the closest I have got to get the home city with the new Contacts framework is:
-(NSArray *)getLandAddressesForContactIOS10:(CNContact*)recordRef {
NSArray <CNLabeledValue<CNPostalAddress *> *> *addresses = recordRef.postalAddresses;
NSMutableArray *formattedAdressResponse = [NSMutableArray array];
for(CFIndex i = 0; i < addresses.count; i++) {
CNLabeledValue *addressi = [addresses objectAtIndex:i];
//NSString *city = addressi.??????; //Stuck here, don't know what else to do
How can I extract the City name from a CNContact??
-(void )getLandAddressesForContactIOS10:(CNContact*)contact
{
for (CNLabeledValue<CNPostalAddress*>* labeledValue in contact.postalAddresses)
{
NSLog(#"%#",labeledValue.value.city);
NSLog(#"%#",labeledValue.value.street);
NSLog(#"%#",labeledValue.value.state);
NSLog(#"%#",labeledValue.value.postalCode);
NSLog(#"%#",labeledValue.value.ISOCountryCode);
}
}
NSArray *addresses = (NSArray*)[contact.postalAddresses valueForKey:#"value"];
if (!(addresses == nil) && addresses.count > 0)
{
for (CNLabeledValue<CNPostalAddress*>* labeledValue in contact.postalAddresses)
{
NSString *city = labeledValue.value.city;
NSLog(#"City = %#",city);
NSString *street = labeledValue.value.street;
NSLog(#"Street = %#",street);
NSString *state = labeledValue.value.state;
NSLog(#"State = %#",state);
NSString *postalCode = labeledValue.value.postalCode;
NSLog(#"PostalCode = %#",postalCode);
NSString *ISOCountryCode = labeledValue.value.ISOCountryCode;
NSLog(#"ISOCountryCode = %#",ISOCountryCode);
}
}
else
{
NSLog(#"No addresses for name = %#",strname);
}
Trying to create a method with only one parameter that may accept NSString or int.
Here's what I did so far:
-(NSString*)LocalizeNumber:(void*)TheNumber{
BOOL IsVarInt = false;
NSString * Num = "";
if(IsVarInt){
Num = [NSString stringWithFormat:#"%i",(int)TheNumber];
}else{
Num = [NSString stringWithFormat:#"%#",(__bridge NSString*)TheNumber];
}
//rest of code...
}
And this is how I call this method:
if passing int:
[self LocalizeNumber:(void*)150];
if passing NSString:
[self LocalizeNumber:#"150"];
The problem is that I still don't know how to know if the parameter "TheNumber" is NSString or int.
Thank you.
While I suggest you rethink your approach, your goal can be achieved as follows:
- (NSString *)localizeNumber:(id)number {
NSString *num = nil;
if ([number isKindOfClass:[NSNumber class]]) {
num = [number stringValue];
} else if ([number isKindOfClass:[NSString class]]) {
num = number;
} else {
// oops - bad value
}
// rest of code using num
}
Then you can call the method as follows:
NSString *someString = #"Hello";
NSString *result = [self localizeNumber:someString];
or:
int someInt = 42;
NSString *result = [self localizeNumber:#(someInt)];
You cannot tell between an object type and a plain primitive. However, you can easily tell between two object types if you pass an int passed in NSNumber wrapper, like this:
-(NSString*)LocalizeNumber:(id)TheNumber {
NSString *Num = #"";
if ([TheNumber isKindOfClass:[NSSTRING class]]) {
Num = [NSString stringWithFormat:#"%#", TheNumber];
} else if ([TheNumber isKindOfClass:[NSNumber class]]) {
Num = [NSString stringWithFormat:#"%i",[TheNumber intValue]];
}
//rest of code...
}
You could use categories to add -stringValue to NSString:
#implementation NSString (LocalizedNumber)
-(NSString*)stringValue
{
return self ;
}
#end
Then you can call:
NSString * localizedNumber = [<number or string object> stringValue]
There is no safe way to tell an int from an NSString reference.
I'm breaking my head on why descending order sort is not working with the following code. I wanted to limit by top 5 scores and other logic. The scores would look like this: 22/30, 12/18, 34/38, 23/32 etc. I added/removed SortDescriptor to sort by descending order and it seems to work for the first 3 items but then is not sorting properly. Can somebody help?
- (NSMutableArray*) method1:(NSString *) mode byScore: (NSString *) score
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *array = [[defaults objectForKey:mode]mutableCopy];
if (!array)
{
array = [[NSMutableArray alloc] init];
}
NSLog(#"The content of array is%#", array);
if ([array count] < 5)
{
if (![array containsObject:score])
{
[array addObject:score];
// Need to sort here. But not too sure this is the right place
NSLog(#"The content of the sorted array upto 5 is%#", array);
}
}
else
{
if (![array containsObject:score])
{
if ([array lastObject] < score)
{
[array addObject:score];
// Need to sort here before I remove the last object
[array removeLastObject];
NSLog(#"The content of the sorted array else is%#",array);
}
}
}
[defaults setObject:array forKey:mode];
[defaults synchronize];
// I want the array in NSUserDefaults to be sorted in desc order
// don't know what to return here ==> the array object or the defaults object cast to NSMutableArray?
}
Helper function
static NSComparisonResult CompareFloats( float a, float b )
{
if ( a < b ) { return NSOrderedAscending ; }
else if ( a > b ) { return NSOrderedDescending ; }
return NSOrderedSame ;
}
Category on NSString
#implementation NSString (Stuff)
-(float)floatValueForFraction
{
NSArray * components = [ self componentsSeparatedByString:#"/" ] ;
return [ components[0] floatValue ] / [ components[1] floatValue ] ;
}
#end
Your method:
- (void)addScore:(NSString*)score forMode:(NSString*)mode
{
NSUserDefaults * defaults = [ NSUserDefaults standardUserDefaults ] ;
NSArray * scores = [ defaults objectForKey:mode ] ;
scores = scores ? [ scores arrayByAddingObject:score ] : #[ score ] ;
scores = [ scores sortedArrayUsingComparator:^(NSString * a, NSString * b){
return CompareFloats( [ a floatValueForFraction ], [ b floatValueForFraction ] ) ;
}]
if ( scores.count > 5 ) { scores = [ scores subarrayWithRange:(NSRange){ .length = 5 } ] ; }
[ default setObject:scores forKey:mode ] ;
}
If you want the updated high scores after calling this method, just use [ [ NSUserDefaults standardUserDefaults ] objectForKey:<mode> ]. It's better to have your methods just do one thing.
One approach to sorting array:
First define a block getNumeratorAndDenominatorFromScoreString as follows:
BOOL (^getNumeratorAndDenominatorFromScoreString)(NSString *, NSInteger *, NSInteger *) = ^(NSString *scoreString, NSInteger *numeratorOut, NSInteger *denominatorOut) {
BOOL res = NO;
NSArray *components = [scoreString componentsSeparatedByString:#"/"];
if (components &&
[components count] == 2) {
res = YES;
if (numeratorOut) {
NSNumber *numeratorNumber = [components objectAtIndex:0];
*numeratorOut = [numeratorNumber integerValue];
}
if (denominatorOut) {
NSNumber *denominatorNumber = [components objectAtIndex:1];
*denominatorOut = [denominatorNumber integerValue];
}
}
return res;
};
Then use this block together with -[NSArray sortedArrayUsingComparator] to sort array:
NSArray *sortedArray = [array sortedArrayUsingComparator: ^(id obj1, id obj2) {
NSComparisonResult res = NSOrderedSame;
NSString *score1 = (NSString *)obj1;
NSString *score2 = (NSString *)obj2;
NSInteger numerator1, denominator1, numerator2, denominator2;
BOOL res1, res2;
res1 = getNumeratorAndDenominatorFromScoreString(score1, &numerator1, &denominator1);
res2 = getNumeratorAndDenominatorFromScoreString(score2, &numerator2, &denominator2);
if (res1
&& res2) {
CGFloat value1 = ((CGFloat)numerator1)/((CGFloat)denominator1);
CGFloat value2 = ((CGFloat)numerator2)/((CGFloat)denominator2);
if (value1 > value2) {
res = NSOrderedDescending;
} else if (value1 < value2) {
res = NSOrderedAscending;
}
}
return res;
}];
This will order array from least to greatest. To order from greatest to least, just replace
if (value1 > value2) {
res = NSOrderedDescending;
} else if (value1 < value2) {
res = NSOrderedAscending;
}
with
if (value1 > value2) {
res = NSOrderedAscending;
} else if (value1 < value2) {
res = NSOrderedDescending;
}
A readable structure for this method would be, in [mostly not] pseudocode
- (void)addScoreToHighscores:(NSString *)score withMethod:(NSString *)mode
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *currentHighscores = [defaults arrayForKey:mode];
if (!currentHighscores) currentHighscores = [NSArray array];
if (![currentHighscores containsObject:score]) {
currentHighscores = [currentHighscores arrayByAddingObject:score];
//sort currentHighscores: adapt the above code so that we have
BOOL (^getNumeratorAndDenominatorFromScoreString)(NSString *, NSInteger *, NSInteger *) = //as above
NSArray *newHighscores = [currentHighscores sortedArrayUsingComparator:^(id obj1, id obj2) {
//as above
}];
//truncate newHighscores
if ([newHighscores count] > 5) {
newHighscores = [newHighscores subarrayWithRange:NSMakeRange(0,5)];
}
[defaults setObject:newHighscores forKey:mode];
} else {
//since score is already in currentHighscores, we're done.
return;
}
}
If you need to screen out scores the strings for which are not equal but the evaluations of the fractions for which are equal (#"1/2" and #"5/10"), you'll need to be more clever.
Here is the full code sketched out above:
- (void)addScoreToHighscores:(NSString *)score withMethod:(NSString *)mode
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *currentHighscores = [defaults arrayForKey:mode];
if (!currentHighscores) currentHighscores = [NSArray array];
if (![currentHighscores containsObject:score]) {
currentHighscores = [currentHighscores arrayByAddingObject:score];
//sort currentHighscores: adapt the above code so that we have
BOOL (^getNumeratorAndDenominatorFromScoreString)(NSString *, NSInteger *, NSInteger *) = ^(NSString *scoreString, NSInteger *numeratorOut, NSInteger *denominatorOut) {
BOOL res = NO;
NSArray *components = [scoreString componentsSeparatedByString:#"/"];
if (components &&
[components count] == 2) {
res = YES;
if (numeratorOut) {
NSNumber *numeratorNumber = [components objectAtIndex:0];
*numeratorOut = [numeratorNumber integerValue];
}
if (denominatorOut) {
NSNumber *denominatorNumber = [components objectAtIndex:1];
*denominatorOut = [denominatorNumber integerValue];
}
}
return res;
};
NSArray *newHighscores = [currentHighscores sortedArrayUsingComparator:^(id obj1, id obj2) {
NSComparisonResult res = NSOrderedSame;
NSString *score1 = (NSString *)obj1;
NSString *score2 = (NSString *)obj2;
NSInteger numerator1, denominator1, numerator2, denominator2;
BOOL res1, res2;
res1 = getNumeratorAndDenominatorFromScoreString(score1, &numerator1, &denominator1);
res2 = getNumeratorAndDenominatorFromScoreString(score2, &numerator2, &denominator2);
if (res1
&& res2) {
CGFloat value1 = ((CGFloat)numerator1)/((CGFloat)denominator1);
CGFloat value2 = ((CGFloat)numerator2)/((CGFloat)denominator2);
if (value1 > value2) {
res = NSOrderedDescending;
} else if (value1 < value2) {
res = NSOrderedAscending;
}
}
return res;
}];
//truncate newHighscores
if ([newHighscores count] > 5) {
newHighscores = [newHighscores subarrayWithRange:NSMakeRange(0,5)];
}
[defaults setObject:newHighscores forKey:mode];
} else {
//since score is already in currentHighscores, we're done.
return;
}
}
I am having a requirement in which I have 5 keys stored in a NSDictionary corresponding to which I have stored 5 values from different arrays. My data structure looks like:
Dictionary is {
1 = (
{
SubRegionName = "abc";
WalkId = 123;
WalkName = xyz;
},
{
},....
2 = (
{
SubRegionName = "abc";
WalkId = 123;
WalkName = xyz;
},
{
},....
3 = (
{
SubRegionName = "abc;
WalkId = 123;
WalkName = xyz;
},
{
},....
4 = (
{
SubRegionName = "abc";
WalkId = 123;
WalkName = xyz;
},
{
},....
5 = (
{
SubRegionName = "abc";
WalkId = 123;
WalkName = xyz;
},
{
},
)
The above data I am getting from sqlite according to some Id say tempId(1,2,3,4,5) and I want to use them separately. As I have to display the walkName as cell.textlabel.text and SubregionName as cell.detailtext.text in tableview.
I am not getting how to access this data from the dictionary. Can anyone please suggest me right way to do it.
My code is:
// For Database queries (To get data from database) ***********
-(void)getMainRegions
{
sqlite3 *walkNameDB;
if (sqlite3_open([[self databasePath] UTF8String], &walkNameDB) != SQLITE_OK) {
sqlite3_close(walkNameDB);
NSAssert(0, #"Failed to open Walk name database");
}
NSString *regionQuery = #"SELECT Rid,RName from MainRegions";
sqlite3_stmt *teststatement = nil;
// NSLog(#"%d",sqlite3_prepare_v2(walkNameDB,[regionQuery UTF8String], -1, &teststatement, nil));
if (sqlite3_prepare_v2(walkNameDB,[regionQuery UTF8String], -1, &teststatement, nil) == SQLITE_OK)
{
RNameArray = [[NSMutableArray alloc] init];
RIdArray = [[NSMutableArray alloc] init];
while( sqlite3_step(teststatement) == SQLITE_ROW )
{
NSNumber *RId;
int temp1 = (int)sqlite3_column_int(teststatement, 0);
RId = [[NSNumber alloc] initWithInt:temp1];
char *RNameCharacter;
RNameCharacter = (char *) sqlite3_column_text(teststatement, 1);
NSString *RNameString = [[NSString alloc] initWithUTF8String:RNameCharacter];
[RNameArray addObject:RNameString];
[RIdArray addObject:RId];
}
}
[self getWalks];
}
-(void)getWalks
{
sqlite3 *walkNameDB;
if (sqlite3_open([[self databasePath] UTF8String], &walkNameDB) != SQLITE_OK) {
sqlite3_close(walkNameDB);
NSAssert(0, #"Failed to open Walk name database");
}
RegionWalksDictionary = [[NSMutableDictionary alloc] init];
for(regionId in RIdArray)
{
regionWalkArray = [[NSMutableArray alloc] init];
NSString *walkQuery = [[NSString alloc] initWithFormat:#"SELECT Wid,WName,SName from Walks,SubRegions WHERE Walks.Sid=SubRegions.Sid AND Rid = %d",[regionId integerValue] ];
sqlite3_stmt *walkstatement = nil;
// NSLog(#"%d",sqlite3_prepare_v2(walkNameDB,[walkQuery UTF8String], -1, &walkstatement, nil));
if (sqlite3_prepare_v2(walkNameDB,[walkQuery UTF8String], -1, &walkstatement, nil) == SQLITE_OK)
{
while( sqlite3_step(walkstatement) == SQLITE_ROW )
{
NSNumber *WId;
int temp1 = (int)sqlite3_column_int(walkstatement, 0);
WId = [[NSNumber alloc] initWithInt:temp1];
char *WNameCharacter;
WNameCharacter = (char *) sqlite3_column_text(walkstatement, 1);
NSString *WNameString = [[NSString alloc] initWithUTF8String:WNameCharacter];
char *SNameCharacter;
SNameCharacter = (char *) sqlite3_column_text(walkstatement, 2);
NSString *SNameString = [[NSString alloc] initWithUTF8String:SNameCharacter];
NSMutableDictionary *tempWalk = [[NSMutableDictionary alloc] init];
[tempWalk setObject:WId forKey:#"WalkId"];
[tempWalk setObject:WNameString forKey:#"WalkName"];
[tempWalk setObject:SNameString forKey:#"SubRegionName"];
[regionWalkArray addObject:tempWalk];
}
}
[RegionWalksDictionary setObject:regionWalkArray forKey:regionId];
// NSLog(#"arr%#",RegionWalksDictionary);
}
}
//*** Methods to get data from database ends here **************
Try this way...
for(id dict in RegionWalksDictionary){
NSLog(#"WalkId: %#",[[[RegionWalksDictionary objectForKey:dict]objectAtIndex:0]objectForKey:#"WalkId"] );
NSLog(#"WalkName: %#",[[[RegionWalksDictionary objectForKey:dict]objectAtIndex:0]objectForKey:#"WalkName"] );
NSLog(#"SubRegionName: %#",[[[RegionWalksDictionary objectForKey:dict]objectAtIndex:0]objectForKey:#"SubRegionName"] );
NSLog(#"----");
}
I have used as following (comment if this is not the structure or your data)
NSMutableDictionary *RegionWalksDictionary=[NSMutableDictionary new];
NSMutableArray *regionWalkArray=[NSMutableArray new];
NSMutableDictionary *tempWalk=[NSMutableDictionary new];
[tempWalk setObject:#"wk1" forKey:#"WalkId"];
[tempWalk setObject:#"wkName1" forKey:#"WalkName"];
[tempWalk setObject:#"srName1" forKey:#"SubRegionName"];
[regionWalkArray addObject:tempWalk];
[RegionWalksDictionary setObject:regionWalkArray forKey:#"1"];
[RegionWalksDictionary setObject:regionWalkArray forKey:#"2"];
[RegionWalksDictionary setObject:regionWalkArray forKey:#"3"];
NSLog(#"tempWalkd dict is %#",RegionWalksDictionary);
try this
NSString * walkName = [[parentDic objectForKey:[NSString stringWithFormat:#"%d",indexPath.row]] objectForKey:#"WalkName"];
NSString * SubregionName = [[parentDic objectForKey:[NSString stringWithFormat:#"%d",indexPath.row]] objectForKey:#"SubRegionName"];
It seems you have an NSDictionary with your 5 keys, and inside that you have an NSArray with a single element that is a NSDictionary. Although I might be wrong, try this:
NSDictionary *dictionaryForKey1 = [[myDictionary objectForKey:#"1"] objectAtIndex:0];
You can then use this:
NSString *walkName = [dictionaryForKey1 objectForKey:#"WalkName"];
AH! now I see it -- there are 5 arrays of length 1
NSArray *array1 = dict[#"1];
NSDictionary *details = array1[0];
//print each var
id WalkName = details[#"WalkName"];
I have 3 strings.
NSString *hour1 = #"10:00";
NSString *hour2 = #"6:00";
NSString *hour3 = #"9:00";
How can I check which string has a bigger value? -> 10:00 > 9:00 > 6:00
Thanks in advance!
Add the strings into an array,
NSString *hour1 = #"10:00";
NSString *hour2 = #"6:00";
NSString *hour3 = #"9:00";
NSArray *hours = [NSArray arrayWithObjects:hour1, hour2, hour3, nil];
Then sort the array,
NSArray *result = [hours sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2){
return ([obj1 compare:obj2 options:NSNumericSearch] == NSOrderedAscending);
}];
NSString *hour1 = #"10:00";
NSString *hour2 = #"6:00";
NSString *hour3 = #"9:00";
// Convert the hours over to int's such as 1000, 600, and 900.
int hour1AsInt = [[hour1 stringByReplacingOccurrencesOfString:#":" withString:#""] intValue];
int hour2AsInt = [[hour2 stringByReplacingOccurrencesOfString:#":" withString:#""] intValue];
int hour3AsInt = [[hour3 stringByReplacingOccurrencesOfString:#":" withString:#""] intValue];
// Make the comparison's... This could be more efficient but works well and is easy to follow:
if (hour1AsInt > hour2AsInt) {
if (hour1AsInt > hour3AsInt) {
NSLog(#"Hour 1 is biggest");
return;
}
}
if (hour2AsInt > hour1AsInt) {
if (hour2AsInt > hour3AsInt) {
NSLog(#"Hour 2 is biggest");
return;
}
}
if (hour3AsInt > hour1AsInt) {
if (hour3AsInt > hour2AsInt) {
NSLog(#"Hour 3 is biggest");
return;
}
}
You should be able to use localizedStandardCompare:
NSLog(#"%ld",[hour1 localizedStandardCompare:hour2]);