Question

Ce que je veux faire semble assez simple, mais je ne trouve aucune réponse sur le Web. J'ai un NSMutableArray objet et disons qu'il s'agit d'un objet 'Personne'. Je veux trier le NSDate par Person.birthDate qui est un <=>.

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

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

En Java, je voudrais que mon objet implémente Comparable ou j'utilise Collections.sort avec un comparateur personnalisé en ligne ... comment peut-on faire cela en Objective-C?

Était-ce utile?

La solution

Méthode de comparaison

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 (meilleur)

ou généralement encore mieux:

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

Vous pouvez facilement trier plusieurs clés en ajoutant plusieurs clés dans le tableau. L'utilisation de méthodes de comparaison personnalisées est également possible. Consultez la page la documentation .

Blocs (brillants!)

Il existe également 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];
}];

Performances

Les méthodes -compare: et basées sur les blocs seront généralement un peu plus rapides que d'utiliser NSSortDescriptor car ce dernier repose sur KVC. Le principal avantage de la méthode NSTableView est qu’elle offre un moyen de définir votre ordre de tri en utilisant des données plutôt que du code, ce qui facilite l’exécution par exemple. configurez les éléments pour que les utilisateurs puissent trier un <=> en cliquant sur la ligne d'en-tête.

Autres conseils

Voir la NSMutableArray méthode sortUsingFunction:context:

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

Les deux objets ne sont que des occurrences de NSOrderedAscending. Le troisième objet est une chaîne, par exemple. @ & "BirthDate &";.

Cette fonction retourne un PersonA.birthDate: Elle retourne PersonB.birthDate si NSOrderedDescending < NSOrderedSame Il retournera <=> si <=> & Gt; <=> Enfin, il retournera <=> si <=> == <=>.

Ceci est un pseudo-code approximatif; vous devrez préciser ce que cela signifie pour une date & "Moins &"; & "Plus &"; ou " égal à " à une autre date (par exemple, en comparant secondes-depuis-é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 cela si vous le faites souvent.

Je l'ai fait dans iOS 4 en utilisant un bloc. J'ai dû convertir les éléments de mon tableau d'id à mon type de classe. Dans ce cas, il s’agissait d’une classe appelée Score avec une propriété appelée points.

Vous devez également décider quoi faire si les éléments de votre tableau ne sont pas du bon type. Pour cet exemple, je viens de renvoyer NSOrderedSame, mais dans mon code, j’ai 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: le tri s'effectue par ordre décroissant.

À partir d'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 renvoie 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 " trié " tableau sera auto-publié.

J'ai tout essayé, mais cela a fonctionné pour moi. Dans une classe, j'ai une autre classe nommée & "; crimeScene &", Et je veux trier par une propriété de & "; <=> &";.

.

Cela fonctionne comme un charme:

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

Il manque une étape dans Georg Sch & La deuxième réponse de # 246; Lly , mais tout se passe bien alors.

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

Merci, ça fonctionne très bien ...

Vos Person objets doivent implémenter une méthode, par exemple compare: qui utilise un autre NSComparisonResult objet, et renvoient sortedArrayUsingSelector: en fonction de la relation entre les 2 objets.

Ensuite, vous appelez @selector(compare:) avec Comparable et vous devez le faire.

Il existe d'autres moyens, mais pour autant que je sache, il n'y a pas d'équivalent Cocoa de l'interface <=>. Utiliser <=> est probablement le moyen le plus simple de le faire.

Les blocs iOS 4 vous feront économiser:)

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

Pour NSMutableArray, utilisez la méthode sortUsingSelector. Il trie son emplacement sans créer de nouvelle instance.

Vous pouvez utiliser la méthode générique suivante à cette fin. Cela devrait résoudre votre problème.

//Called method
-(NSMutableArray*)sortArrayList:(NSMutableArray*)arrDeviceList filterKeyName:(NSString*)sortKeyName ascending:(BOOL)isAscending{
    NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:sortKeyName ascending:isAscending];
    [arrDeviceList sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
    return arrDeviceList;
}

//Calling method
[self sortArrayList:arrSomeList filterKeyName:@"anything like date,name etc" ascending:YES];

Si vous ne faites que trier un tableau de NSNumbers, vous pouvez les trier en 1 appel:

[arrayToSort sortUsingSelector: @selector(compare:)];

