iOS comparing NSDictionary values - ios

I'm fetching two JSONs and store them individually as NSArray and NSDictionary.
This is what the JSON data structures looks like
(
{
Cells = (
{
ValueName = "name";
Text = "John Appleseed";
},
{
"Option": {
"Text": "19"
},
ValueName = "age"
}, etc...
)
},
{
Cells = (
{
ValueName = "name";
Text = "John Appleseed";
},
{
ValueName = "age",
"Option": {
"Text": "19"
}
}, etc...
)
}
)
{
"map": {
"first": "name",
"second": "age"
}, etc...
}
The two has values that needs to be cross referenced (as one contains all data (allData), and the other contains a "map" (mapData) of what data is needed).
How may I compare all the values of allData with all the values of mapData while, if possible, keeping simplicity and memory in mind?
A great approach would be to use containsObject matching [allData allValues] with [mapData allValues] and being given back an NSArray with the object of the match. Alas, this is not the case. The approach perfectly illustrates what I wish to accomplish.
My initial approach is slow, memory hogging and ugly (wrote this from memory):
NSArray *allData = [NSJSONSerialization JSONObjectWithData:allDataJSON options:kNilOptions error:&JSONSerializationError];
NSDictionary *mapData = [NSJSONSerialization JSONObjectWithData:mapDataJSON options:kNilOptions error:&JSONSerializationError];
NSMutableDictionary *resultData = [[NSMutableDictionary alloc] init];
for (int count = 0; count<allData.count; count++) {
id dataValue = [allData objectAtIndex:count];
for (id key in mapData) {
if ([[mapData objectForKey:key] containsObject:dataValue]) {
[resultData setObject:[dataValue objectForKey:#"value"] forKey:key]
}
}
}
It's also important to be able to reference to each match of the values that match (as done in my approach).
This is not an opinion based question as optimisation and memory usage is not an opinion.

I found that using this data model is memory efficient and pretty fast (clocked in at an average time of 0.001 sec with allData's length of 1000 lines).
By extracting all wanted values from mapData and placing them in an NSArray the values can easily be matched with the values of allData.
When looping thru the mapDataArray a secondary loop can be created for going thru allData's values. If mapDataArray containsObject returns true, the data can be stored in a momentary storage dictionary.
This allows for easy management of what data is needed and what can be ignored. It is important that the momentary storage is created inside of the first loop (as you'll see in a moment).
If and when all the values from the mapDataArray is found (by checking mapDataArray.count with momentaryData.count) we can break the current loop (thus saving time and memory).
It's probably easier to just look at the code to understand what is really going on.
The data objects as referenced in the question.
NSArray *allData = [NSJSONSerialization JSONObjectWithData:allDataJSON options:kNilOptions error:&JSONSerializationError];
NSDictionary *mapData = [NSJSONSerialization JSONObjectWithData:mapDataJSON options:kNilOptions error:&JSONSerializationError];
‏
Creating the mapDataArray, looping thru it, creating the momentary dictionary, appending found objects and appending the momentary data to resultData.
NSArray *mapDataArray = [[NSArray alloc] init];
mapDataArray = [[mapDataArray arrayByAddingObjectsFromArray:[[mapData objectForKey:#"map"] objectForKey:#"first"]] arrayByAddingObjectsFromArray:[[mapData objectForKey:#"map"] objectForKey:#"second"]];
mapDataArray = [[NSOrderedSet orderedSetWithArray:mapDataArray] array]; //Remove duplicates
NSMutableDictionary *resultData = [[NSMutableDictionary alloc] init];
for (int count = 0; count<allData.count; count++) {
NSMutableDictionary *momentaryData = [[NSMutableDictionary alloc] init];
int dataNumber = count;
NSArray *literal = [[allData objectAtIndex:dataNumber] objectForKey:#"Cells"];
for (int count = 0; count<literal.count; count++) {
id literal = [[[allData objectAtIndex:dataNumber] objectForKey:#"Cells"] objectAtIndex:count];
if ([mapDataArray containsObject:[literal objectForKey:#"ValueName"]]) {
id value = [literal objectForKey:#"Text"];
if (!value) {
value = [literal objectForKey:#"Option"];
if (value && ![value isEqual:[NSNull null]]) {
value = [value objectForKey:#"Text"];
} else {
value = #"";
}
}
[momentaryData setObject:[NSString stringWithFormat:#"%#", value] forKey:[literal objectForKey:#"ValueName"]];
if (mapDataArray.count == momentaryData.count) {
break; //We have what we came for...
}
}
}
[resultData setObject:momentaryData forKey:[NSString stringWithFormat:#"%d", dataNumber]];
}

You can compare arrays or dictionaries using isEqual:.
For arrays it compares whether the arrays have the same number of elements or if the matching elements are equal.
For dictionaries it checks if both have the same number of keys, if set of keys is equal and if each key in one dictionary has the same value as the matching key in the other dictionary.

Related

xcode - set value of a nested object

I have a json from a service and I need to change the values of one obeject.
{
question = (
{
answer = (
{
Id = 1;
value = 1;
},
{
Id = 2;
value = 0;
}
);
},
.....
I use that code to directly access to the second "value" element and set it to "true"
NSMutableDictionary * dict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
NSMutableDictionary *preguntasDict = [[NSMutableDictionary alloc] init];
preguntasDict =[[[dict valueForKey:#"question"]mutableCopy];
NSMutableDictionary *answer = [[NSMutableDictionary alloc] init];
respuestasDict =[[[[preguntasDict valueForKey:#"answer"]objectAtIndex:0]objectAtIndex:1] mutableCopy];
[respuestasDict setObject:[NSNumber numberWithBool:true] forKey:#"value"];
It works: "respuestasDict" changes but the whoole "dict" not.
My question is: how could rebuild the entire dictionary? or it is possible to access directly to the nested object and change it?
Note that perguntasDict and respuestasDict are mutable copies of your dictionaries, so basically you are editing copies of your dict. You need to access dict directly, like this:
NSArray *answers = dict[#"question"][#"answer"];
[answers firstObject][#"value"] = #(YES);
PS: Doing dict[#"question"] is the same thing as [dict objectForKey:#"question"]. Also, #(YES) is the same thing as [NSNumber numberWithBool:true]

ios sort a mutable dictionary order [duplicate]

This question already has answers here:
NSDictionary with ordered keys
(9 answers)
Closed 8 years ago.
I saw many examples on SO but I'm not sure if it applies to this situation. Everywhere it says NSMutableDictionries are not guaranteed an order...but I'm getting my data from the server...so my NSMuteDic looks like this:
{
joe = (
{
fromName = joe;
id = 25;
theMessage = "this is going to be a really big message...";
timeAdded = "2014-04-07 21:08:12";
toName = "me";
},
{
fromName = joe;
id = 10;
theMessage = "why???";
timeAdded = "2014-04-05 20:10:04";
toName = "me";
}
);
bob = (
{
fromName = "me";
id = 24;
theMessage = "blah blah";
timeAdded = "2014-04-06 21:15:06";
toName = bob;
},
{
fromName = bob;
id = 22;
theMessage = message;
timeAdded = "2014-04-06 20:11:57";
toName = "me";
}
);
//more entries here
}
What I want to do is change the order...put bob first and joe second. Is this really impossible to do? I saw many very complex solutions...there's no easy way to do this with just a for loop?
cellForRowAtIndexPath:
NSMutableArray *temp = [myDict objectForKey:keys[indexPath.row]];
cell.Message.text = [[reverse lastObject] valueForKey:#"theMessage"];
cell.dateTime.text = [[reverse lastObject] valueForKey:#"timeAdded"];
return cell;
This is how I'm using it...and when a row is selected I pass the array to the next view controller. The reason why I want to reorder is if a new message will be inserted in the pushed view controller, I want that dictionary to be first in the list so the root view controller can be reordered.
NSArray *keys = [myDict allKeys];
[myDict removeObjectForKey:to];
NSMutableDictionary *temp = [NSMutableDictionary dictionaryWithCapacity:[myDict.count];
temp = myDict;
[myDict removeAllObjects];
[myDict setObject:currentDict forKey:to];
for (int i=0; i<temp.count; i++) {
[myDict setObject:[temp valueForKey:keys[i]] forKey:keys[i]];
}
That's not working because it looks like since myDict is a NSObject, temp gets changed every time myDict changes...from the looks of it the logic should work but it isn't...
NSMutableDictionary is not ordered. There is nothing you can do to guarantee the order of keys because NSDictionary makes no attempt to preserve any particular ordering. To go over a dictionary in a particular order, you have to make an array of the keys that is in the order you want, then iterate that and fetch the corresponding values.
// Get the keys
NSArray *keys = [myDict allKeys];
// Sort the keys
NSArray *sortedArray = [NSArray arrayWithArray:[keys sortedArrayUsingComparator:^(NSString* a, NSString* b) {
return [a compare:b];
}]];
// Iterate the dictionary
for (NSUInteger n = 0 ; < [sortedArray count]; n++) {
id value = [myDict objectForKey:[sortedArray objectAtIndex:n]];
}

how to group array of nsdictionary according to the value inside the element

I have array of dictionary that needs to be grouped according to PO which is part of the element and also get the total of quantityOrdered according to the same PO.
The PO is dynamic it means it can be any value that needs to be filtered and compute the quantityOrderd accordingly.
Please help.
{
PO = PO2;
QuantityReceived = 1;
},
{
PO = PO1;
QuantityReceived = 3;
},
{
PO = PO1;
QuantityReceived = 3;
},
{
PO = PO3;
QuantityReceived = 2;
},
{
PO = PO2;
QuantityReceived = 2;
},
{
PO = PO3;
QuantityReceived = 4;
},
{
PO = PO1;
QuantityReceived = 1;
},
Apology for the confusion or incomplete question but i need to create a new array of dictionary with similar like this :
{
PO = PO1;
TotalQuanityReceived=7;
LineItems=3;
},
{
PO = PO2;
TotalQuanityReceived=3;
LineItems=2;
},
{
PO = PO3;
TotalQuanityReceived=6;
LineItems=2;
},
i updated my example and make it easy to read.
- (NSArray *)whatever:(NSArray *)dictionaries
{
NSMutableArray *results = [[NSMutableArray alloc] init];
NSMutableDictionary *resultsByPO = [[NSMutableDictionary alloc] init];
for (NSDictionary *dictionary in dictionaries) {
id po = [dictionary objectForKey:#"PO"];
NSMutableDictionary *result = [resultsByPO objectForKey:po];
if (result == nil) {
result = [[NSMutableDictionary alloc] init];
[resultsByPO setObject:result forKey:po];
[results addObject:result];
[result setObject:po forKey:#"PO"];
}
double total = [[result objectForKey:#"TotalQuantityReceived"] doubleValue];
total += [[dictionary objectForKey:#"QuantityOrdered"] doubleValue];
int count = 1 + [[result objectForKey:#"Count"] intValue];
[result setObject:#(total) forKey:#"TotalQuantityReceived"];
[result setObject:#(count) forKey:#"Count"];
}
return results;
}
More pain will come with PO values not conforming to NSCopying.
You can do it the clever way with KVC or the stupid easy way. Let's do it the stupid easy way!
Make an empty NSMutableDictionary. Let's call it dict.
Cycle through your array of dictionaries. For each dictionary:
Fetch its PO. Call that value thisPO.
Fetch dict[thisPO]. Was it nil?
a. Yes. Okay, so this particular PO has not yet been encountered. Set dict[thisPO] to this dictionary's quantity received (as an NSNumber).
b. No. Turn that value into an integer, add this dictionary's quantity received, and set the total back into dict[thisPO] (as an NSNumber).
Done! The result is not quite what you asked for; the result looks like this:
{
PO1 = 100;
PO2 = 120;
...
}
But now, you see, the work of totalling is done and it is easy to transform that into an array of dictionaries if that is what you want.
Not 100% sure if this is what you are saying or not, but to sort an array of dictionaries based on one of the elements it would look something like this.
NSDictionary *d1 = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithDouble:100],#"PO",
[NSNumber numberWithDouble:0], #"Category",
nil];
NSDictionary *d2 = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithDouble:50],#"PO",
[NSNumber numberWithDouble:90], #"Category",
nil];
NSArray *unsorted = #[d1, d2];
NSArray *sortedArray;
sortedArray = [unsorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSDictionary *first = (NSDictionary*)a;
NSDictionary *second = (NSDictionary*)b;
NSNumber *firstPO = [first objectForKey:#"PO"];
NSNumber *secondPO = [second objectForKey:#"PO"];
return [firstPO compare:secondPO];
}];
NSLog(#"unsorted = %#", unsorted);
NSLog(#"sorted = %#", sortedArray);
I wasn't really sure what PO was, so I just used an NSNumber as an example. Look at this page for an overview of how you would compare a customer object. http://nshipster.com/nssortdescriptor/.
Then you can loop through the array, now in the correct order and build your next NSDictionary.

Accessing an array with nested objects and displaying keys values

I have this data set here: https://gist.github.com/ryancoughlin/8043604 - If you see tide.tideSummary it contains an array but inside, it contains multiple dictionaries. I am trying access and display tide.tideSummary.date.pretty and display (all 33 of them) in some type of table (but that I have working with dummy data).
I was going through a somewhat similar question here: Keypath for first element in embedded NSArray
Having some trouble finding a solution to access those nested values. Been through plenty of tutorials and posts here on StackOverflow that deal with very basic JSON strings and those work great for me.
UPDATE:
Came across this question: Parsing nested JSON objects with JSON Framework for Objective-C
In my case, I have the following:
NSArray *prettyDate = [[tide objectForKey:#"tideSummary"] valueForKey:#"date"];
prettyDate prints this from NSLog
Pretty date array: (
{
epoch = 1388388109;
hour = 02;
mday = 30;
min = 21;
mon = 12;
pretty = "2:21 AM EST on December 30, 2013";
tzname = "America/New_York";
year = 2013;
},
{
epoch = 1388397506;
hour = 04;
mday = 30;
min = 58;
mon = 12;
pretty = "4:58 AM EST on December 30, 2013";
tzname = "America/New_York";
year = 2013;
},
{
epoch = 1388405656;
hour = 07;
mday = 30;
min = 14;
mon = 12;
pretty = "7:14 AM EST on December 30, 2013";
tzname = "America/New_York";
year = 2013;
}
Then I would be able to loop through each, grab the object and display?
Would I do something like:
Grab parent item tideSummary - array
Grab the item above and store date - dictionary
Access pretty via objectForKey
I have this initWithDict
-(id)initWithDict:(NSDictionary *)json {
self = [super init];
if(self) {
NSDictionary *tide = [json valueForKeyPath:#"tide"];
NSArray *arrOfTideSummaryStats = [json valueForKeyPath:#"tide.tideSummaryStats"];
NSDictionary *dctOfTideSummaryStats = [arrOfTideSummaryStats objectAtIndex:0];
NSArray *arrOfTideSummary = [json valueForKeyPath:#"tide.tideSummary"];
// Loop through the date then...
// Loop and grab 'pretty'
// The "first" is the from the example link in my question above. Experimental oonly
id pretty = [arrOfTideSummary valueForKeyPath: #"#first.tideSummary.date.pretty"];
// This all works below
self.maxheight = [NSString stringWithFormat:#"%#", [dctOfTideSummaryStats valueForKey: #"maxheight"]];
self.minheight = [NSString stringWithFormat:#"%#", [dctOfTideSummaryStats valueForKey: #"minheight"]];
/*============================================================*/
NSArray *arrOfTideInfo = [json valueForKeyPath:#"tide.tideInfo"];
NSDictionary *dctOfTideInfo = [arrOfTideInfo objectAtIndex:0];
self.tideSite = [dctOfTideInfo valueForKey:#"tideSite"];
}
return self;
}
Does anyone have any examples or a direction to steer me in? Would love a resource or question to work off of.
-(void)parseJson:(id)json{
NSDictionary *tide = [json objectForKey:#"tide"];
NSArray *tideSummary = [tide objectForKey:#"tideSummary"];
for (int i = 0; i < tideSummary.count; i++) {
NSDictionary *eachTideSummary = [tideSummary objectAtIndex:i];
NSDictionary *dateDic = [eachTideSummary objectForKey:#"date"];
NSDictionary *utcdateDic = [eachTideSummary objectForKey:#"utcdate"];
NSLog(#"Preety from date dictionary: %#", [dateDic objectForKey:#"pretty"]);
NSLog(#"Preety from utcdate dictionary: %#", [utcdateDic objectForKey:#"pretty"]);
}
}
You can take a look at Initialize NSArray with data from NSdictionary as I have already answered this
Hope this helps.
If I'm understanding what you are trying to do, you should be able to replace this part:
// Loop through the date then...
// Loop and grab 'pretty'
with something like:
for (NSDictionary *tideSummary in arrOfTideSummary) {
NSDictionary *date = [tideSummary valueForKey:#"date"];
NSLog(#"Pretty date: %#", [date valueForKey:#"pretty"]);
}
If you want the date values in a table of some sort, then you could store them into a new array or some other data structure and then create a table view that uses that as its data source.
I have below data on JSON link...
{
"results":[
{
"address_components": [
{
"long_name": "Satadhar",
"short_name": "Satadhar",
"types":[
"point_of_interest",
"establishment"
]
} ]
]
I fetched data through ASIHTTPREQUEST so the code is below given
-(void)requestFinished:(ASIHTTPRequest *)request {
NSError *err;
NSData *data=[request responseData];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err];
arr=[[dict valueForKey:#"results"]valueForKey:#"address_components"]; //to fetch the data till inside Starting point i.e address_component
_arrTable=[[NSMutableArray alloc]init]; //take a array to store the data of the index 0 [index 0 store the whole data that is start from [array] ]
_arrTable =[arr objectAtIndex:0]; //get the data of 0th index
NSLog(#" whole data%#",_arrTable);//print it u will get the idea
NSLog(#" my %#",[[arr objectAtIndex:0] valueForKey:#"long_name"]);
NSLog(#" my %#",[[arr objectAtIndex:0] valueForKey:#"short_name"]);
[self.tblview reloadData];
}

Get a value from nsdictionary

I want to give a key value from my NSDictionary and get the value associated to it.
I have this:
NSArray *plistContent = [NSArray arrayWithContentsOfURL:file];
NSLog(#"array::%#", plistContent);
dict = [plistContent objectAtIndex:indexPath.row];
cell.textLabel.text = [dict objectForKey:#"code"];
with plistContent :
(
{
code = world;
key = hello;
},
{
code = 456;
key = 123;
},
{
code = 1;
key = yes;
}
)
So how do I get "hello" by giving the dictionary "world"?
If I understand your question correctly, you want to locate the dictionary where "code" = "world" in order to get the value for "key".
If you want to keep the data structure as it is, then you will have to perform a sequential search, and one way to do that is simply:
NSString *keyValue = nil;
NSString *searchCode = #"world";
for (NSDictionary *dict in plistContents) {
if ([[dict objectForKey:#"code"] isEqualToString:searchCode]) {
keyValue = [dict objectForKey:#"key"]); // found it!
break;
}
}
However if you do alot of this searching then you are better off re-organizing the data structure so that it's a dictionary of dictionaries, keyed on the "code" value, converting it like this:
NSMutableDictionary *dictOfDicts = [[NSMutableDictionary alloc] init];
for (NSDictionary *dict in plistContents) {
[dictOfDicts setObject:dict
forKey:[dict objectForKey:#"code"]];
}
(note that code will break if one of the dictionaries doesn't contain the "code" entry).
And then look-up is as simple as:
NSDictionary *dict = [dictOfDicts objectForKey:#"world"]);
This will be "dead quick".
- (NSString*) findValueByCode:(NSString*) code inArray:(NSArray*) arr
{
for(NSDictonary* dict in arr)
{
if([[dict valueForKey:#"code"] isEqualToString:code])
{
return [dict valueForKey:#"key"]
}
}
return nil;
}

Resources