ios - Come si ordina un NSMutableArray con oggetti personalizzati al suo interno?




objective-c sorting (17)

Quello che voglio fare sembra piuttosto semplice, ma non riesco a trovare alcuna risposta sul web. NSMutableArray un NSMutableArray di oggetti e diciamo che sono oggetti "Person". Voglio ordinare NSMutableArray da Person.birthDate che è un NSDate .

Penso che abbia qualcosa a che fare con questo metodo:

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

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


Confronta il metodo

O implementa un metodo di confronto per il tuo oggetto:

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

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

NSSortDescriptor (migliore)

o di solito anche meglio:

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

È possibile ordinare facilmente più chiavi aggiungendo più di una all'array. È anche possibile utilizzare metodi di confronto personalizzati. Dai un'occhiata alla documentazione .

Blocchi (lucenti!)

C'è 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];
}];

Prestazione

Il -compare: e i metodi basati su blocchi saranno un po 'più veloci, in generale, rispetto all'utilizzo di NSSortDescriptor poiché quest'ultimo si basa su KVC. Il vantaggio principale del metodo NSSortDescriptor è che fornisce un modo per definire il tuo ordinamento usando i dati, piuttosto che il codice, il che rende facile, ad esempio, impostare le cose in modo che gli utenti possano ordinare un NSTableView facendo clic sulla riga dell'intestazione.


Ordina array in Swift

Per Swifty Person di seguito è una tecnica molto pulita per raggiungere l'obiettivo sopra per globalmente. Forniamo un esempio di classe personalizzata di User che ha alcuni attributi.

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

Ora abbiamo un array che dobbiamo ordinare sulla base di createdDate in ordine ascendente e / o discendente. Quindi aggiungiamo 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 abbiamo extension di Array for User . In parole semplici, aggiungiamo alcuni metodi solo per quelli di Array che contengono solo oggetti User in esso.

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 discendente

let sortedArray = someArray.sortUserByDate(.orderedAscending)

Utilizzo per lo stesso ordine

let sortedArray = someArray.sortUserByDate(.orderedSame)

Il metodo sopra extension sarà accessibile solo se l' Array è di tipo [User] || Array<User>


C'è un passo mancante nella seconda risposta di Georg Schölly , ma funziona bene allora.

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

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)

Ho appena effettuato l'ordinamento a più livelli in base ai 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 creato una piccola libreria di metodi di categoria, chiamata Linq to ObjectiveC , che rende questo tipo di cose più facile. Utilizzando il metodo di sort con un selettore di chiave, è possibile ordinare per birthDate di birthDate come segue:

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

Ho provato tutti, ma questo ha funzionato per me. In una classe ho un'altra classe chiamata " crimeScene ", e voglio ordinare per una proprietà di " crimeScene ".

Funziona come un fascino:

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

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

Nel mio caso, utilizzo "sortedArrayUsingComparator" 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

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

Puoi utilizzare il seguente metodo generico per il tuo 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 una serie di NSNumbers , puoi ordinarli con 1 chiamata:

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

Questo funziona perché gli oggetti nell'array (oggetti NSNumber ) implementano il metodo di confronto. Si potrebbe fare la stessa cosa per gli oggetti NSString o anche per una serie di oggetti dati personalizzati che implementano un metodo di confronto.

Ecco alcuni esempi di codice usando i blocchi di confronto. Ordina una serie di dizionari in cui ogni dizionario include un numero in una chiave "tasto_ordinamento".

#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 precedente passa attraverso il lavoro di ottenere un valore intero per ogni chiave di ordinamento e confrontandoli, come esempio di come farlo. Poiché NSNumber oggetti NSNumber implementano un metodo di confronto, potrebbero essere riscritti in modo molto più semplice:

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

oppure il corpo del comparatore potrebbe anche essere distillato fino a 1 linea:

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

Tendo a preferire affermazioni 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 c'è alcun vantaggio per la versione all-in-one-line.


Vedi il metodo NSMutableArray sortUsingFunction:context:

Dovrai impostare una funzione di confronto che prende due oggetti (di tipo Person , dal momento che stai confrontando due oggetti Person ) e un parametro di contesto .

I due oggetti sono solo esempi di Person . Il terzo oggetto è una stringa, ad es. @ "BirthDate".

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

Questo è uno pseudocodice approssimativo; dovrai precisare che cosa significa che una data deve essere "meno", "più" o "uguale" a un'altra data (come confrontare secondi, epoche 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 gli operatori ternari:

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

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


iOS 4 blocchi ti salverà :)

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


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

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(@"===============================");

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

Grazie, sta funzionando bene ...





nsmutablearray