I've the following array containing other arrays.
NSArray *results = [[NSArray alloc] initWithObjects:
[NSArray arrayWithObjects:#"Nickname1", [NSNumber numberWithBool:YES], nil],
[NSArray arrayWithObjects:#"Nickname2", [NSNumber numberWithBool:NO], nil],
[NSArray arrayWithObjects:#"Nickname3", [NSNumber numberWithBool:YES], nil],
[NSArray arrayWithObjects:#"Nickname4", [NSNumber numberWithBool:YES], nil],
nil ];
How do I sort the elements of this array by the second parameter (the boolean one). can I use NSSortDescriptor ? (I want all YES before).
thanks
You can use the sortedArrayUsingComparator method for this. Since you want the YES values (a constant of value 1) sorted before the NO values (a constant value of 0), just reverse the order of the objects in the compare: call.
NSArray *sortedArray = [results sortedArrayUsingComparator:^(id o1, id o2) {
NSArray *ary1 = (NSArray *)o1;
NSArray *ary2 = (NSArray *)o2;
return [[ary2 objectAtIndex:1] compare:[ary1 objectAtIndex:1]];
}];
Related
Say I have a dictionary with keys (words) and values (scores) as follows:
GOD 8
DONG 16
DOG 8
XI 21
I would like to create an NSArray of dictionary keys (words) that is sorted first by score then alphabetically. From the example above this would be:
XI
DONG
DOG
GOD
What's the best way to achieve this?
I would use and NSArray of NSDictionaries and then implement it using NSSortDescriptors:
NSSortDescriptor *sdScore = [NSSortDescriptor alloc] initWithKey:#"SCORE" ascending:NO];
NSSortDescriptor *sdName = [NSSortDescriptor alloc] initWithKey:#"NAME" ascending:YES];
NSArray *sortedArrayOfDic = [unsortedArrayOfDic sortedArrayUsingDescriptors:[NSArray arrayWithObjects: sdScore, sdName, nil]];
Carlos' answer is the correct one I'm just posting the full code I ended up with just in case anyone is interested:
NSDictionary *dataSourceDict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:8], #"GOD",
[NSNumber numberWithInt:16], #"DONG",
[NSNumber numberWithInt:8], #"DOG",
[NSNumber numberWithInt:21], #"XI", nil];
NSSortDescriptor *scoreSort = [NSSortDescriptor sortDescriptorWithKey:#"SCORE" ascending:NO];
NSSortDescriptor *wordSort = [NSSortDescriptor sortDescriptorWithKey:#"WORD" ascending:YES];
NSArray *sorts = [NSArray arrayWithObjects:scoreSort, wordSort, nil];
NSMutableArray *unsortedArrayOfDict = [NSMutableArray array];
for (NSString *word in dataSourceDict)
{
NSString *score = [dataSourceDict objectForKey:word];
[unsortedArrayOfDict addObject: [NSDictionary dictionaryWithObjectsAndKeys:word, #"WORD", score, #"SCORE", nil]];
}
NSArray *sortedArrayOfDict = [unsortedArrayOfDict sortedArrayUsingDescriptors:sorts];
NSDictionary *sortedDict = [sortedArrayOfDict valueForKeyPath:#"WORD"];
NSLog(#"%#", sortedDict);
Related: NSDictionary split into two arrays (objects and keys) and then sorted both by the objects array (or a similar solution)
I couldn't test this because i'm not on a Mac (sorry if I misspelled something), but:
NSDictionary *dic1 = [NSDictionary dictionaryWithObjectsAndKeys:#"GOD", #"WORD", [NSNumber numberWithInt:8], #"SCORE", nil];
NSDictionary *dic2 = [NSDictionary dictionaryWithObjectsAndKeys:#"DONG", #"WORD", [NSNumber numberWithInt:16], #"SCORE", nil];
NSDictionary *dic3 = [NSDictionary dictionaryWithObjectsAndKeys:#"DOG", #"WORD", [NSNumber numberWithInt:8], #"SCORE", nil];
NSDictionary *dic4 = [NSDictionary dictionaryWithObjectsAndKeys:#"XI", #"WORD", [NSNumber numberWithInt:21], #"SCORE", nil];
NSSortDescriptor *scoreSort = [NSSortDescriptor sortDescriptorWithKey:#"SCORE" ascending:NO];
NSSortDescriptor *wordSort = [NSSortDescriptor sortDescriptorWithKey:#"WORD" ascending:YES];
NSArray *sortedArrayOfDic = [[NSArray arrayWithObjects:dic1, dic2, dic3, dic4, nil] sortedArrayUsingDescriptors:[NSArray arrayWithObjects:scoreSort, wordSort, nil]];
NSLog(#"%#", [sortedArrayOfDict valueForKeyPath:#"WORD"]);
This would do the same but a bit reduced and avoiding an iteration.
Aim: To obtain an NSArray containing unique keys for given NSDictionary(s) using elegant code
Example Code with Current Working Solution:
NSArray *data = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1], #"a", [NSNumber numberWithInt:2], #"b", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:3], #"b", [NSNumber numberWithInt:4], #"c", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:5], #"a", [NSNumber numberWithInt:6], #"c", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:7], #"b", [NSNumber numberWithInt:8], #"a", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:8], #"c", [NSNumber numberWithInt:9], #"b", nil],
nil];
// create an NSArray of all the dictionary keys within the NSArray *data
NSMutableSet *setKeys = [[NSMutableSet alloc] init];
for (int i=0; i<[data count]; i++) {
[setKeys addObjectsFromArray:[[data objectAtIndex:i] allKeys]];
}
NSArray *arrayKeys = [setKeys allObjects];
NSLog(#"arrayKeys: %#", arrayKeys);
Which returns the desired array of keys:
2012-06-11 16:52:57.351 test.kvc[6497:403] arrayKeys: (
a,
b,
c
)
Question: Is there a more elegant way of approaching this? Surely there must be some KVC approach that can get all the keys without having to iterate through the array? I've been looking at Apple Developer Documentation and can't see a solution. Any ideas? (looking at purely elegance of code rather than performance).
Normally you could use KVC by doing something like this:
NSArray *uniqueKeys = [data valueForKeyPath:#"#distinctUnionOfArrays.allKeys";
However NSDictionary overrides the valueForKey: selector which is used by the KVC internals, so this will not work correctly.
The documentation for NSDictionary's valueForKey: method tells us that:
If key does not start with “#”, invokes objectForKey:. If key does start with “#”, strips the “#” and invokes [super valueForKey:] with the rest of the key.
So we just insert an # before allKeys:
NSArray *uniqueKeys = [data valueForKeyPath:#"#distinctUnionOfArrays.#allKeys"];
And we get what we want:
(lldb) po [data valueForKeyPath:#"#distinctUnionOfArrays.#allKeys"]
(id) $14 = 0x07bb2fc0 <__NSArrayI 0x7bb2fc0>(
c,
a,
b
)
This is less ugly, and possibly marginally faster, I suppose:
NSMutableSet *setKeys = [[NSMutableSet alloc] init];
for (NSDictionary* dict in data) {
for (id key in [dict keyEnumerator]) {
[setKeys addObject:key];
}
}
But you're not doing a particularly common operation, so I wouldn't expect to find some incredibly elegant method. If that's what you want, go learn Haskell.
You could try this:
NSMutableSet *setKeys = [[NSMutableSet alloc] init];
for(NSDictionary *dict in data) {
[setKeys addObjectsFromArray:[dict allKeys]];
}
NSArray *arrayKeys = [setKeys allObjects];
If you prefer blocks you could use this:
[data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[setKeys addObjectsFromArray:[obj allKeys]];
}];
I have:
boardValue = [NSNumber numberWithInteger: 2];
NSDictionary * dict = [NSDictionary dictionaryWithValuesForKeys: #"sample", #"word", boardValue , #"value", nil];
This is very similar to the following example:
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: #"value1", #"key1", #"value2", #"key2", nil];
from Apple documentations at:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsdictionary_Class/Reference/Reference.html
I get the error "too many arguments to method call, expect 1, have 5". What is the problem?
dictionaryWithValuesForKeys: takes an array as an argument, not a variable list of arguments.
(Also, I believe it's an instance method, not a class method, so [NSDictionary dictionaryWithValuesForKeys:args]` won't work.)
Notice your code compared to the documentation. You want to call...
boardValue = [NSNumber numberWithInteger: 2];
NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys: #"sample", #"word", boardValue , #"value", nil];
I tried to insert NSDictionary's in a NSMutableDictionary. There is no error but it won't work, it remains empty.
Here's my code:
NSMutableDictionary *einnahmen = [[NSMutableDictionary alloc] initWithCapacity:20];
NSArray *objects = [NSArray arrayWithObjects:
name,
[NSNumber numberWithInt: x],
[NSNumber numberWithInt: y],
[NSNumber numberWithInt: z],
nil];
NSArray *keys = [NSArray arrayWithObjects:
#"name",
#"startJahr",
#"zins",
#"entnahmefaehig",
nil];
NSDictionary *entry = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
[einnahmen setObject:entry forKey:#"name"]; //seems not to work
After [einnahmen setObject:entry the Debugger shows this:
I have solved this problem. The following code was in the wrong init-method:
NSMutableDictionary *einnahmen =
[[NSMutableDictionary alloc] initWithCapacity:20];
The compiler should show a error.
I have a NSMutableArray with 30 dictionaries inside of it. Each contains a name and a value. I currently have the names sorted so that the show in a table view alphabetically. However, I'd like to make a UIButton to give the option of STILL DISPLAYING THE NAMES, but ordered by the value. The value doesn't need to be displayed. Also, where in the .m/.h files would these codes be placed? THANKS!
To be able to help you I need a test data to work with. Let's say your array with data looks like this:
NSMutableArray *aMutableArray = [NSMutableArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
#"A", #"name",
[NSNumber numberWithInt:6], #"value", nil],
[NSDictionary dictionaryWithObjectsAndKeys:
#"C", #"name",
[NSNumber numberWithInt:2], #"value", nil],
[NSDictionary dictionaryWithObjectsAndKeys:
#"B", #"name",
[NSNumber numberWithInt:4], #"value", nil], nil];
In this case case here's how you sort it by value:
NSSortDescriptor *aSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"value" ascending:YES];
[aMutableArray sortUsingDescriptors:[NSArray arrayWithObject:aSortDescriptor]];
As for where to place this code place it wherever you need the reordering to happen (in the implementation [.m] file of course).
HTH and if there will be any questions ask them in the comments.