Cela fonctionne car les objets du tableau (NSNumber objets) implémentent la méthode de comparaison. Vous pouvez faire la même chose pour les NSString objets, ou même pour un tableau d'objets de données personnalisés qui implémentent une méthode de comparaison.

Voici un exemple de code utilisant des blocs de comparaison. Il trie un tableau de dictionnaires dans lequel chaque dictionnaire comprend un numéro dans une clé & "Sort_key &";.

.
#define SORT_KEY @\"sort_key\"

[anArray sortUsingComparator: 
 ^(id obj1, id obj2) 
  {
  NSInteger value1 = [[obj1 objectForKey: SORT_KEY] intValue];
  NSInteger value2 = [[obj2 objectForKey: SORT_KEY] intValue];
  if (value1 > value2) 
{
  return (NSComparisonResult)NSOrderedDescending;
  }

  if (value1 < value2) 
{
  return (NSComparisonResult)NSOrderedAscending;
  }
    return (NSComparisonResult)NSOrderedSame;
 }];

Le code ci-dessus explique comment obtenir une valeur entière pour chaque clé de tri et les comparer, afin d’illustrer comment procéder. Puisque les <=> objets implémentent une méthode de comparaison, celle-ci pourrait être réécrite beaucoup plus simplement:

 #define SORT_KEY @\"sort_key\"

[anArray sortUsingComparator: 
^(id obj1, id obj2) 
 {
  NSNumber* key1 = [obj1 objectForKey: SORT_KEY];
  NSNumber* key2 = [obj2 objectForKey: SORT_KEY];
  return [key1 compare: key2];
 }];

ou le corps du comparateur pourrait même être distillé jusqu'à 1 ligne:

  return [[obj1 objectForKey: SORT_KEY] compare: [obj2 objectForKey: SORT_KEY]];

J'ai tendance à préférer les instructions simples et de nombreuses variables temporaires, car le code est plus facile à lire et à déboguer. Le compilateur optimise malgré tout les variables temporaires, la version tout en un ne présente donc aucun avantage.

J'ai créé une petite bibliothèque de méthodes de catégorie, appelée de Linq à ObjectiveC , ce qui rend cette sorte de chose plus facile. En utilisant la méthode sort avec un sélecteur de clé, vous pouvez trier par birthDate comme suit:

NSArray* sortedByBirthDate = [input sort:^id(id person) {
    return [person birthDate];
}]

Je viens de faire un tri sur plusieurs niveaux en fonction d'exigences personnalisées.

// trie les valeurs

    [arrItem sortUsingComparator:^NSComparisonResult (id a, id b){

    ItemDetail * itemA = (ItemDetail*)a;
    ItemDetail* itemB =(ItemDetail*)b;

    //item price are same
    if (itemA.m_price.m_selling== itemB.m_price.m_selling) {

        NSComparisonResult result=  [itemA.m_itemName compare:itemB.m_itemName];

        //if item names are same, then monogramminginfo has to come before the non monograme item
        if (result==NSOrderedSame) {

            if (itemA.m_monogrammingInfo) {
                return NSOrderedAscending;
            }else{
                return NSOrderedDescending;
            }
        }
        return result;
    }

    //asscending order
    return itemA.m_price.m_selling > itemB.m_price.m_selling;
}];

https://sites.google.com/site/greateindiaclub/ mobil-apps / ios / multilevelsortinginiosobjectivec

J'ai utilisé sortUsingFunction :: dans certains de mes projets:

int SortPlays(id a, id b, void* context)
{
    Play* p1 = a;
    Play* p2 = b;
    if (p1.score<p2.score) 
        return NSOrderedDescending;
    else if (p1.score>p2.score) 
        return NSOrderedAscending;
    return NSOrderedSame;
}

...
[validPlays sortUsingFunction:SortPlays context:nil];
-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted 
{
  NSArray *sortedArray;
  sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
  {
    return [a compare:b];
 }];
 return [sortedArray mutableCopy];
}

Vous utilisez NSSortDescriptor pour trier un NSMutableArray avec des objets personnalisés

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

Le tri NSMutableArray est très simple:

NSMutableArray *arrayToFilter =
     [[NSMutableArray arrayWithObjects:@"Photoshop",
                                       @"Flex",
                                       @"AIR",
                                       @"Flash",
                                       @"Acrobat", nil] autorelease];

