I have array which has dictionaries. each dictionary is :
NSDictionary *imageAndIndex=[[NSDictionary alloc] initWithObjectsAndKeys:image,[NSNumber numberWithLong:index], nil];
Where the object is image, and the key is NSNumber key made from index.
I want to sort the array according to the NSNumbers indexes so it will become:
0,1,2,3,4 ..
How can i use NSSortDescriptor ?
The problem (and the debate herein) is complicated by two factors: 1) The OP design choice to sort based on a dictionary key, rather than on a value. #sooper in comments pointed out correctly that the better design would be to add a #"sortBy" key, whose value is the NSNumber to be sorted. 2) The second complication is the question's reference to NSSortDescriptor, which is going to depend upon values for a given key, not the key itself.
I think the right answer is to take the #sooper suggestion to add #"sortBy" key-value pairs, but if you must sort the data as is...
- (void)sortDictionaries {
NSDictionary *d0 = #{ #0: someUIImage0};
NSDictionary *d1 = #{ #1: someUIImage1};
NSDictionary *d2 = #{ #": someUIImage2};
NSArray *unsorted = #[d1, d2, d0];
NSArray *sorted = [unsorted sortedArrayUsingComparator:^(NSDictionary *obj1, NSDictionary *obj2) {
NSNumber *key1 = [self numericKeyIn:obj1];
NSNumber *key2 = [self numericKeyIn:obj2];
return [key1 compare:key2];
}];
NSLog(#"%#", sorted);
}
- (NSNumber *)numericKeyIn:(NSDictionary *)d {
// ps. yuck. what do we want to assume here?
// that it's a dictionary?
// that it has only one key value pair?
// that an NSNumber is always one of the keys?
return [d allKeys][0];
}
Not sure why we had to handle this with so much ill-temperment. It's programming, it's supposed to be fun!
Anyway, here's how you'd do it with a sort key and sort descriptor:
- (void)betterSortDictionaries {
NSDictionary *d0 = #{ #"image":image1, #"sortBy":#0 };
NSDictionary *d1 = #{ #"image":image2, #"sortBy":#1 };
NSDictionary *d2 = #{ #"image":image3, #"sortBy":#2 };
NSArray *unsorted = #[d1, d2, d0];
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"sortBy" ascending:YES];
NSArray *sorted = [unsorted sortedArrayUsingDescriptors:#[descriptor]];
NSLog(#"%#", sorted);
}
Related
Let's assume I have this NSDictionary:
NSDictionary *d = #{
#"124":#[#"-40",#"1489365614.248664"],
#"130":#[#"-40",#"1489365604.258358"],
#"134":#[#"-40",#"1489365615.49739"],
#"53":#[#"-40",#"1489365610.502131"],
#"57":#[#"-40",#"1489365609.253352"],
#"73":#[#"-40",#"1489365608.004844"],
#"89":#[#"-44",#"1489365611.750874"],
#"91":#[#"-64",#"1489365606.755874"],
#"93":#[#"-45",#"1489365605.507149"],
#"96":#[#"-45",#"1489365613.000054"]
};
I can sort it by the first value of the array like this:
NSArray *sortedKeys = [d keysSortedByValueUsingComparator: ^(NSArray *obj1, NSArray *obj2) {
return (NSComparisonResult)[obj1[0] compare:obj2[0]];
}];
And will return this array:
#[73,134,53,124,130,57,89,96,93,91]
Which translates to the dictionary been like this:
#{
#"73": #[#"-40",#"1489365608.004844"],
#"134":#[#"-40",#"1489365615.49739"],
#"53": #[#"-40",#"1489365610.502131"],
#"124":#[#"-40",#"1489365614.248664"],
#"130":#[#"-40",#"1489365604.258358"],
#"57": #[#"-40",#"1489365609.253352"],
#"89": #[#"-44",#"1489365611.750874"],
#"96": #[#"-45",#"1489365613.000054"],
#"93": #[#"-45",#"1489365605.507149"],
#"91": #[#"-64",#"1489365606.755874"]
};
Now, as you might imagine, the second value of the array in the dictionary, is a timestamp. And if the first values are equal, I'd like to sort by this timestamp so I can get the newest one first.
If I was able to do that, I should be getting back an array like this:
#[134,124,53,57,73,130,89,96,93,91]
Which would translate the dictionary to actually be like this:
#{
#"134":#[#"-40",#"1489365615.49739"],
#"124":#[#"-40",#"1489365614.248664"],
#"53": #[#"-40",#"1489365610.502131"],
#"57": #[#"-40",#"1489365609.253352"],
#"73": #[#"-40",#"1489365608.004844"],
#"130":#[#"-40",#"1489365604.258358"],
#"89": #[#"-44",#"1489365611.750874"],
#"96": #[#"-45",#"1489365613.000054"],
#"93": #[#"-45",#"1489365605.507149"],
#"91": #[#"-64",#"1489365606.755874"]
};
Hope it makes sense and someone has an answer.
Thanks
Use this logic:
NSArray *sort = [d keysSortedByValueUsingComparator:^(NSArray *obj1, NSArray *obj2) {
NSComparisonResult result = [obj1[0] compare:obj2[0]];
if (result == NSOrderedSame) {
result = [obj2[1] compare:obj1[1]];
}
return result;
}];
If there's a tie, just compare the second values...
NSArray *sortedKeys = [d keysSortedByValueUsingComparator: ^(NSArray *obj1, NSArray *obj2) {
NSComparisonResult result = [obj1[0] compare:obj2[0]];
return (result == NSOrderedSame)? [obj2[1] compare:obj1[1]] : result;
}];
This question already has answers here:
sort NSDictionary values by key alphabetical order
(4 answers)
Closed 6 years ago.
I have a dictionary in which, for a single key(for example key "0") there are a key value pair data.The keys are like name, id,p_id. I want to sort the NSMutableDictionary for the values related to the Key "name". The data in the dictionary is as follows,
0 = {
id = 12;
name = "Accounts ";
"p_id" = 13222071;
};
1 = {
id = 13;
name = "consultant";
"p_id" = 15121211;
};
2 = {
id = 11;
name = "Tania";
"p_id" = 10215921;
};
}
Any help is appreciated!
Please try out the below code:
[yourMutableArray sortUsingComparator: (NSComparator)^(NSDictionary *a, NSDictionary *b) {
NSString *key1 = [a objectForKey: #"name"];
NSString *key2 = [b objectForKey: #"name"];
return [key1 compare: key2];
}];
NSLog(#"Sorted Array By name key : %#", yourMutableArray);
Hope this helps!
NSArray *sortedKeys = [dict.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSDictionary *d1, NSDictionary *d2) {
return [d1[#"name"] compare:d2[#"name"]];
}];
NSArray *objects = [dict objectsForKeys:sortedKeys notFoundMarker:[NSNull null]];
Dictionaries are not sorted, and doesn't resemble any order. What you should do is to getAll the keys first. Then apply a sort method on the keys, then request the objects according to the ordered keys.
E.g:
NSArray *keys = [dictionary allKeys];
NSArray *sortedKeys = <sort the keys according to your preferred method>
Now you can iterate the Dictionary from the order of the array sortedKeys.
While it has been made abundantly clear that Dictionaries can't be sorted and rightfully so, that does not mean the ends you are aiming for can't be achieved. This code will do that for you:
NSArray *arrayOfDicts = dic.allValues; //Now we got all the values. Each value itself is a dictionary so what we get here is an array of dictionaries
NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES]; //Create sort descriptor for key name
NSArray *sortingDesc = [NSArray arrayWithObject:nameDescriptor];
NSArray *sortedArray = [arrayOfDicts sortedArrayUsingDescriptors:sortingDesc]; //Get sorted array based on name
NSMutableDictionary *kindaSortedDict = [[NSMutableDictionary alloc] init];
int keyForDict=0;
for(NSDictionary *valDict in sortedArray)
{
[kindaSortedDict setObject:valDict forKey:[NSString stringWithFormat:#"%i",keyForDict]]; //Set values to our new dic which will be kind of sorted as the keys will be assigned to right objects
keyForDict++;
}
//Now you can simply get sorted array of keys from kindaSortedDic and results for them will always be sorted alphabetically. Alternatively you can just skip all that bother and directly use sortedArray
I have added comments in code to help you understand that.
For accessing sorted values I'd do this:
NSArray *sortedKeys = [kindaSortedDict.allKeys sortedArrayUsingDescriptors:
#[[NSSortDescriptor sortDescriptorWithKey:#"intValue"
ascending:YES]]];
for(NSString *key in sortedKeys)
{
NSDictionary *valDict = [kindaSortedDict objectForKey: key];
NSLog(#"Dict is: %# for key: %#",valDict,key);
}
I have an array having objects with different properties. I want to create an array of sets which contain objects with same value of a single property of the object.
Suppose this is an array of object which has property a and b
1: {a:10, b:5}, 2: {a:2,b:5}, 3: {a:20,b:5}, 4: {a:5,b:5}, 5: {a:4,b:20}, 6: {a:51,b:20}
I want to create another array of NSSet of objects with distinct values of property b
so the result would be the following Array of 2 NSSet
1: {a:10, b:5}, {a:2,b:5}, {a:20,b:5}, {a:5,b:5}
2: {a:4,b:20}, {a:51,b:20}
How can this be done?
I'd do this by first creating a dictionary of sets where the keys of the dictionary are the unique values of "b".
Note: This is untested code. There could be typos here.
NSArray *objectArray = ... // The array of "SomeObject" with the "a" and "b" values;
NSMutableDictionary *data = [NSMutableDictionary dictionary];
for (SomeObject *object in objectArray) {
id b = object.b;
NSMutableSet *bSet = data[b];
if (!bSet) {
bSet = [NSMutableSet set];
data[b] = bSet;
}
[bSet addObject:object];
}
NSArray *setArray = [data allValues];
setArray will contain your array of sets.
This codes also assumes you have a sane isEqual: and hash implementation on your SomeObject class.
This is how you can do this:
NSArray *data = #[#{#"a":#10, #"b":#5}, #{#"a":#2,#"b":#5}, #{#"a":#4,#"b":#20}, #{#"a":#51,#"b":#20}];
NSSet *bSet = [NSSet setWithArray: [data valueForKey: #"b"]];
NSMutableArray *filteredArray = [[NSMutableArray alloc] initWithCapacity: bSet.count];
for (NSNumber *bValue in bSet) {
NSPredicate *anArrayFilterPredicate = [NSPredicate predicateWithBlock:^BOOL(NSDictionary *aDictionaryData, NSDictionary *bindings) {
if ([aDictionaryData[#"b"] isEqual:bValue]) {
return YES;
}
return NO;
}];
NSArray *uniqueBValueArray = [data filteredArrayUsingPredicate:anArrayFilterPredicate];
[filteredArray addObject:uniqueBValueArray];
}
NSLog(#"filteredArray = %#", filteredArray);
Using Key-Value coding collection operators, we can get the array of distinct values for an object. Then you could easily compute the results you want.
In your case this could be done like this.
NSArray *arrayOfDistinctObjects = [array valueForKeyPath:#"#distinctUnionOfObjects.b"];
NSMutableArray *newSetArray = [NSMutableArray new];
for (id value in arrayOfDistinctObjects) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.b == %#", value];
NSArray *filterArray = [array filteredArrayUsingPredicate:predicate];
NSSet *newSet = [NSSet setWithArray:filterArray];
[newSetArray addObject:newSet];
}
where array is the array of objects on which you wanna operate.
arrayOfDistinctObjects gives you the array of distinct values for b.
This question already has answers here:
How do I sort an NSMutableArray with custom objects in it?
(27 answers)
Closed 8 years ago.
I have array of dictionaries that have NSNumber for key #"id". I want to sort this array based on #"id" value. How can I do it?
You can sorting dictionary using NSSortDescriptor. Please try this :
// Example array containing three dictionaries with "id" and "name" keys
NSArray *unsortedArray = #[#{ #"id":#3, #"name":#"abc"}, #{ #"id":#1, #"name":#"123" }, #{ #"id": #2, #"name":#"xyz" }];
NSLog(#"Unsorted Array === %#", unsortedArray);
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey: #"id" ascending: YES];
NSArray *sortedArray = [unsortedArray sortedArrayUsingDescriptors: [NSArray arrayWithObject:sortDescriptor]];
NSLog(#"Sorted Array ==== %#", sortedArray);
You can do this easily with -[NSArray sortedArrayUsingComparator:] and a comparison block.
The comparison block needs to return NSComparisonResult. Fortunately, your values associated with key "id" are NSNumbers, so simply return the result of -[NSNumber compare:].
// Example array containing three dictionaries with "id" keys
NSArray *unsortedArray = #[#{ #"id": #3 }, #{ #"id": #1 }, #{ #"id": #2 }];
NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[#"id"] compare:obj2[#"id"]];
}];
NSArray is immutable so to sort your array you can replace it with a sorted version like this:
NSArray * myArray = SortArray(myArray);
// SortArray works like this.
// Helper function.
NSInteger MyComparisonFunction(id a, id b, void* context) {
NSNumber *aNum = (NSNumber*)a[#"id"];
NSNumber *bNum = (NSNumber*)b[#"id"];
return [aNum compare:bNum];
}
NSArray* SortArray (NSArray* unsorted) {
return [unsorted sortedArrayUsingFunction:MyComparisonFunction context:nil];
}
Another method would be to instead use an NSMutableArray and then sort that in a similar way.
I am trying to filter out a NSArray of NSDictionaries. With my below example, I want dict1, dict2 & dict4 grouped in one array, dict3 & dict5 grouped in second array and dict6 in third array.
I am getting this data in NSArray, so essentially the "orig" array below is my input and I know that I need to do grouping based on "Name" key.
Instead of looping through the NSArray I though of using valueForKeyPath to return me array based on the key path but this does not work (crashes with logs -[NSMutableArray addObjectsFromArray:]: array argument is not an NSArray').
Any suggestion.
NSDictionary *dict1 = #{#"Name" : #"T1", #"Age" : #"25"};
NSDictionary *dict2 = #{#"Name" : #"T1", #"Age" : #"25"};
NSDictionary *dict3 = #{#"Name" : #"T2", #"Age" : #"27"};
NSDictionary *dict4 = #{#"Name" : #"T1", #"Age" : #"25"};
NSDictionary *dict5 = #{#"Name" : #"T2", #"Age" : #"27"};
NSDictionary *dict6 = #{#"Name" : #"T3", #"Age" : #"28"};
NSArray *orig = #[dict1, dict2, dict3, dict4, dict5, dict6];
NSMutableArray *final = [NSMutableArray array];
final = [orig valueForKeyPath:#"#unionOfArrays.Name"];
NSLog(#"Final = %#", final);
It's a little hard to tell if what you want is three different arrays where each one only contains entries with a specific Name value (as your first paragraph suggests) or if you want a single array where the entries are sorted by Name (as your second paragraph suggests). Regardless,
To sort orig by the value of the Name field:
NSArray *sortedByName = [orig sortedArrayUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"Name" ascending:YES]]];
To get a new array by selecting only entries with a specific value for Name:
NSArray *t1Only = [orig filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"Name = %#", #"T1"]];
If the desired output is an array of arrays, you can get there by building a dictionary keyed by the name attribute in the orig dictionaries:
- (NSArray *)collateByName:(NSArray *)original {
NSMutableDictionary *collate = [NSMutableDictionary dictionary];
for (NSDictionary *d in original) {
NSString *newKey = d[#"Name"];
NSMutableArray *newValue = collate[newKey];
if (!newValue) {
newValue = [NSMutableArray array];
collate[newKey] = newValue;
}
[newValue addObject:d];
}
return [collate allValues];
}
It's a little verbose, but clear, I think. If you want to decide the attribute to distinguish with programmatically, pass in another param called attribute and replace the literal #"Name" with it.