Domanda

Quello che voglio fare sembra piuttosto semplice, ma non riesco a trovare risposte sul web. Ho un NSMutableArray di oggetti e diciamo che sono oggetti "Person". Voglio ordinare NSDate per Person.birthDate che è <=>.

Penso che abbia qualcosa a che fare con questo metodo:

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

In Java renderei il mio oggetto implementabile Comparable, o utilizzare Collections.sort con un comparatore personalizzato inline ... come mai lo fai in Objective-C?

È stato utile?

Soluzione

Metodo di confronto

O implementate un metodo di confronto per il vostro oggetto:

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

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

NSSortDescriptor (meglio)

o di solito anche meglio:

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

È possibile ordinare facilmente per più chiavi aggiungendone più di una all'array. È anche possibile utilizzare metodi di confronto personalizzati. Dai un'occhiata a la documentazione .

Blocchi (lucido!)

Esiste anche la possibilità di ordinare con un blocco da Mac OS X 10.6 e 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];
}];

Prestazioni

I metodi -compare: e basati su blocchi saranno un po 'più veloci, in generale, rispetto all'uso di NSSortDescriptor poiché quest'ultimo si basa su KVC. Il vantaggio principale del metodo NSTableView è che fornisce un modo per definire il tuo ordinamento utilizzando i dati anziché il codice, il che semplifica ad es. impostare le cose in modo che gli utenti possano ordinare un <=> facendo clic sulla riga di intestazione.

Altri suggerimenti

Vedi il metodo NSMutableArray sortUsingFunction:context:

Dovrai impostare una funzione confronta che accetta due oggetti (di tipo Person, poiché stai confrontando due NSComparisonResult oggetti) e un contesto parametro.

I due oggetti sono solo istanze di NSOrderedAscending. Il terzo oggetto è una stringa, ad es. & @ Quot; quot birthDate &;.

Questa funzione restituisce un PersonA.birthDate: restituisce PersonB.birthDate se NSOrderedDescending < NSOrderedSame. Restituirà <=> se <=> & Gt; <=>. Infine, restituirà <=> se <=> == <=>.

Questo è uno pseudocodice approssimativo; dovrai capire cosa significa per una data essere " less " ;, " more " oppure " uguale a " a un'altra data (come il confronto dei secondi dall'epoca ecc.):

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;
}

Se vuoi qualcosa di più compatto, puoi usare operatori ternari:

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

L'allineamento potrebbe forse accelerare un po ', se lo fai molto.

L'ho fatto in iOS 4 usando un blocco. Ho dovuto trasmettere gli elementi del mio array dall'id al mio tipo di classe. In questo caso era una classe chiamata Score con una proprietà chiamata points.

Inoltre, devi decidere cosa fare se gli elementi del tuo array non sono del tipo giusto, per questo esempio sono appena tornato NSOrderedSame, tuttavia nel mio codice ho considerato un'eccezione.

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: questo è l'ordinamento in ordine decrescente.

A partire da iOS 4 puoi anche usare i blocchi per l'ordinamento.

Per questo esempio particolare suppongo che gli oggetti nell'array abbiano un metodo 'position', che restituisce 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];

Nota: il " ordinato " l'array verrà rilasciato automaticamente.

Ho provato tutto, ma questo ha funzionato per me. In una classe ho un'altra classe chiamata & Quot; crimeScene & Quot; e desidero ordinare in base a una proprietà di & Quot; <=> & Quot ;.

Funziona come un fascino:

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

C'è un passaggio mancante in Georg Sch & # 246; la seconda risposta di lly , ma allora funziona bene.

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];

Grazie, funziona benissimo ...

I tuoi Person oggetti devono implementare un metodo, dire compare: che accetta un altro NSComparisonResult oggetto e restituire sortedArrayUsingSelector: in base alla relazione tra i 2 oggetti.

Quindi chiameresti @selector(compare:) con Comparable e dovrebbe essere fatto.

Esistono altri modi, ma per quanto ne so non esiste un equivalente Cacao dell'interfaccia <=>. L'uso di <=> è probabilmente il modo più indolore per farlo.

I blocchi iOS 4 ti salveranno :)

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 po 'di descrizione

Per NSMutableArray, utilizzare il metodo sortUsingSelector. Ordina al posto, senza creare una nuova istanza.

È possibile utilizzare il seguente metodo generico per il proprio scopo. Dovrebbe risolvere il tuo problema.

//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];

Se stai solo ordinando un array di NSNumbers, puoi ordinarli con 1 chiamata:

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

