Trier NSArray des chaînes de date ou des objets

86

J'ai un NSArrayqui contient des chaînes de date (c'est-à-dire NSString) comme ceci: "Thu, 21 May 09 19:10:09 -0700"

J'ai besoin de trier NSArraypar date. J'ai d'abord pensé à convertir la chaîne de date en NSDateobjet, mais je suis resté coincé sur la façon de trier par NSDateobjet.

Merci.

service postal14
la source
Redéfini car il est lié au framework Foundation et non spécifique à l'iPhone.
Quinn Taylor

Réponses:

110

Stockez les dates en tant NSDatequ'objets dans un tableau NS (mutable), puis utilisez -[NSArray sortedArrayUsingSelector:ou -[NSMutableArray sortUsingSelector:]et passez @selector(compare:)comme paramètre. La -[NSDate compare:]méthode classera les dates par ordre croissant pour vous. C'est plus simple que de créer un NSSortDescriptor, et beaucoup plus simple que d'écrire votre propre fonction de comparaison. (les NSDateobjets savent comment se comparer les uns aux autres au moins aussi efficacement que nous pourrions espérer le faire avec du code personnalisé.)

Quinn Taylor
la source
11
Il n'est pas clair si la question d'origine portait sur le tri d'un tableau de dates ou le tri d'un tableau d'objets qui ont un champ de date. C'est l'approche la plus simple si c'est la première, mais si c'est la seconde, NSSortDescriptor est la voie à suivre.
smorgan
@smorgan Comment NSSortDescriptor gère-t-il les dates nulles?
Ash
Merci pour votre aide, je pense que nous devrions en savoir plus dans Apple Docs mais cela semble tellement ennuyeux, jusqu'à ce que vous l'utilisiez.
Abo3atef
1
Pourriez-vous montrer cela dans un extrait de code à titre d'exemple? Merci d'avance.
uplearnedu.com
115

