Nicolas Roy
Nicolas Roy

Reputation: 3803

NSDictionary allKeys order

I need to display in a UITableView the content of a NSDictionary returned by an API, respecting the order of the keys.

I'm using :

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
        NSString *key = self.data.allKeys[indexPath.section];
        NSArray *list = self.data[key];
        id data = list[indexPath.row];

        PSSearchCell *cell = [PSSearchCell newCellOrReuse:tableView];

        cell.model = data;

        return cell;
}

but as I do self.data.allKeys, I'm loosing the order of my keys. I can't sort them by value as it doesn't concern them.

Upvotes: 11

Views: 18252

Answers (4)

JuanM.
JuanM.

Reputation: 432

You said that you were losing the order of the keys, so I suppose you pull those keys from a NSArray. Right? And in that NSArray you have the keys ordered as you want. And I also see that the objects of the NSDictionary are Arrays, right? Yes. So in your Ordered Array you have more arrays.

data is the name of the NSDictionary.

So, all you have to do is to bring that NSArray (the one that is ordered as you always wanted) into this .m file and after that use some awesome code in your cellForRowAtIndexPath method. You can do it by doing the following:

@interface yourViewController ()
{
    NSArray *yourArrayOrdered;
}
@end

@implementation yourViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    yourArrayOrdered = [[NSArray alloc] initWith...:... ];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath     *)indexPath
{
    NSString *key = yourArrayOrdered[indexPath.row];
    NSArray *list = [self.data objectForKey:yourArrayOrdered[indexPath.row]];

    //your code continues here...

    /*id data = list[indexPath.row]; //don't think you need this line

    PSSearchCell *cell = [PSSearchCell newCellOrReuse:tableView];

    cell.model = data; */

    return cell;
}

And this is all you have to do to keep the order you want using a NSDictionary and NSArray.

To visualize it better, in the case that your ordered array only contains strings it would be like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath      *)indexPath
{
    NSString *key = yourArrayOrdered[indexPath.row];

    NSString *myString = [self.data objectForKey:yourArrayOrdered[indexPath.row]];

    cell.textLabel.text = [NSString stringWithFormat:@"THIS IS THE OBJECT %@", myString];

    cell.detailTextLabel.text = [NSString stringWithFormat:@"AND THIS IS THE KEY %@", key];

    return cell;
}

Hope it helps.

Upvotes: 0

βhargavḯ
βhargavḯ

Reputation: 9836

Try this,

NSArray *keys = [myDictionary allKeys];
keys = [keys sortedArrayUsingComparator:^(id a, id b) {
    return [a compare:b options:NSNumericSearch];
}];

NSLog(@"%@",keys);

Now fetch values based on the key sorted.

EDIT

To sort them in alphabetical order try this,

NSArray *keys = [myDictionary allKeys];
keys = [[keys mutableCopy] sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];

NSLog(@"%@",keys);

Upvotes: 26

GeneralMike
GeneralMike

Reputation: 3001

I wrote a quick method to take a source array (of objects that are all out of order) and a reference array (that has objects in a desired (and totally arbitrary) order), and returns an array where the items of the source array have been reorganized to match the reference array.

- (NSArray *) reorderArray:(NSArray *)sourceArray toArray:(NSArray *)referenceArray
{
    NSMutableArray *returnArray = [[NSMutableArray alloc] init];
    for (int i = 0; i < [referenceArray count]; i++)
    {
        if ([sourceArray containsObject:[referenceArray objectAtIndex:i]])
        {
            [returnArray addObject:[arrReference objectAtIndex:i]];
        }
    }
    return [returnArray copy];
}

Note that this is very fragile. It uses NSArray's containsObject: method, which ultimately will call NSObject's isEqual:. Basically, it should work great for arrays of NSStrings, NSNumbers, and maybe NSDates (haven't tried that one yet), but outside of that, YMMV. I imagine if you tried to pass arrays of UITableViewCells or some other really complex object, it would totally sh*t itself, and either crash or return total garbage. Likewise if you were to do something like pass an array of NSDates as the reference array and an array of NSStrings as the source array. Also, if the source array contains items not covered in the reference array, they'll just get discarded. One could address some of these issues by adding a little extra code.

All that said, if you're trying to do something simple, it should work nicely. In your case, you just send arrayOne from the answer you posted as the source array, and arrayTwo as the reference array.

Upvotes: 0

Nicolas Roy
Nicolas Roy

Reputation: 3803

As everyone said, I can't order my keys as they appear in my NSDictionary log because a NSDictionary is not ordered.

I asked people from the API to return an array instead:

0 => name : key 1
     value : value 1

1 => name : key 2
     value : value 2

/----------------------------------------------------------------------------/

Too bad there isn't a method "sortedArrayUsingArray" used like that :

NSArray * arrayOne = [@"key E", @"key A", @"key C"];

NSArray * arrayTwo = [@"key A", @"key B", @"key C", @"key D", @"key E", @"key F"];

NSArray * orderedArray = [arrayOne sortedArrayUsingArray: arrayTwo];

Upvotes: 1

Related Questions