How to keep the original order when read the JSON string about iOS development? - ios

When I read the JSON string to be a dictionary, the dictionary order is different from the JSON string original order. Is there anyone knows how to solve it? Maybe someone will tell me the dictionary is disorderly, however, our project needs sort the dictionary by JSON string original order.

A JSON object is define in the JSON spec as an unordered collection.
An object is an unordered set of name/value pairs.
(http://json.org/)
Here are some possible solutions:
Change the JSON object to an array of objects. If you control the server this is by far the best option. If order is important the an array is the right choice.
Send the order along with the JSON. You can have an array of the keys also included in the JSON. Again, you would need to have control of the server to do this.
Sort the keys after you get them. This can only work if the order that you want can be derived from the keys. For example if the keys are alphabetical or are numbers. If you control the keys (even if you don't control the entire server response) you can hack this by change the keys to be sortable in a way that works for you (for example change the keys from ["stores","locations"] to ["0_stores","1_locations"]. This can also work if you know all the possible keys and their order.
Order the keys by the location in the JSON dictionary. If the keys in the JSON are guaranteed to not show up any where else in the JSON (i.e. there is no arbitrary text field in the JSON) then you can get all the dictionary keys sorted by looking where they appear in the JSON string:
NSArray* sortedKeys = [[dictionary allKeys] sortedArrayUsingComparator:^NSComparisonResult(NSString* key1, NSString* key2) {
NSInteger location1 = [jsonString rangeOfString:key1].location;
NSInteger location2 = [jsonString rangeOfString:key2].location;
return [#(location1) compare:#(location2)];
}];
If none of these options work for you then you would have find or create your own JSON parser that respects order. I think it will be hard to find such a library, since it would be explicitly against the JSON spec. I do not recommend this option.

Related

NSDictionary: is order of iteration preserved?

Say I want to access all objects in some NSDictionary object.
I want to iterate like this:
for (key in dict){}
Is it guaranteed that for each run the objects in the dictionary will be accessed in the same order?
PS. Let me explain my question more thoroughly: if I iterate dictionary once and access keys in some concrete order - will I have the same order on second iteration attempt?
Sorting of keys is not guaranteed because of the nature a key is placed inside the NSDictionary. But that is only the half answer. read on...
If we iterate thru the dict with a for (NSString* key in dict) loop or even more unspecific with for (id<NSCopying> key in dict) loop then we use actually NSEnumeration. Those kind of iterations are unpredictable to you. NSEnumeration and NSFastEnumeration do not iterate the dict with indexes. They go thru by addresses or hashes of keys, so to speak. Thats also a blurry answer that is not complete.
Keys inside a dict are unique compared to other keys in the same dict.
Which is the great thing and why you would use a NSDictionary or NSMutableDictionary instead of indexed NSArray or NSPointerArray or unspecific NSSet, and specially offered datatypes as NSOrderedSet, NSOrderedCollection. NSMapTable and NSHashTable tend to behave like NSDictionaries but they have a completely different way how they store the keys and how they iterate.
So what happens when you rewrite a keyed value in NSDictionary?
NSDictionary *dict = #{
#"A" : #(1234),
#"B" : #(4321)
}
NSMutableDictionary *mutabledict = [dict mutablecopy];
mutabledict[#"A"] = #(5678); //beware this only works on NSMutableDictionary.
dict = mutabledict;
here we exchange the value of the first declared key A.
mutabledict[#"A"] [key] is a getter subscript used to find the address of the keys value pair. Nothing is changed on the key itself. So the order of keys stays as it was the moment the unique key was copied in.
You need to be careful when you create a mutablecopy, because then whole pairs are copied. The outcome in sorting may be unpredictable as well.
Simple: But Accessing keys value does not change their memory layout.
But: NSDictionary are immutable, so the keys can not be changed once they are set. So you can say:
The keys are ordered in the order they where stored the first time. But you can't access the values stored in a guaranteed order when you don't know the order the keys:value pairs where placed and with this process its unique keys copied into the dict.
If you can't control the order the keys are set then the keys order is unknown to you, (respecting the question) not undefined. And NSEnumeration iteration gives you not a real picture of the order they are stored.
How to deal with that?
The easies way to get known ordered sorting of keys is by manually sorting all its keys like..
NSArray *sortedKeys = [dict.allKeys sortedArrayUsingSelector:#selector(compare:)];
which is giving you ascending order of keys and lets you iterate thru your dict with arrays indexes containing addresses to keys. like ..
for (unsigned int i=0; i<sortedKeys.count; i++) {
NSString* key = sortedKeys[i];
dict[ key ] = yourValue;
}
if keys are not of interest but the guaranteed sorting is more important to you, then you could convert the NSDictionary into a sorted NSArray without keys and access its indexes in a loop. With the obvious back-draw to have no keys unless you store them manually.
id notfoundmarker = #"empty";
NSArray *sortedkeys= [dict.allKeys sortedArrayUsingSelector:#selector(compare:)];
NSArray *oneForOneSorted = [dict objectsForKeys:sortedkeys notFoundMarker:notfoundmarker];
for (unsigned int i=0; i<oneForOneSorted.count; i++) {
id<NSObject> value = oneForOneSorted[i];
NSLog(#"%#", value);
};
My question was different: if I iterate dictionary once and access keys in some concrete order - will I have the same order on second iteration attempt?
Short Answer
In all probability yes
Longer Answer
How a dictionary is constructed internally is not specified, and there are multiple ways to represent dictionaries, all that is known is that key values must be hashable which implies hashing is used somehow.
It is also not know what algorithm a dictionary uses to provide the keys when enumerating them, and again for an particular possible representation there could be more than one enumeration algorithm possible.
So we have a lot of unknowns.
What do we know?
In the absence of threading, random number generation, and anything similar an Objective-C program is deterministic, a trait it has in common with C, Swift, Java, C# and a host of other languages.
NSDictionary is not thread-safe so its unlikely to use threading in its implementation.
And why would it use random numbers?
NSDictionary is also an immutable type so once constructed there is no obvious reason to re-order its internal storage in response to calls querying its contents.
So, in all probability, you will get the same key order on every enumeration.
Without the source though you can't be certain, you cannot prove the absence of something by black-box testing. Maybe the programmer decided that every millionth enumeration they'd throw a little randomness in – just for fun ;-) Is this likely? Maybe not (its probably a good way to get fired!), but it's not impossible.
If you ask out of curiosity, then good stay curious!
If however you want to rely on the order being deterministic for code correctness then sort the keys (into a peculiar order if you wish as long as its deterministic), the cost of doing so will in all probability be inconsequential.
HTH
The docs state that for the allKeys property the order of the elements in the array is not defined.
So you can just sort the keys array to ensure it's always sorted.
Each iteration will be in a random order because Swift dictionaries don't prioritize order. Arrays do, however, so you can sort the dictionary (by key or value) to produce an array of tuples, which in effect can be treated like a sorted dictionary.
let dictionary = ["k4": 3, "k2": 8, "k1": 6]
let sortedArrayOfTuples = dictionary.sorted(by: { $0.key < $1.key })
for entry in dictionary {
print(entry.key)
}
for entry in sortedArrayOfTuples {
print(entry.key)
}

iOS Dictionary Response by added objects [duplicate]

I've run into the same problem as found in this question. However, I have a follow-up question. I seem to be in the same situation as the original asker: I have a plist with a hierarchy of dictionaries that define a configuration screen. These are not mutable and will stay the same throughout the application. Since the original discussion seems to focus on problems arising from mutating the dictionary, I must ask for comfirmation: is the order of a dictionary guaranteed the same as they are in the plist, i.e. as it is read (with initWithContentsOfFile)? Can I use allKeys on it in this case to get a correct-order array of keys if the dictionary never changes?
No, the keys are not likely to appear in the same order as your plist file. I can't look under the hood, but I would guess that the keys are ordered in whatever way that provides an efficient look-up. The documentation for allKeys says that the order of the elements in the array is not defined.
If you need an NSDictionary that maintains order, take a look at CHDataStructure's CHOrderedDictionary, which does exactly that. I use it all the time.
I would not assume that the keys will be in any kind of order because allKeys does not guarantee order.
Internally, NSDictionary uses a hash table.
From allKeys description:
The order of the elements in the array
is not defined.
If you want to display values in some order then get the keys, sort the keys array and then get the value for each key.
Nothing at all is guaranteed about order. It’s not even guaranteed that iterating over a dictionary twice will give you the keys in the same order.

Get index for value NSDictionary

I've got a value like so: #"2329300" and I've got a NSDictionary like so :{#"John Appleseed":[#"2329300",#"2342322",#"32i249"]}
How do I find the index of the key/value pair in the NSDictionary when I've only got a string value of the entire list that's known as the value. I'm assuming there's no duplicates in the dict.
I know that there's indexForObject on a NSArray but is there a similar thing for a dict?
I imagine it would look something like this:
[NSDictionary indexForValue:value]; // returns index number.
And even then the NSString doesn't match the value, so I'd need a workaround for that too.
You have a basic misunderstanding. Dictionaries are unordered collections. They do not have any particular order for their key/value pairs. You can't have indexes to the key/value pairs because that implies a fixed order.
Think of a dictionary as a bunch of kids milling around on a playground. You can call out a kid's name "Johnny, come here!" and fetch that kid (use a key to find an object) but what does order mean for kids that won't sit still?
You can create an array of the keys from a dictionary and sort that into a particular order (an alphabetical list of the kids on the playground) if that's what you want, or you can create an array of dictionaries, or an array of a custom data object that contains any arbitrary properties that you want.
EDIT:
For a table view, an array of dictionaries is probably a good choice. Each entry in the array contains a dictionary with all the settings for a cell in the dictionary. If you have a sectioned table view then you want an outer array for sections, containing inner arrays for the rows, and each entry in the inner array containing a dictionary.
I tend to prefer custom data objects to dictionaries though. (An object that just has properties for each setting I want.) That way the list of values and their types is crystal-clear and fairly self-documenting.

Loading NSDictionary from Plist as Listed

I'm using plist to store key-value lists. When the application needs that list, trying load list to a NSDictionary. Everything is well until here.
Here how I load the plist file:
NSString *myPlistFilePath = [[NSBundle mainBundle] pathForResource: #"cities" ofType: #"plist"];
cities = [NSDictionary dictionaryWithContentsOfFile: myPlistFilePath];
When we look at, cities is a NSDictionary. Then I pushed all list key values to a TableView, somehow its not listed as in plist file. Sorting random.
Is there way to figure out?
Thanks in advance.
An NSDictionary is not an ordered collection, that is, it does not guarantee to preserve order of the contents in any way. Its only function is to map from keys to values.
If you need the contents ordered, you can extract it from the NSDictionary using for example keysSortedByValueUsingSelector, which will extract the data in the collection, sort it by your criteria and store it in an (order preserving) NSArray.
Alternatively, consider using an Array in the root of the plist, containing an ordered list of city dictionaries. Then if you iterate over the array contained therein, they will be in the array order contained in the plist.
NSDictionary is not an ordered data structure.
Objects are listed based on allKeys functions and keys are listed in undefined way.
See the apple doc for allKeys function.
allKeys Returns a new array containing the dictionary’s keys.
(NSArray *)allKeys
Return Value A new array containing the dictionary’s keys, or an empty
array if the dictionary has no entries.
Discussion The order of the elements in the array is not defined.
If you wish to avoid using a selector to sort the keys, consider using an Array in the root of the plist. Then when you iterate over the array, the order is preserved. This runs o(n), which is shorter than the fastest possible sort algorithm by a factor of log(n)
eg:
cities{
list[
city{}
city{}
city{}
...
]
}

Nsdictionary as a data type

Would nsdictionary be a good data type to use for storing long string values as values and names of those descriptions for keys? Or would a different data type be more effective? I am using it for animals, and having and array hold all the data then using a dictionary to point to the name and description of the animal. I'm just curious if this is used for smaller data like states and capitals
Or should I just use a #Define #"rhino description"
[Animal animalObj:#"rhino" location:#"the water" description:[[self setGenericAnimals] valueForKey:#"Rhino"]]
NSDictionary is OK for this. Whats great about using NSDictionary is you can save your data as JSON in a sperate file and then serialzie it into a NSDictonary when you need it. This would make it easier for you to manage all your data and it seperates it from your application.
this is a good start on how to convert JSON into a NSdictionary.
http://www.raywenderlich.com/5492/working-with-json-in-ios-5
Remember though that the entire NSDictionary must fit into memory so if your going to have thousands of strings you might want to separate that into different JSON files and then serialize them into Dictionaries when you need them.
Another thing to remember is that if you want to do simple comparisons and sorting options on objects you are better using CoreData as this allows you to store lots of strings and easily access them.

Resources