Funziona perché gli oggetti nell'array (NSNumber oggetti) implementano il metodo di confronto. Potresti fare la stessa cosa per NSString oggetti o anche per una matrice di oggetti dati personalizzati che implementano un metodo di confronto.

Ecco un esempio di codice che utilizza i blocchi comparatore. Ordina una serie di dizionari in cui ogni dizionario include un numero in una chiave & Quot; sort_key & Quot ;.

#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;
 }];

Il codice in alto passa attraverso il lavoro per ottenere un valore intero per ogni chiave di ordinamento e confrontarli, come illustrazione di come farlo. Poiché <=> gli oggetti implementano un metodo di confronto, potrebbe essere riscritto molto più semplicemente:

 #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];
 }];

o il corpo del comparatore potrebbe persino essere distillato fino a 1 riga:

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

Tendo a preferire istruzioni semplici e molte variabili temporanee perché il codice è più facile da leggere e più facile da eseguire il debug. Il compilatore ottimizza comunque le variabili temporanee, quindi non vi è alcun vantaggio rispetto alla versione all-in-one-line.

Ho creato una piccola libreria di metodi di categoria, chiamata Linq to ObjectiveC , che rende questo tipo di cosa più facile. Usando il metodo sort con un selettore chiave, puoi ordinare per birthDate come segue:

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

Ho appena fatto l'ordinamento a più livelli in base a requisiti personalizzati.

// ordina i valori

    [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

Ho usato sortUsingFunction :: in alcuni dei miei progetti:

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];
}

Si utilizza NSSortDescriptor per ordinare un NSMutableArray con oggetti personalizzati

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

L'ordinamento NSMutableArray è molto semplice:

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];

Ordina usando NSComparator

Se vogliamo ordinare oggetti personalizzati, dobbiamo fornire NSComparator, che viene utilizzato per confrontare oggetti personalizzati. Il blocco restituisce un valore NSComparisonResult per indicare l'ordinamento dei due oggetti. Pertanto, al fine di ordinare l'intero array <=> viene utilizzato nel modo seguente.

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

Ordina tramite NSSortDescriptor
Supponiamo, ad esempio, che abbiamo un array contenente istanze di una classe personalizzata, Employee ha gli attributi firstname, lastname ed age. L'esempio seguente mostra come creare un NSSortDescriptor che può essere utilizzato per ordinare i contenuti dell'array in ordine crescente in base alla chiave di età.

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

Ordina utilizzando Confronti personalizzati
I nomi sono stringhe e quando ordinate le stringhe da presentare all'utente dovreste sempre usare un confronto localizzato. Spesso si desidera anche eseguire un confronto senza distinzione tra maiuscole e minuscole. Ecco un esempio con (localizedStandardCompare :) per ordinare l'array per cognome e nome.

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];

Per riferimento e discussione dettagliata fare riferimento a: 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/

I protocolli e la programmazione funzionale di Swift ti rendono molto semplice, devi solo rendere la tua classe conforme al protocollo comparabile, implementare i metodi richiesti dal protocollo e quindi utilizzare la funzione di ordine superiore ordinata (per:) per creare un array ordinato senza è necessario utilizzare array mutabili a proposito.

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)

Nel mio caso, utilizzo " SortArrayUsingComparator " per ordinare un array. Guarda il codice qui sotto.

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];
}];

Anche il mio oggetto è,

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

Ordina array in Swift


Per Swifty La persona sotto è una tecnica molto pulita per raggiungere l'obiettivo sopra per globalmente. Consente di avere una classe personalizzata di esempio User con alcuni attributi.

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

Ora abbiamo un array che dobbiamo ordinare in base a createdDate sia crescente che / o decrescente. Quindi, consente di aggiungere una funzione per il confronto delle date.

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
    }
}

Ora consente di avere extension di Array per [User]. In parole semplici, consente di aggiungere alcuni metodi solo per quegli array che contengono solo Array<User> oggetti.

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
    }
}

Utilizzo per ordine crescente

let sortedArray = someArray.sortUserByDate(.orderedAscending)

Utilizzo per ordine decrescente

let sortedArray = someArray.sortUserByDate(.orderedSame)

Utilizzo per lo stesso ordine

<*>
  

Il metodo sopra in <=> sarà accessibile solo se <=> è di tipo   <=> || <=>

Devi creare sortDescriptor e quindi puoi ordinare nsmutablearray usando sortDescriptor come di seguito.

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

Usa così per oggetti nidificati,

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

lastRoute è un oggetto e quell'oggetto contiene l'oggetto to, quello all'oggetto contiene i valori della stringa del cognome.

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(@"===============================");
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top