NSMutableArray *productsToRemove = [[NSMutableArray array] autorelease];

for (NSString *products in arrayToFilter) {
    if (fliterText &&
        [products rangeOfString:fliterText
                        options:NSLiteralSearch|NSCaseInsensitiveSearch].length == 0)

        [productsToRemove addObject:products];
}
[arrayToFilter removeObjectsInArray:productsToRemove];

Trier à l'aide de NSComparator

Si nous voulons trier les objets personnalisés, nous devons fournir NSComparator, qui est utilisé pour comparer les objets personnalisés. Le bloc renvoie une valeur NSComparisonResult indiquant l'ordre des deux objets. Donc, pour trier tout le tableau, <=> est utilisé de la manière suivante.

NSArray *sortedArray = [employeesArray sortedArrayUsingComparator:^NSComparisonResult(Employee *e1, Employee *e2){
    return [e1.firstname compare:e2.firstname];    
}];

Tri avec NSSortDescriptor
Supposons que & # 8217; s ait, à titre d’exemple, un tableau contenant les occurrences d’une classe personnalisée, Employee ayant les attributs prénom, nom et âge. L'exemple suivant montre comment créer un descripteur NSSort pouvant être utilisé pour trier le contenu du tableau en ordre croissant en fonction de la clé d'âge.

NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES];
NSArray *sortDescriptors = @[ageDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];

Trier à l'aide de comparaisons personnalisées
Les noms sont des chaînes et lorsque vous triez des chaînes à présenter à l'utilisateur, vous devez toujours utiliser une comparaison localisée. Souvent, vous souhaitez également effectuer une comparaison insensible à la casse. Voici un exemple avec (localizedStandardCompare :) pour classer le tableau par nom et prénom.

NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc]
              initWithKey:@"lastName" ascending:YES selector:@selector(localizedStandardCompare:)];
NSSortDescriptor * firstNameDescriptor = [[NSSortDescriptor alloc]
              initWithKey:@"firstName" ascending:YES selector:@selector(localizedStandardCompare:)];
NSArray *sortDescriptors = @[lastNameDescriptor, firstNameDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];

Pour référence et discussion détaillée, veuillez vous reporter à: https://developer.apple.com/library /ios/documentation/Cocoa/Conceptual/SortDescriptors/Articles/Creating.html
http: // www.ios-blog.co.uk/tutorials/objective-c/how-to-sort-nsarray-with-custom-objects/

Grâce aux protocoles et à la programmation fonctionnelle de Swift, il est très facile de rendre votre classe conforme au protocole Comparable, d'implémenter les méthodes requises par le protocole, puis d'utiliser la fonction d'ordre élevé trié (par:) pour créer un tableau trié sans besoin d'utiliser des tableaux mutables en passant.

class Person: Comparable {
    var birthDate: NSDate?
    let name: String

    init(name: String) {
        self.name = name
    }

    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.birthDate === rhs.birthDate || lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedSame
    }

    static func <(lhs: Person, rhs: Person) -> Bool {
        return lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedAscending
    }

    static func >(lhs: Person, rhs: Person) -> Bool {
        return lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedDescending
    }

}

let p1 = Person(name: "Sasha")
p1.birthDate = NSDate() 

let p2 = Person(name: "James")
p2.birthDate = NSDate()//he is older by miliseconds

if p1 == p2 {
    print("they are the same") //they are not
}

let persons = [p1, p2]

//sort the array based on who is older
let sortedPersons = persons.sorted(by: {$0 > $1})

//print sasha which is p1
print(persons.first?.name)
//print James which is the "older"
print(sortedPersons.first?.name)

Dans mon cas, j'utilise & "; triéArrayUsingComparator &"; trier un tableau. Regardez le code ci-dessous.

contactArray = [[NSArray arrayWithArray:[contactSet allObjects]] sortedArrayUsingComparator:^NSComparisonResult(ContactListData *obj1, ContactListData *obj2) {
    NSString *obj1Str = [NSString stringWithFormat:@"%@ %@",obj1.contactName,obj1.contactSurname];
    NSString *obj2Str = [NSString stringWithFormat:@"%@ %@",obj2.contactName,obj2.contactSurname];
    return [obj1Str compare:obj2Str];
}];

Mon objet est aussi,

