2

Im trying to sort through my Firebase Data by putting the User with the highest combat power at the top and the lowest at the bottom. However, I've never used NSSortDescriptor before and it's not working. This crashes and says:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryM sortedArrayUsingDescriptors:]: unrecognized selector sent to instance 0x174056e60'

-(void) getObjectCount
{
    self.ref = [[FIRDatabase database] reference];
    posts = [ref child:@"posts"];

    [posts observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot)
     {
         myCount = snapshot.childrenCount;
         for (snapshot in snapshot.children)
         {
            self.userPosts = snapshot.value;

             NSLog(@"BEFORE Sorting *** : %@",userPosts.description);

             NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"Combat Power" ascending:NO];
             NSArray *sorter = @[ sort ];
             NSArray *sortedArray = [userPosts sortedArrayUsingDescriptors:sorter];

             NSLog(@"After Sorting *** : %@",sortedArray.description);
         }
         [self.tableView reloadData];
     }];
}
adjuremods
  • 2,938
  • 2
  • 12
  • 17
  • 1
    You've already asked Firebase to queryOrderByChild so they will be in order lowest to highest. To get the highest at the top you can sort it again (as you are doing) or push the easy button an insert them into the array into position 0 as they are read in. That will leave the highest combat power at index 0 in the array. You could also read the array in as Firebase delivers it and then leverage the reverseObjectEnum like this NSArray* userPostsArray = [[justReadInArray reverseObjectEnumerator] allObjects]; – Jay Oct 30 '16 at 13:30
  • I'm afraid thats not working. My method for sorting them in the above code crashes. –  Oct 30 '16 at 13:36
  • My guess is that they have to be put into an NSDictionary first? Not sure tho... –  Oct 30 '16 at 13:37
  • Read my updated comment for other options. Your code will crash as it's storing a FDataSnapshot in the array and yes, it needs to be a NSDictionary so you you can access the keys as NSSortDescriptors. See [Sort Array Of Objects](http://stackoverflow.com/questions/805547/how-to-sort-an-nsmutablearray-with-custom-objects-in-it/805589#805589) for some great tips on sorting. – Jay Oct 30 '16 at 13:40
  • This is what my print out looks like `BEFORE Sorting ******************************** : { "Combat Power" = 1854; Date = "2016-10-29 15:21:09"; Name = "Joshua Hart"; Pokemon = Gyarados;` –  Oct 30 '16 at 18:03
  • You've got a number of issues but the main one is Snapshot.value is a dictionary. You need to iterate over the snapshot and add each child in that dictionary to your array. You can do this with a for...loop. You cannot sort the array until that's done. – Jay Oct 31 '16 at 18:53
  • I expounded on my original comment with an answer so maybe that will help clarify. – Jay Oct 31 '16 at 19:12
  • @JoshuaHart you got your answer???? – Bhavin Bhadani Nov 08 '16 at 05:27
  • The answer provided is a solution to your question and does function correctly. The code is documented. Is there something not working? If so, please provide that information so we can further assist. – Jay Nov 09 '16 at 18:51
  • @JoshuaHart can you add a breakpoint after `self.userPosts = snapshot.value;`, enter `po self.userPosts[0]` in the Console and add the output to your question? – d.felber Nov 10 '16 at 07:48

5 Answers5

1

I think's it's helpful for you below the code work for me.

//assume userPosts is initialized as an empty mutable array
-(void) getObjectCount
{
    [[_ref child:@"posts"] observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot)
     {
         myCount = snapshot.childrenCount;
         for (snapshot in snapshot.children)
         {
             self.userPosts = snapshot.value;

             NSLog(@"BEFORE Sorting *** : %@",userPosts.description);
             NSSortDescriptor *sortDescript = [NSSortDescriptor sortDescriptorWithKey:@"Combat Power" ascending:YES];
             [userPosts sortUsingDescriptors:[NSArray arrayWithObject:sortDescript]];

             NSLog(@"After Sorting *** : %@",userPosts.description);
         }
         [self.tableView reloadData];
     }];
}

and you insert the key for "Combat Power" so you can add the value for string format. for example firebase database show below this format.

{
     Combat Power : "1854" 
}
Ravi Dhorajiya
  • 1,531
  • 3
  • 21
  • 26
0

userPosts is an NSDictionary type object. You can't sort the items in a dictionary, that doesn't make a whole lot of sense. Put your items in an array first.

noobular
  • 3,257
  • 2
  • 24
  • 19
0

Here's an example of reading in a users node and iterating over each user and adding it to an array. Then sorting the array by name

users
  -uid_0
    name: "User 0"
  -uid_1
    name: "User 1"
  -uid_2
    name: "User 2"


//assume self.userArray is initialized as an empty array
//  and myRef is the users node shown above
[myRef observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
    for ( FDataSnapshot *child in snapshot.children) {
         NSDictionary *dict = child.value; //move each child into a dictionary
         [self.userArray addObject:dict] //add each dict to the array
    }

    //sort the array by name
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name"
                                             ascending:YES]; //sorting by 'name' ascending
    //now build the sortDescriptor array which is required to perform the sort
    NSArray *arrayOfDescriptors = [NSArray arrayWithObject:sortDescriptor];

    //now sort the array in place
    [self.userArray sortUsingDescriptors: arrayOfDescriptors];

}

The other option is to let Firebase return the data ordered by combat power using orderedBy.

Then insert each child into index 0 of the array when the snapshot is iterated over. The will result in the last object being read in (the highest combat power) being in position 0 in the array.

    for ( FDataSnapshot *child in snapshot.children) {
         NSDictionary *dict = child.value;
         [self.userArray insertObject:dict atIndex:0]
    }
Jay
  • 34,438
  • 18
  • 52
  • 81
0

By looking in to logs printed before crash snapshot.value is clearly NSDictionary object. Please modify your code as below and it should work.

-(void) getObjectCount
{
    self.ref = [[FIRDatabase database] reference];
    posts = [ref child:@"posts"];

    [posts observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot)
    {
         UInt32 myCount = snapshot.childrenCount;
         for (snapshot in snapshot.children)
         {
             id value = snapshot.value;
             if ([value isKindOfClass:[NSDictionary class]] {
                 value = @[value]
             }
             self.userPosts = value;

             NSLog(@"BEFORE Sorting *** : %@",userPosts.description);

             NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"Combat Power" ascending:NO];
             NSArray *sorter = @[ sort ];
             NSArray *sortedArray = [userPosts sortedArrayUsingDescriptors:sorter];

             NSLog(@"After Sorting *** : %@",sortedArray.description);
         }
         [self.tableView reloadData];
     }];
}
Aruna Mudnoor
  • 4,795
  • 14
  • 16
0

The way I got this to work was to use "queryOrderedByChild" in the get method, and then yourNewArray = [yourArray reverseObjectEnumerator].allObjects of the Array.

Easiest way to do it.