Si j'ai un NSMutableArraydes objets avec un champ "beginDate" de type, NSDatej'utilise un NSSortDescriptorcomme ci-dessous:

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"beginDate" ascending:TRUE];
[myMutableArray sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
vinzenzweber
la source
3
Je sais que beaucoup de temps s'est écoulé (lorsque la réponse précédente a été acceptée, il ne pouvait pas encore y avoir de NSSortDescriptor), mais pour une utilisation future, celle-ci devrait être la bonne réponse.
Vive
1
C'est une solution formidable. a résolu mon problème en quelques lignes de code.Merci une tonne @vinzenzweber
Pratyusha Terli
1
Vraiment ... J'ai été choqué que cela ait fonctionné lors de la première manche! Je pensais que initWithKey était réservé aux dictionnaires où la clé est définie, mais tout ce que vous avez à faire est de référencer une propriété dans un tableau d'objets? Laissez la lumière briller vinzenzweberskavitchski !!
whyoz
NSSortDescriptor existe depuis OS X 10.3, fin 2003. Il s'agit d'un scénario légèrement différent, où la date est un champ dans un autre objet. Avec une API encore plus moderne, vous pouvez aussi bien utiliser -[NSMutableArray sortUsingComparator:](10.6+) et passer un bloc qui renvoie le résultat de l'appel -compare:sur les deux champs. Ce serait probablement plus rapide que d'appeler -valueForKey:chaque objet. Vous pouvez même utiliser -sortWithOptions:usingComparator:et transmettre des options pour trier simultanément.
Quinn Taylor
Comment le NSSortDescriptor ci-dessus gère-t-il les dates nulles? Avons-nous besoin de dire au profanateur quoi en faire si oui, comment?
Ash
17

Vous pouvez également utiliser quelque chose comme ce qui suit:

//Sort the array of items by  date
    [self.items sortUsingComparator:^NSComparisonResult(id obj1, id obj2){
        return [obj2.date compare:obj1.date];
    }];

Mais cela suppose que la date est stockée NSDateplutôt comme unNString , ce qui ne devrait pas poser de problème à faire / à faire. De préférence, je recommande également de stocker les données dans leur format brut. Facilite la manipulation dans des situations comme celle-ci.

TheCodingArt
la source
9

Vous pouvez utiliser des blocs pour trier sur place:

sortedDatesArray = [[unsortedDatesArray sortedArrayUsingComparator: ^(id a, id b) {
    NSDate *d1 = [NSDate dateWithString: s1];
    NSDate *d2 = [NSDate dateWithString: s2];
    return [d1 compare: d2];
}];

Je vous suggère de convertir toutes vos chaînes en dates avant de trier pour ne pas faire la conversion plus de fois qu'il n'y a d'éléments de date. Tout algorithme de tri vous donnera plus de conversions de chaîne à date que le nombre d'éléments dans le tableau (parfois beaucoup plus)

un peu plus sur le tri des blocs: http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html

Kostiantyn Sokolinskyi
la source
Il s'agit d'une variante de la même idée sous-optimale présentée par @notoop. Dans tous les cas, la conversion vers NSDate est certainement la voie à suivre.
Quinn Taylor
Il a simplement mis ma réponse avec la réponse de @ notoop ..... tout à fait inutile de les doubler si vous me le demandez ....
TheCodingArt
C'est beaucoup plus lent que d'analyser les chaînes de date dans le tableau de NSDate, puis de trier le tableau de NSDate, car il y aura 2 * (n - 1) fois une analyse supplémentaire de la chaîne. L'analyse des chaînes est généralement un travail lourd sur le processeur.
CarlLee
5

Ce qui a fonctionné dans mon cas était le suivant:

    NSArray * aUnsorted = [dataToDb allKeys];
    NSArray * arrKeys = [aUnsorted sortedArrayUsingComparator: ^ NSComparisonResult (id obj1, id obj2) {
        NSDateFormatter * df = [[NSDateFormatter alloc] init];
        [df setDateFormat: @ "jj-MM-aaaa"];
        NSDate * d1 = [df dateFromString: (NSString *) obj1];
        NSDate * d2 = [df dateFromString: (NSString *) obj2];
        return [d1 comparer: d2];
    }];

J'avais un dictionnaire, où toutes les clés où les dates au format jj-MM-aaaa. Et allKeys renvoie les clés du dictionnaire non triées, et je voulais présenter les données dans l'ordre chronologique.

Javier Calatrava Llavería
la source
5

Vous pouvez utiliser sortedArrayUsingFunction:context:. Voici un exemple:

NSComparisonResult dateSort(NSString *s1, NSString *s2, void *context) {
    NSDate *d1 = [NSDate dateWithString:s1];
    NSDate *d2 = [NSDate dateWithString:s2];
    return [d1 compare:d2];
}

NSArray *sorted = [unsorted sortedArrayUsingFunction:dateSort context:nil];

Lorsque vous utilisez a NSMutableArray, vous pouvez utiliser à la sortArrayUsingFunction:context:place.

notnoop
la source
9
C'est une mauvaise idée - une fonction de comparaison devrait être rapide et ne devrait pas avoir à créer des objets NSDate à partir de chaînes pour chaque comparaison. Si vous utilisez déjà - [NSDate compare:], stockez simplement les dates dans le tableau et utilisez -sortedArrayUsingSelector: directement.
Quinn Taylor
Je pense que c'est une bonne solution si vous triez des objets qui ont déjà un champ NSDate. Non?
GnarlyDog
1
Si le tableau contient déjà des NSDateobjets, vous pouvez utiliser ma réponse ci-dessus, ou même utiliser -[NSMutableArray sortUsingComparator:], qui a été ajoutée dans 10.6.
Quinn Taylor
Si vous utilisez un tableau de dictionnaire dans lequel il existe un objet clé-valeur pour la date en tant que NSString avec d'autres objets clé-valeur, cette solution est la meilleure à ce jour. Juste besoin d'une petite modification dans la fonction dateSort. Pouces vers le haut! :)
Erfan
4

Une fois que vous avez un NSDate, vous pouvez créer un NSSortDescriptoravec initWithKey:ascending:, puis l'utiliser sortedArrayUsingDescriptors:pour effectuer le tri.

smorgan
la source
Cette approche fonctionnera, mais l'utilisation de -sortedArrayUsingSelector: est un peu plus simple et ne nécessite pas la création d'un objet supplémentaire.
Quinn Taylor
Pré-édition, la question mentionnait "une clé 'date'", donc j'ai supposé que c'était vraiment un tableau d'objets / dictionnaires qui avaient une date comme un champ, pas un tableau de dates brutes. Dans ce cas, sortedArrayUsingSelector: est plus complexe, car il nécessite l'écriture d'une routine de tri personnalisée pour effectuer la recherche.
smorgan
Ouais, désolé pour toute confusion - j'ai nettoyé cette référence car il disait juste qu'il stockait son tableau avec une clé «date», et j'ai réalisé que cela rendait la question plus confuse. Vous avez certainement raison sur la complexité relative étant donné une structure de données plus complexe.
Quinn Taylor
2

Swift 3.0

myMutableArray = myMutableArray.sorted(by: { $0.date.compare($1.date) == ComparisonResult.orderedAscending })
Abhishek Jain
la source
1

Change ça

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"beginDate" ascending:TRUE];
[myMutableArray sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];

À

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"Date" ascending:TRUE];
[myMutableArray sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];

Changez simplement la CLE: elle doit Datetoujours être

user2339385
la source