@interface ContactListData : JsonData
@property(nonatomic,strong) NSString * contactName;
@property(nonatomic,strong) NSString * contactSurname;
@property(nonatomic,strong) NSString * contactPhoneNumber;
@property(nonatomic) BOOL isSelected;
@end

Trier un tableau dans Swift

Pour Swifty personne ci-dessous est une technique très propre pour atteindre l'objectif ci-dessus pour globalement. Donnons un exemple de classe personnalisée de User qui possède certains attributs.

class User: NSObject {
    var id: String?
    var name: String?
    var email: String?
    var createdDate: Date?
}

Nous avons maintenant un tableau que nous devons trier sur la base de createdDate soit croissant et / ou décroissant. Ajoutons donc une fonction de comparaison de dates.

class User: NSObject {
    var id: String?
    var name: String?
    var email: String?
    var createdDate: Date?
    func checkForOrder(_ otherUser: User, _ order: ComparisonResult) -> Bool {
        if let myCreatedDate = self.createdDate, let othersCreatedDate = otherUser.createdDate {
            //This line will compare both date with the order that has been passed.
            return myCreatedDate.compare(othersCreatedDate) == order
        }
        return false
    }
}

Laissons maintenant un extension de Array pour [User]. En termes simples, ajoutons des méthodes uniquement pour les tableaux contenant uniquement Array<User> des objets.

extension Array where Element: User {
    //This method only takes an order type. i.e ComparisonResult.orderedAscending
    func sortUserByDate(_ order: ComparisonResult) -> [User] {
        let sortedArray = self.sorted { (user1, user2) -> Bool in
            return user1.checkForOrder(user2, order)
        }
        return sortedArray
    }
}

Utilisation pour l'ordre croissant

let sortedArray = someArray.sortUserByDate(.orderedAscending)

Utilisation pour l'ordre décroissant

let sortedArray = someArray.sortUserByDate(.orderedSame)

Utilisation pour le même ordre

<*>
  

La méthode ci-dessus dans <=> ne sera accessible que si le <=> est de type   <=> || <=>

Vous devez créer sortDescriptor, puis vous pouvez trier le nsmutablearray en utilisant sortDescriptor comme ci-dessous.

 let sortDescriptor = NSSortDescriptor(key: "birthDate", ascending: true, selector: #selector(NSString.compare(_:)))
 let array = NSMutableArray(array: self.aryExist.sortedArray(using: [sortDescriptor]))
 print(array)

Utilisez comme ceci pour les objets imbriqués,

NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastRoute.to.lastname" ascending:YES selector:@selector(caseInsensitiveCompare:)];
NSMutableArray *sortedPackages = [[NSMutableArray alloc]initWithArray:[packages sortedArrayUsingDescriptors:@[sortDescriptor]]];

lastRoute est un objet et cet objet contient l'objet to, celui-ci contenant les valeurs de chaîne du dernier nom.

NSSortDescriptor  *sort = [[NSSortDescriptor alloc] initWithKey:@"_strPrice"
                                                 ascending:sortFlag selector:@selector(localizedStandardCompare:)] ;
NSMutableArray *stockHoldingCompanies = [NSMutableArray arrayWithObjects:fortune1stock,fortune2stock,fortune3stock,fortune4stock,fortune5stock,fortune6stock , nil];

NSSortDescriptor *sortOrder = [NSSortDescriptor sortDescriptorWithKey:@"companyName" ascending:NO];

[stockHoldingCompanies sortUsingDescriptors:[NSArray arrayWithObject:sortOrder]];

NSEnumerator *enumerator = [stockHoldingCompanies objectEnumerator];

ForeignStockHolding *stockHoldingCompany;

NSLog(@"Fortune 6 companies sorted by Company Name");

    while (stockHoldingCompany = [enumerator nextObject]) {
        NSLog(@"===============================");
        NSLog(@"CompanyName:%@",stockHoldingCompany.companyName);
        NSLog(@"Purchase Share Price:%.2f",stockHoldingCompany.purchaseSharePrice);
        NSLog(@"Current Share Price: %.2f",stockHoldingCompany.currentSharePrice);
        NSLog(@"Number of Shares: %i",stockHoldingCompany.numberOfShares);
        NSLog(@"Cost in Dollars: %.2f",[stockHoldingCompany costInDollars]);
        NSLog(@"Value in Dollars : %.2f",[stockHoldingCompany valueInDollars]);
    }
    NSLog(@"===============================");
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top