In my application I take input from user and store it in NSMutableDictionary and Fetch result successfully.
But I am little confused about the output print on console. It's not in an order.
What is reason behind this output?
Thanks
I use the following code to store input from textField and print it on Console.
-(IBAction)doneButtonClicked:(id)sender
{
NSArray *arr=[NSArray arrayWithObjects:#"firstName",#"middleName",#"lastName",#"address",#"email",#"phone",nil];
DataClass *obj=[DataClass getInstance];
obj.personelInfo=[NSMutableDictionary dictionary];
for (int a=1; a<=personelInfoCounter; a++) {
UITextField *textField = (UITextField*)[self.view viewWithTag:a];
NSString *fieldValue = textField.text;
if(fieldValue != nil)
{
[obj.personelInfo setObject:fieldValue forKey:[arr objectAtIndex:a-1]];
}
else
{
[obj.personelInfo setObject:#"" forKey:[arr objectAtIndex:a-1]];
}
}
NSLog(#"Final value of Personal Info Dictionary is ----------------------------------:");
for (id key in obj.personelInfo) {
NSLog(#"%# , %#", key, [obj.personelInfo objectForKey:key]);
}
}
And the output on console is
it is not in order. What is reason behind this output?
NSDictionary and NSMutableDictionary don't maintain the order of the things you put in them. It's undefined what order you'll get when you enumerate a dictionary.
As wattson said "the dictionary in not ordered". you can make your own logics depending on your key values.
for more info https://developer.apple.com/library/mac/#documentation/cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html
Related
I have a code like that
if ([dataArray valueForKey:#"success"]) {
[self.feedsArray addObjectsFromArray:dataArray];
NSLog(#"self.feedsArray: %#",self.feedsArray);
} else {
NSLog(#"no feed found ");
}
dataArray is a NSMutableArray which ultimately contains a JSON Dictionary.
but I am getting the same console output independent of success either TRUE or FALSE, but my console output is always same.my console output is:
for FALSE or NO:
self.feedsArray: (
{
action = register;
message = "Invalid parameters";
success = 0;
}
)
and for TRUE or YES:
self.feedsArray: (
{
action = register;
message = "valid parameters";
success = 1;
}
)
in both cases if part is executed.
in NSUserDefaults there is a method boolForKey but how to do this in case of NSMutableArray.
You need to read the fine print for [NSArray valueForKey:], specifically:
Returns an array containing the results of invoking valueForKey: using
key on each of the array's objects.
and:
The returned array contains NSNull elements for each object that
returns nil.
So if the array contains, say, 3 objects and none of them have a success key then you will get an array of 3 NSNull objects returned.
Therefore the if statement will fire whenever dataArray is non-empty, which is obviously not what you intended.
You should check the contents of the returned array:
BOOL succeeded = NO;
NSArray *results = [dataArray valueForKey:#"success"];
for (NSObject *obj in results) {
succeeded = [obj isKindOfClass:[NSNumber class]] && [(NSNumber *)obj boolValue];
if (succeeded)
break;
}
if (succeeded) {
[self.feedsArray addObjectsFromArray:dataArray];
NSLog(#"self.feedsArray: %#",self.feedsArray);
} else {
NSLog(#"no feed found ");
}
You can do this in simple way:
What i see in your response json value is, you have dictionary in dataArray at index 0
NSMutableDictionary *responseDict = [dataArray objectAtIndex:0];
if([[responseDict objectForKey:#"success"] boolValue])
{
NSLog(#"Success: 1");
}
{
NSLog(#"Success: 0");
}
Use index instead of key for an array.
NSDictionary dictionary = (NSDictionary *)dataArray[0];
if ([(NSNumber *)[dictionary objectForKey:#"success"] boolValue]) {
// ...
}
otherwise use if([[[dataArray objectAtIndex:0] valueForKey:#"success"] isEqualToString:#"1"])
An array does not store keys, the only way to access items in an array is by index.
You should be using an NSDictionary/NSMutableDictionary instead. If you want to use a bool store it as a NSNumber, [NSNumber numberWithBool:YES] and then use the instance method valueForBool to read it back.
Try this
if ([[dataArray valueForKey:#"success"]isEqualToString:#"1"]) {
[self.feedsArray addObjectsFromArray:dataArray];
NSLog(#"self.feedsArray: %#",self.feedsArray);
}
else {
NSLog(#"no feed found ");
}
It 'll work out.
use this if you want bool value
if([[dataArray valueForKey:#"success"] boolValue])
{
//i.e success is true
}
if response contains array of dictionaries then we can use loop and check condition,
here i is index variable of array,
if([[[dataArray objectAtIndex:i] objectForKey:#"success"] boolValue])
{
// success is true ,
}
Replace you code line
if ([dataArray valueForKey:#"success"]) {
}
with
if ([[dataArray valueForKey:#"success"] integerValue]) {
}
Hope it will work for you.
its working with replacing the line with
if ([[[dataArray objectAtIndex:0] valueForKey:#"success"] boolValue])
What I'm trying to do is search an array of dictionaries for a specific target dictionary, and if found, replace the actual dictionary in the original array with the target dictionary. The search algorithim works, but the copying of dictionaries doesn't. The main line in question is the one that says:
tempDict=targetDict;
I was hoping that tempDict would be a pointer to the original dictionary from the original array, but when trying to log the author name, I get "moe" instead of "steve".
-(void)viewDidAppear
{
[actualDictionary setObject:#"test" forKey:#"mainNote"];
[actualDictionary setObject:#"moe" forKey:#"authorName"];
[targetDictionary setObject:#"test" forKey:#"mainNote"];
[targetDictionary setObject:#"steve" forKey:#"authorName"];
[arrayOfNotes addObject:actualDictionary];
[self beginSearchWithMainArray];
}
-(void)beginSearchWithMainArray;
{
[self searchArray:arrayOfNotes forDict:targetDictionary];
}
-(void)searchArray:(NSMutableArray*)array forDict:(NSMutableDictionary*)targetDict
{
NSString *targetText=[targetDict objectForKey:#"mainNote"];
for(int i=0;i<[array count];i++)
{
NSMutableDictionary *tempDict=[array objectAtIndex:i];
NSString *possibleText=[tempDict objectForKey:#"mainNote"];
if([possibleText isEqualToString:targetText])
{
//found match, replace tempDict with targetDict
tempDict=targetDict;
NSLog(#"found match");
NSString *authorName=[[arrayOfNotes objectAtIndex:0] objectForKey:#"authorName"];
NSLog(#"%#", authorName); //should be steve
return;
}
//no match, search sub notes
...
}
}
replace
tempDict=targetDict;
with
[array replaceObjectAtIndex:i withObject:targetDict];
or
[tempDict setDictionary:targetDict]
tempDict is a pointer to a NSMutableDictionary, but assign it to another instance doesn't means change the content of the previous instance
you have to modify what "pointer point to" not the "pointer", thats why you can use setDictionary: to do the "assignment"
tempDict is just a reference to the matched element of the array. Changing its value will not modify the array. Replace
tempDict=targetDict;
to
[array replaceObjectAtIndex:i withObject:targetDict];
I am new to Objective-C. I have written a function that returns two values. Now I would like to print it into two separate labels, how I can do it?
-(NSString *)abc:(NSInteger)weeks year:(NSInteger)year{
............
return ca , da ;
}
and when I call this function like
resultLabel1.text = [self abc year:year]; //output show the value of da
now I want to show the value of ca into resultLabel1.text and da into resultLabel2.text
is it possible?
You can only return a single value from any method in C and C-derived languages. So you simply need to return a single value that represents both values. You can achieve this by making use of a NSDictionary.
So make it:
-(NSDictionary *)abc:(NSInteger)weeks year:(NSInteger)year{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
ca, #"object1",
da, #"object2", nil];
return dict;
}
Another way is to return an NSArray:
- (NSArray *)abc:(NSInteger)weeks year:(NSInteger)year {
NSArray *myArray = [NSArray arrayWithObjects:da, ca, nil];
return myArray;
}
And you can then use these values as:
NSArray *myArray = [self abc:2 year:2004];
textLabel.text = (NSString *)[myArray objectAtIndex:0];
textLabel2.text = (NSString *)[myArray objectAtIndex:1];
As Jules points out a method can "return" only a single value. However, you have several options for returning multiple values:
Return a pointer to an object, where the object contains multiple values. The object can be an NSArray, NSDictionary or you own class. Jules answer gave some examples of this.
Pass multiple pointers in your parameters, and the method can store results in the object or variable pointed to. See example here.
Return a struct that has multiple fields. There's an example here.
I'd use a NSDictionary to return multiple values from a method. In the dictionary each value is named and referenced by a key. The keys in this example are "ca" and "da" and the values are both a short string of text.
-(NSDictionary *) abc: (NSInteger) weeks year:(NSInteger) year {
NSString* ca = [NSString stringWithFormat:#"Week is %d", weeks];
NSString* da = [NSString stringWithFormat:#"Year is %d", year];
return [[NSDictionary alloc] initWithObjectsAndKeys:ca, #"ca", da, #"da", nil];
}
Call the method and pick out the returned values with code like this:
NSInteger weekParam = #"52".integerValue;
NSInteger yearParam = #"2011".integerValue;
NSDictionary *result = [self abc:weekParam year:yearParam];
NSLog(#"ca has value: %#", [result objectForKey:#"ca"]);
NSLog(#"da has value: %#", [result objectForKey:#"da"]);
You log should have the following lines added:
ca has value: Week is 52
da has value: Year is 2011
You can "return" multiple objects as parameters in a block:
- (void)method {
[self returnMultipleObjectsWithCompletion:^(NSString *firstString, NSString *secondString) {
NSLog(#"%# %#", firstString, secondString);
}];
}
- (void)returnMultipleObjectsWithCompletion:(void (^)(NSString *firstString, NSString *secondString))completion {
if (completion) {
completion(#"firstReturnString", #"secondReturnString");
}
}
You will have to return an NSArray or NSDictionary with the two values.
I am having a NSMutableDictionary. I have to dynamically rename any Key in the dictionary to a new value, in my code.. I can't find any built-in API to do this..
How can I do this? Is there any built-in API available to do this?
Thanks everyone..
// assumes that olkdey and newkey won't be the same; they can't as
// constants... but...
[dict setObject: [dict objectForKey: #"oldkey"] forKey: #"newkey"];
[dict removeObjectForKey: #"oldkey"];
Think about what "directly editing an existing key" means. A dictionary is a hash; it hashes the contents of the keys to find a value.
What happens if you were to change the contents of a key? The key would need to be rehashed (and the dictionary's internal structures re-balanced) or the value would no longer be retrievable.
Why do you want to edit the contents of a key in the first place? I.e. what problem does that solve that the above does not?
This should work:
- (void) renameKey:(id<NSCopying>)oldKey toKey:(id<NSCopying>)newKey{
NSObject *object = [dictionary objectForKey:oldKey];
[object retain];
[dictionary removeObjectForKey:oldKey];
[dictionary setObject:object forKey:newKey];
[object release];
}
This does exactly the same as bbum's answer but, if you remove the old key first (like in this example) then you have to retain the object temporarily otherwise it might get deallocated in the way ;)
Conclusion: Unless you need explicitly to remove the old key first do as bbum.
#interface NSMutableDictionary (KAKeyRenaming)
- (void)ka_replaceKey:(id)oldKey withKey:(id)newKey;
#end
#implementation NSMutableDictionary (KAKeyRenaming)
- (void)ka_replaceKey:(id)oldKey withKey:(id)newKey
{
id value = [self objectForKey:oldKey];
if (value) {
[self setObject:value forKey:newKey];
[self removeObjectForKey:oldKey];
}
}
#end
This also handles the case where the dictionary doesn't have a value for the key nicely.
I have to navigate a complete JSON response object that holds fields, sub-dictionaries and sub-arrays. All because one of the JSON fields is called "return" which is an iOS reserved word, so can't be used with the JSONModel Cocoa Pod.
Here's the code:
+ (id) sanitizeJSON:(id) dictIn {
if (dictIn) //check not null
{
// if it's a dictionary item
if ([dictIn isKindOfClass:[NSDictionary class]])
{
NSMutableDictionary *dictOut = [dictIn mutableCopy];
// Do the fix replace "return" with "not_return"
if ([dictOut objectForKey: #"return"])
{[dictOut setObject: [dictIn objectForKey: #"return"] forKey: #"not_return"];
[dictOut removeObjectForKey: #"return"];}
// Continue the recursive walk through
NSArray*keys=[dictOut allKeys]; //get all the keys
for (int n=0;n<keys.count;n++)
{
NSString *key = [keys objectAtIndex:n];
//NSLog(#"key=%# value=%#", key, [dictOut objectForKey:key]);
if (([[dictOut objectForKey:key] isKindOfClass:[NSDictionary class]]) || ([[dictOut objectForKey:key] isKindOfClass:[NSArray class]]))
{
// recursive call
id sanitizedObject = [self sanitizeJSON:[dictOut objectForKey:key]];
[dictOut removeObjectForKey: key];
[dictOut setObject:sanitizedObject forKey:key];
// replace returned (poss modified) item with this one
}
}
return dictOut; //return dict
}
else if ([dictIn isKindOfClass:[NSArray class]]) //Or if it's an array item
{
NSMutableArray *tempArray = [dictIn mutableCopy];
// Do the recursive walk across the array
for (int n=0;n< tempArray.count; n++)
{
// if array item is dictionary
if (([[tempArray objectAtIndex:n] isKindOfClass:[NSDictionary class]]) || ([[tempArray objectAtIndex:n] isKindOfClass:[NSArray class]]))
{
// recursive call
id sanitizedObject = [self sanitizeJSON:[tempArray objectAtIndex:n]];
// replace with the possibly modified item
[tempArray replaceObjectAtIndex:n withObject:sanitizedObject];
}
}
return tempArray; //return array
}
return dictIn; //Not nil or dict or array
}
else
return dictIn; //return nil
}
A little bit of background:
I'm appending an image to an already existing pdf file. I found out that there isn't a way to just add a page to it, you must recreate the whole pdf, then remove the old one then move the new one. (If that is wrong please tell me, and it will save me a lot of headaches.) I've accomplished that part, but I'm now trying to copy of the Auxiliary Info (Title, Author, Keys, etc). Problem is there are so many type problems that the current method I'm using is to get the CGPDFDocumentRef's CGPDFDictionaryRef, then call CGPDFDictionaryApplierFunction. In the C function I pass it, I am extracting each key to an NSMutableDictionary so that I can then do something with the values and not have them locked in this terrible CGPDF format.
So basically my question would be:
Is there a better way of doing this? I've stared at so many documentation files I couldn't imagine missing anything, but really hope I have because the amount of workaround I'm having to do is getting absurd.
I've completed my workaround. I would still like to know if there is any better way to do this, so if you know of one or have any suggestions I'm willing to try them out.
Notes: I only check for bools, ints, strings, and arrays because according to Apple's documentation thats all that should be in the Auxiliary Information in a PDF. The array bit is untested, because I dont have a PDF with an array in the Aux Info. If someone would like to test that for me or link to a pdf with one in it I will gladly test it out.
First, in the class header create an id selfClass outside the #interface tags so that the C functions called by CGPDFDictionaryApplyFunction can access the current class. Also add an NSDictionary *auxInfo for storing the information. Once extracted, the NS type can be easily cast like this:
CFDictionaryRef newDictionary = (CFDictionaryRef)[self auxInfo];
I was actually done last night but thought I had to do another round of looping to convert from NS to CF, forgetting they were token-free bridged. So there you have it, hope everyone benefits from my labors. Ask questions if you need clarification. And once again, if there is an easier way to do this, if only an optimization of my code, please say so. I know this isn't a very elegant way to do this but it works for now.
- (void)extractPDFDictionary:(CGPDFDocumentRef)pdf{
NSLog(#"extractingPDFDictionary");
CGPDFDictionaryRef oldDict = CGPDFDocumentGetInfo(pdf);
CGPDFDictionaryApplyFunction(oldDict, copyDictionaryValues, NULL);
}
void copyDictionaryValues (const char *key, CGPDFObjectRef object, void *info) {
NSLog(#"key: %s", key);
CGPDFObjectType type = CGPDFObjectGetType(object);
switch (type) {
case kCGPDFObjectTypeString: {
CGPDFStringRef objectString;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeString, &objectString)) {
NSString *tempStr = (NSString *)CGPDFStringCopyTextString(objectString);
[[selfClass auxInfo] setObject:tempStr
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
[tempStr release];
NSLog(#"set string value");
}
}
case kCGPDFObjectTypeInteger: {
CGPDFInteger objectInteger;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeInteger, &objectInteger)) {
[[selfClass auxInfo] setObject:[NSNumber numberWithInt:objectInteger]
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
NSLog(#"set int value");
}
}
case kCGPDFObjectTypeBoolean: {
CGPDFBoolean objectBool;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeBoolean, &objectBool)) {
[[selfClass auxInfo] setObject:[NSNumber numberWithBool:objectBool]
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
NSLog(#"set boolean value");
}
}
case kCGPDFObjectTypeArray : {
CGPDFArrayRef objectArray;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeArray, &objectArray)) {
NSArray *tempArr = [selfClass copyPDFArray:objectArray];
[[selfClass auxInfo] setObject:tempArr
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
[tempArr release];
NSLog(#"set array value");
}
}
}
}
- (NSArray *)copyPDFArray:(CGPDFArrayRef)arr{
int i = 0;
NSMutableArray *temp = [[NSMutableArray alloc] init];
for(i=0; i<CGPDFArrayGetCount(arr); i++){
CGPDFObjectRef object;
CGPDFArrayGetObject(arr, i, &object);
CGPDFObjectType type = CGPDFObjectGetType(object);
switch(type){
case kCGPDFObjectTypeString: {
CGPDFStringRef objectString;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeString, &objectString)) {
NSString *tempStr = (NSString *)CGPDFStringCopyTextString(objectString);
[temp addObject:tempStr];
[tempStr release];
}
}
case kCGPDFObjectTypeInteger: {
CGPDFInteger objectInteger;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeInteger, &objectInteger)) {
[temp addObject:[NSNumber numberWithInt:objectInteger]];
}
}
case kCGPDFObjectTypeBoolean: {
CGPDFBoolean objectBool;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeBoolean, &objectBool)) {
[temp addObject:[NSNumber numberWithBool:objectBool]];
}
}
case kCGPDFObjectTypeArray : {
CGPDFArrayRef objectArray;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeArray, &objectArray)) {
NSArray *tempArr = [selfClass copyPDFArray:objectArray];
[temp addObject:tempArr];
[tempArr release];
}
}
}
}
return temp;
}
You might accomplish what you are describing by drawing into CGPDFContext, but if you have a complex PDF, you may be attempting something beyond what the API was intended for. You might take a look at section 3.4 of the PDF spec and see what you're getting into.