11

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.

Nicolas Roy
  • 3,773
  • 5
  • 27
  • 42
  • 1
    Those keys never where ordered. Dictionaries don't have an order. – Matthias Bauch Jul 19 '13 at 11:47
  • possible duplicate of [How can i get Original order of NSDictionary/NSMutableDictionary?](http://stackoverflow.com/questions/3386921/how-can-i-get-original-order-of-nsdictionary-nsmutabledictionary) – Amar Jul 19 '13 at 11:48

4 Answers4

26

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);
βhargavḯ
  • 9,786
  • 1
  • 37
  • 59
  • But I don't have such thing as "Foo1", "Foo2"... I don't have any numbers in my key name. My API returns key = "Cinema" value = list of cinema, key = "Theatre" , value = list of theatre, and I need to display them in that order : Cinemas first, then theaters. – Nicolas Roy Jul 19 '13 at 11:47
  • @βhargavḯ I don,t want to sort these keys where I want the keys should be received without changing order. So is it possible? please help. – Pradumna Patil Jun 10 '15 at 12:44
  • @βhargavḯ Thanks.. Its working for numeric order for dictionary :) – Manann Sseth Aug 07 '15 at 13:03
  • use sortedArrayUsingSelector instead sortUsingSelector – Siddhartha Moraes May 04 '17 at 09:18
1

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];
Nicolas Roy
  • 3,773
  • 5
  • 27
  • 42
  • No such method exists: "sortedArrayUsingArray". Use: http://blog.pioneeringsoftware.co.uk/2008/08/20/sorting-an-array-by-another-array/ – Sanjay Chaudhry Jul 19 '13 at 17:04
0

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.

GeneralMike
  • 2,951
  • 3
  • 28
  • 56
0

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.

Ev.
  • 7,109
  • 14
  • 53
  • 87
JuanM.
  • 434
  • 1
  • 8
  • 19