Question Comment trier un NSMutableArray avec des objets personnalisés?


Ce que je veux faire semble assez simple, mais je ne trouve aucune réponse sur le web. j'ai un NSMutableArray d'objets, et disons qu'ils sont des objets 'Personne'. Je veux trier le NSMutableArray par Person.birthDate qui est un NSDate.

Je pense que cela a quelque chose à voir avec cette méthode:

NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(???)];

En Java, je rendrais mon objet comparable à Compare, ou utiliser Collections.sort avec un comparateur personnalisé en ligne ... comment diable faites-vous cela en Objective-C?


1173
2018-04-30 06:10


origine


Réponses:


Comparer la méthode

Soit vous implémentez une méthode de comparaison pour votre objet:

- (NSComparisonResult)compare:(Person *)otherObject {
    return [self.birthDate compare:otherObject.birthDate];
}

NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(compare:)];

NSSortDescriptor (mieux)

ou habituellement encore mieux:

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                           ascending:YES];
NSArray *sortedArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];

Vous pouvez facilement trier par plusieurs clés en ajoutant plus d'un au tableau. L'utilisation de méthodes de comparaison personnalisées est également possible. Jettes un coup d'oeil à La documentation.

Blocs (brillants!)

Il y a aussi la possibilité de trier avec un bloc depuis Mac OS X 10.6 et iOS 4:

NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
    NSDate *first = [(Person*)a birthDate];
    NSDate *second = [(Person*)b birthDate];
    return [first compare:second];
}];

Performance

le -compare: et les méthodes basées sur des blocs seront en général un peu plus rapides que NSSortDescriptor comme ce dernier s'appuie sur KVC. Le principal avantage de la NSSortDescriptor méthode est qu'il fournit un moyen de définir votre ordre de tri en utilisant des données, plutôt que du code, ce qui le rend facile, par exemple. mettre en place les choses afin que les utilisateurs peuvent trier NSTableView en cliquant sur la ligne d'en-tête.


2215
2018-04-30 06:24



Voir le NSMutableArray méthode sortUsingFunction:context: 

Vous devrez configurer un comparer fonction qui prend deux objets (de type Person, puisque vous comparez deux Person objets) et un le contexte paramètre.

Les deux objets ne sont que des exemples de Person. Le troisième objet est une chaîne, par ex. @"date de naissance".

Cette fonction renvoie un NSComparisonResult: Ça revient NSOrderedAscending si PersonA.birthDate < PersonB.birthDate. Il reviendra NSOrderedDescending si PersonA.birthDate > PersonB.birthDate. Enfin, il reviendra NSOrderedSame si PersonA.birthDate == PersonB.birthDate.

C'est un pseudocode rugueux; vous aurez besoin de préciser ce que cela signifie pour une date d'être "moins", "plus" ou "égal" à une autre date (comme la comparaison des secondes depuis l'époque, etc.):

NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {
  if ([firstPerson birthDate] < [secondPerson birthDate])
    return NSOrderedAscending;
  else if ([firstPerson birthDate] > [secondPerson birthDate])
    return NSOrderedDescending;
  else 
    return NSOrderedSame;
}

Si vous voulez quelque chose de plus compact, vous pouvez utiliser des opérateurs ternaires:

NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {
  return ([firstPerson birthDate] < [secondPerson birthDate]) ? NSOrderedAscending : ([firstPerson birthDate] > [secondPerson birthDate]) ? NSOrderedDescending : NSOrderedSame;
}

Inlining pourrait peut-être accélérer un peu, si vous le faites beaucoup.


103
2018-03-24 22:15



Je l'ai fait dans iOS 4 en utilisant un bloc. J'ai dû lancer les éléments de ma matrice de l'id à mon type de classe. Dans ce cas, c'était une classe appelée Score avec une propriété appelée points.

Aussi vous devez décider quoi faire si les éléments de votre tableau ne sont pas du bon type, pour cet exemple je viens de retourner NSOrderedSame, cependant dans mon code, je pense à une exception.

NSArray *sorted = [_scores sortedArrayUsingComparator:^(id obj1, id obj2){
    if ([obj1 isKindOfClass:[Score class]] && [obj2 isKindOfClass:[Score class]]) {
        Score *s1 = obj1;
        Score *s2 = obj2;

        if (s1.points > s2.points) {
            return (NSComparisonResult)NSOrderedAscending;
        } else if (s1.points < s2.points) {
            return (NSComparisonResult)NSOrderedDescending;
        }
    }

    // TODO: default is the same?
    return (NSComparisonResult)NSOrderedSame;
}];

return sorted;

PS: C'est le tri par ordre décroissant.


58
2018-03-16 18:46



À partir de iOS 4, vous pouvez également utiliser des blocs pour le tri.

Pour cet exemple particulier, je suppose que les objets de votre tableau ont une méthode 'position', qui retourne un NSInteger.

NSArray *arrayToSort = where ever you get the array from... ;
NSComparisonResult (^sortBlock)(id, id) = ^(id obj1, id obj2) 
{
    if ([obj1 position] > [obj2 position]) 
    { 
        return (NSComparisonResult)NSOrderedDescending;
    }
    if ([obj1 position] < [obj2 position]) 
    {
        return (NSComparisonResult)NSOrderedAscending;
    }
    return (NSComparisonResult)NSOrderedSame;
};
NSArray *sorted = [arrayToSort sortedArrayUsingComparator:sortBlock];

Remarque: le tableau "trié" sera auto-libéré.


28
2017-11-22 10:51



J'ai tout essayé, mais cela a fonctionné pour moi. Dans une classe j'ai une autre classe nommée "crimeScene", et veulent trier par une propriété de"crimeScene".

Ça fonctionne super bien:

NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];

24
2018-05-06 16:25



Il y a un pas manquant dans La deuxième réponse de Georg Schölly, mais ça marche bien alors.

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                              ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptor:sortDescriptors];

19
2018-03-30 05:54



NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptors:sortDescriptors];

Merci, ça marche bien ...


18
2018-04-30 06:20



Votre Person les objets doivent mettre en œuvre une méthode, disons compare: qui prend un autre Person objet, et retour NSComparisonResult selon la relation entre les 2 objets.

Ensuite, vous appelleriez sortedArrayUsingSelector: avec @selector(compare:) et cela devrait être fait.

Il y a d'autres façons, mais pour autant que je sache, il n'y a pas d'équivalent cacao Comparable interface. En utilisant sortedArrayUsingSelector: est probablement le moyen le plus indolore de le faire.


16
2017-11-09 13:47



iOS 4 blocs vous sauvera :)

featuresArray = [[unsortedFeaturesArray sortedArrayUsingComparator: ^(id a, id b)  
{
    DMSeatFeature *first = ( DMSeatFeature* ) a;
    DMSeatFeature *second = ( DMSeatFeature* ) b;

    if ( first.quality == second.quality )
        return NSOrderedSame;
    else
    {
        if ( eSeatQualityGreen  == m_seatQuality || eSeatQualityYellowGreen == m_seatQuality || eSeatQualityDefault  == m_seatQuality )
        {
            if ( first.quality < second.quality )
                return NSOrderedAscending;
            else
                return NSOrderedDescending;
        }
        else // eSeatQualityRed || eSeatQualityYellow
        {
            if ( first.quality > second.quality )
                return NSOrderedAscending;
            else
                return NSOrderedDescending;
        } 
    }
}] retain];

http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html un peu de description


8
2017-12-06 07:51



Pour NSMutableArray, Utilisez le sortUsingSelector méthode. Il trie le lieu, sans créer une nouvelle instance.


7
2018-04-16 09:17