Dictionary Key Sorting [duplicate] - ios

This question already has answers here:
NSDictionary with ordered keys
(9 answers)
Closed 9 years ago.
Hi i have an dictionary which is been set by me and am accessing it
the code is as follows.
NSMutableDictionary* filteredDictionary = [NSMutableDictionary dictionary];
[filteredDictionary setObject:#"xcode" forKey:#"1"];
[filteredDictionary setObject:#"ios" forKey:#"3"];
[filteredDictionary setObject:#"ipad" forKey:#"2"];
[filteredDictionary setObject:#"iphone" forKey:#"5"];
[filteredDictionary setObject:#"simulator" forKey:#"4"];
NSLog(#"%#",filteredDictionary);
current output:
{
1 = xcode;
2 = ipad;
3 = ios;
4 = simulator;
5 = iphone;
}
but i want
{
1 = xcode;
3 = ios;
2 = ipad;
5 = iphone;
4 = simulator;
}
i want the dictionary as i set the object in it
i dont want to make the dictionary to sort according to it
Pls Help
Thanks in advance.....

You can't sort the keys in-place, as dictionaries are hash-tables.
You can get the key/value pairs as an array, and sort the array before showing it though:
See https://stackoverflow.com/a/4558777/15721

The NSDictionary doesn't remember the order in which you add the keys. You'll have to do it yourself. I would suggest using an NSMutableOrderedSet.
NSMutableDictionary* filteredDictionary = [NSMutableDictionary dictionary];
NSMutableOrderedSet* keyOrder = [[NSMutableOrderedSet alloc] init];
[filteredDictionary setObject:#"xcode" forKey:#"1"];
[keyOrder addObject: #"1"];
[filteredDictionary setObject:#"ios" forKey:#"3"];
[keyOrder addObject: #"3"];
// etc
Obviously this is a pain in the neck, so create yourself a new collection class
#implementation MyMutableOrderedDictionary
{
NSMutableDictionary* filteredDictionary = [NSMutableDictionary dictionary];
NSMutableOrderedSet* keyOrder = [[NSMutableOrderedSet alloc] init];
}
-(void) setObject: (id) object forKey: (id <NSCopying>) key
{
[filteredDictionary setObject: object forKey: key];
[keyOrder addObject: key];
}
-(NSOrderedSet*) keys
{
return keyOrder;
}
// Some other methods
#end
You can implement some enumeration methods by iterating over keyOrder internally or over the keys property externally.
Note I'm not subclassing NSMutableDictionary, because that can be a pain in the arse as it is a class cluster.

NSDictonary doesn't guarantee order when you access it. You have to use NSArray if you want to keep order of elements.
There few ideas like OrderedDictionary (http://www.cocoawithlove.com/2008/12/ordereddictionary-subclassing-cocoa.html) but it's better to just use right objects for right purposes.

NSDictonary is not mean to sort the objects and it is absolutely unimportant to consider the order of the elements.
NSdictonary is a set of key-value pair where it is expected to access the value using the respective key and so the concept of index is not useful in it.
Still if you want to play on indexes you should use NSArray.

Related

how to array value convert in dictionary objective c

How to convert an array into a dictionary in Objective-C language?
This is the array:
[{"1":"2"}, {"2":"3"}]
But I want :
{"1":"2", "2":"3"}
Please help me, I am a new iPhone developer.
I am assuming that you want to combine an array of dictionaries into a single dictionary.
NSArray *array = #[#{#"1":#"2"},#{#"2":#"3"}];
NSMutableDictionary *result = [[NSMutableDictionary alloc]init];
for (NSDictionary *dict in array)
{
[result addEntriesFromDictionary:dict];
}
/*
result = {
1 = 2;
2 = 3;
}
*/
Where did your data come from? The easiest way to "convert" the above is to simply create a single dictionary in the first place.
Otherwise:
NSMutableDictionary* newDict = [NSMutableDictionary dictionary];
for (NSDictionary* oldDict in sourceArray) {
[newDict addEntriesFromDictionary:oldDict];
}

Use NSDictionary as other NSDictionary's key

i'm trying to achieve the following structure:
NSMutableDictionary *dict = [#{} mutableCopy];
NSDictionary *key1 = #{#"id_format": #(1), #"date": #"2014-08-01"};
NSDictionary *key2 = #{#"id_format": #(2), #"date": #"2014-08-02"};
// This runs perfect and can be checked in llvm debugger
// data1 & data2 are NSArray that contain several NSDictionary
[dict setObject:data1 forKey:key1];
[dict setObject:data2 forKey:key2];
// Later, if i try to access dict using another key, returns empty NSArray
NSDictionary *testKey = #{#"id_format": #(1), #"date": #"2014-08-01"}; // Note it's equal to "key1"
for(NSDictionary *dictData in dict[testKey]){
// dictData is empty NSArray
}
// OR
for(NSDictionary *dictData in [dict objectForKey:testKey]){
// dictData is empty NSArray
}
So the question is if is there possible to use NSDictionary as key, or not.
An object can be used as a key if it conforms to NSCopying, and should implement hash and isEqual: to compare by value rather than by identity.
Dictionaries follow the array convention of returning [self count] for hash. So it's a pretty bad hash but it's technically valid. It means your outer dictionary will end up doing what is effectively a linear search but it'll work.
Dictionaries implement and correctly respond to isEqual:. They also implement NSCopying.
Therefore you can use a dictionary as a dictionary key.

Multiple dictionaries within an array and Checking for duplicate keys - Objective C

I have an array which contains multiple Dictionaries each one with 3 keys (#"date", #"username", #"text").
What I want to check for, is whether the same user (#"username") exists in more than one dictionary in that Array. And, if she does, combine the text for those "duplicates" into one dictionary.
I have considered this answer to check for duplicates and this one
but I cannot figure out how to combine these two.
Jumping in here because although I think you should work on the code yourself first, I think Miro's answer is more complicated than the issue requires and though I like the idea of using predicates in Greg's answer, here's a 3rd solution that (1) wouldn't require you to change your data structure and (2) references the necessary loops...
The way I'd do it: Create an NSMutableArray then start adding the usernames in order. If the NSMutableArray already contains the username though, don't add another instance of the username, but instead merge the dictionary info.
ex.
// Note: I'm calling your array of user dictionaries userArray.
// Create a username array to store the usernames and check for duplicates
NSMutableArray *usernames = [[NSMutableArray alloc] init];
// Create a new userArray to store the updated dictionary info, merged
// entries et. al.
NSMutableArray *newUserArray = [[NSMutableArray alloc] init];
// Go through the array of user dictionaries
for (NSDictionary *userDict in userArray) {
// If the usernames array doesn't already contain the username,
// add it to both the usernames array and the newUserArray as is
if (![usernames containsObject:[userDict objectForKey:#"username"]]) {
[usernames addObject:[userDict objectForKey:#"username"]];
[newUserArray addObject:userDict];
}
// Otherwise, merge the userArray entries
else {
// Get a mutable copy of the dictionary entry at the first instance
// with this username
int indexOfFirstInstance = [usernames indexOfObject:[userDict objectForKey:#"username"]];
NSMutableDictionary *entry = [[newUserArray objectAtIndex:indexOfFirstInstance] mutableCopy];
// Then combine the "text" or whatever other values you wanted to combine
// by replacing the "text" value with the combined text.
// (I've done so with a comma, but you could also store the value in an array)
[entry setValue:[[entry objectForKey:#"text"] stringByAppendingString:[NSString stringWithFormat:#", %#", [userDict objectForKey:#"text"]]] forKey:#"text"];
// Then replace this newly merged dictionary with the one at the
// first instance
[newUserArray replaceObjectAtIndex:indexOfFirstInstance withObject:entry];
}
}
Maybe something like this [untested] example? Loop through, maintain a hash of existing items, and if a duplicate is found then combine with existing and remove.
NSMutableArray main; // this should exist, with content
NSMutableDictionary *hash = [[NSMutableDictionary alloc] init];
// loop through, backwards, as we're attempting to modify array in place (risky)
for(int i = [main count] - 1; i >= 0; i--){
// check for existing
if(hash[main[i][#"username"]] != nil){
int existingIdx = [hash[main[i][#"username"]] integerValue]; // get existing location
main[existingIdx][#"text"] = [main[existingIdx][#"text"] stringByAppendingString:main[i][#"text"]]; // "combine text" .. or however you'd like to
[main removeObjectAtIndex:i]; // remove duplicate
} else {
[hash setValue:[[NSNumber alloc] initWithInt:i] forKey:main[i][#"username"]]; // mark existance, with location
}
}
If you use NSMutableDictionary, NSMutableArray and NSMutableString you can do it with predicate like that:
NSMutableDictionary *d1 = [#{#"username": #"Greg", #"text" : [#"text 1" mutableCopy]} mutableCopy];
NSMutableDictionary *d2 = [#{#"username": #"Greg", #"text" : [#"text 2" mutableCopy]} mutableCopy];
NSMutableDictionary *d3 = [#{#"username": #"John", #"text" : [#"text 3" mutableCopy]} mutableCopy];
NSMutableArray *array = [#[d1, d2, d3] mutableCopy];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"username = %#", #"Greg"];
NSArray *filterArray = [array filteredArrayUsingPredicate:predicate];
NSMutableDictionary * firstDict = filterArray[0];
for (NSDictionary *d in filterArray)
{
if (firstDict != d)
{
[firstDict[#"text"] appendString:d[#"text"]];
[array removeObject:d];
}
}

Merging dictionaries - Incompatible type error

I've been trying to merge two NSDictionaries for a couple hours now. Searched and found that I can use [NSMutableDictionary addEntriesFromDictionary:].
NSDictionary *areaAttributes = [[area entity] attributesByName];
NSDictionary *gpsAttributes = [[gps entity] attributesByName];
NSMutableDictionary *areaAttributesM = [areaAttributes mutableCopy];
NSMutableDictionary *gpsAttributesM = [gpsAttributes mutableCopy];
NSMutableDictionary *combinedAttributes = [areaAttributesM addEntriesFromDictionary:gpsAttributesM];
But I get the error:
Initializing 'NSMutableDictionary *_strong' with an expression of incompatible type 'void'
So this is saying that [areaAttributesM addEntriesFromDictionary:gpsAttributesM] returns void? Is my understanding correct? And why is it returning void?
Yes, you are correct. From the docs:
- (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary
As to why, that's simple: Functions that mutate an object in place in Cocoa usually return void, so you can easily distinguish them from functions that return a different object.
Also, there's no reason to mutableCopy the gpsAttributes dictionary; it's just being used as the argument to -[addEntriesFromDictionary:], which doesn't need to be mutable.
So, the right way to do this is:
NSDictionary *areaAttributes = [[area entity] attributesByName];
NSDictionary *gpsAttributes = [[gps entity] attributesByName];
NSMutableDictionary *combinedAttributes = [areaAttributes mutableCopy];
[combinedAttributes addEntriesFromDictionary:gpsAttributes];
You may want to wrap this up in a function (or a method in a category on NSDictionary), if you do if often:
NSDictionary *mergeDictionaries(NSDictionary *lhs, NSDictionary *rhs) {
NSMutableDictionary *ret = [lhs mutableCopy];
[ret addEntriesFromDictionary:rhs];
return ret;
}
From the Documentation, addEntriesFromDictionary tells that:
If both dictionaries contain the same key, the receiving dictionary’s previous value object for that key is sent a release message, and the new value object takes its place.
You need to use setObject to add each object to the dictionary.YOu need to loop through the keys of one dictionary and add it to the final dictionary.
Even setObject tells the same:
The key for value. The key is copied (using copyWithZone:; keys must conform to the NSCopying protocol). If aKey already exists in the dictionary, anObject takes its place.
You cannot have two same keys in the dictionary. All keys in the dictionary are unique.
If you still want to have the same key-value in the dictionary, you must use a different key.
For example, you have two dictionaries with following values:
NSDictionary *dict1=#{#"hello":#"1",#"hello2" :#"2"};
NSDictionary *dict2=#{#"hello":#"1",#"hello2":#"2",#"hello3":#"1",#"hello6":#"2",#"hello4":#"1",#"hello5" :#"2"};
NSMutableDictionary *mutableDict=[NSMutableDictionary dictionaryWithDictionary:dict1];
for (id key in dict2.allKeys){
for (id subKey in dict1.allKeys){
if (key==subKey) {
[mutableDict setObject:dict2[key] forKey:[NSString stringWithFormat:#"Ext-%#",key]];
}else{
[mutableDict setObject:dict2[key] forKey:key];
}
}
}
and by the end of this loop, your new mutable dictionaries will have the follwoing key-values:
{
"Ext-hello" = 1;
"Ext-hello2" = 2;
hello = 1;
hello2 = 2;
hello3 = 1;
hello4 = 1;
hello5 = 2;
hello6 = 2;
}
As you can see, hello, and hello2 keys are renamed as Ext-hello1, Ext-hello2. form the dict1, and you still have all the dict2 values added to your mutable dict.
IF you don't want to add a new key, then you can add the values into an arrya and add that array to the dictionary. YOu can modify the for-loop to:
for (id key in dict2.allKeys){
for (id subKey in dict1.allKeys){
if (key==subKey) {
NSMutableArray *myArr=[[NSMutableArray alloc]init];
[myArr addObject:dict1[subKey]];
[myArr addObject:dict2[key]];
[mutableDict setObject:myArr forKey:key];
}else{
[mutableDict setObject:dict2[key] forKey:key];
}
}
}
And now you will have the values merged into an array:
{
hello = (
1,
1
);
hello2 = 2;
hello3 = 1;
hello4 = 1;
hello5 = 2;
hello6 = 2;
}
In this way, the number of keys will be same, and the values for the same key will be added as an array.

NSMutableDictionary -- using allKeysforObject not retrieving array values

NSMutableDictionary *expense_ArrContents = [[NSMutableDictionary alloc]init];
for (int i = 1; i<=4; i++) {
NSMutableArray *current_row = [NSMutableArray arrayWithObjects:#"payer_id",#"Expense_Type_id",#"Category_Id",#"SubCategory_Id",nil];
[expense_ArrContents setObject:current_row forKey: [NSNumber numberWithInt:i]];
}
NSArray *newArray = [expense_ArrContents allKeysForObject:#"payer_id"];
NSLog(#"%#",[newArray description]);
i want to get the list of key values containing the particular object which is in the array of values stored in nsmutabledictionary for a particular key.
In the line where you get all the keys ([expense_ArrContents allKeysForObject:#"payer_id"];) you actually get keys for an object that is not in any of the array's items. This #"player_id" is different object than the #"player_id" you added in current_row. In fact, maybe all of your rows have different #"player_id" objects (except if the compiler has made some optimization - maybe it threats that same string literal as one object instead of creating new object for each iteration).
Try creating an NSString object for the #"player_id" which you add to the current_row and then get all the keys for that same object:
NSString* playerId = #"player_id";
for(){
NSMutableArray *current_row = [NSMutableArray arrayWithObjects: playerId,...];
...
}
NSArray *newArray = [expense_ArrContents allKeysForObject:playerId];
Your NSArray *newArray = [expense_ArrContents allKeysForObject:#"payer_id"]; will not return any value because in expense_ArrContents there is no such key(#"payer_id"), instead there are keys like 1,2,3 etc.What is your requirement?Want to see what all keys are there in expense_ArrContents just log
NSArray*keys=[expense_ArrContents allKeys];
Try this :
NSMutableArray *array_key=[[NSMutableArray alloc]init];
for (NSString *key in expense_ArrContents) {
if ([[expense_ArrContents objectForKey:key] containsObject:#"payer_id"]) {
[array_key addObject:key];
}
}

Resources