Pergunta

O que eu quero fazer parece bastante simples, mas não consigo encontrar quaisquer respostas na web. Eu tenho um NSMutableArray de objetos, e vamos dizer que eles são objetos 'pessoa'. Eu quero classificar a NSMutableArray por Person.birthDate que é um NSDate.

Eu acho que tem algo a ver com esse método:

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

Em Java eu ??faria meu objeto implementar Comparable, ou usar Collections.sort com um comparador personalizado em linha ... como na terra você fazer isso em Objective-C?

Foi útil?

Solução

método Compare

Ou você implementar um método de comparar para o objeto:

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

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

NSSortDescriptor (melhor)

ou geralmente ainda melhor:

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

Você pode facilmente classificar por várias chaves, adicionando mais de uma para a matriz. Usando personalizados comparadores-métodos também é possível. Ter um olhar para a documentação .

Blocks (brilhante!)

Há também a possibilidade de classificação com um bloco desde 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];
}];

Desempenho

O -compare: e métodos baseados em blocos será um pouco mais rápido, em geral, do que usar NSSortDescriptor como o último se baseia em KVC. A principal vantagem do método NSSortDescriptor é que ele fornece uma forma de definir a sua ordem de classificação usando dados, em vez de código, o que torna fácil de exemplo definir as coisas para que os usuários podem classificar uma NSTableView, clicando sobre a linha de cabeçalho.

Outras dicas

Veja o método NSMutableArray sortUsingFunction:context:

Você precisará configurar um comparar função que toma dois objetos (do tipo Person, desde que você está comparando dois objetos Person) e um contexto parâmetro.

Os dois objetos são apenas instâncias de Person. O terceiro objecto é uma cadeia de, por exemplo, @ "BirthDate".

Esta função retorna um NSComparisonResult: Ele retorna NSOrderedAscending se PersonA.birthDate <PersonB.birthDate. Ele irá retornar NSOrderedDescending se PersonA.birthDate> PersonB.birthDate. Finalmente, ele irá retornar NSOrderedSame se PersonA.birthDate == PersonB.birthDate.

Esta é pseudocódigo áspera; você terá de carne para fora o que significa para uma data a ser "menos", "mais" ou "igual" para outra data (como comparar segundos-desde-época 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;
}

Se você quiser algo mais compacto, você pode usar operadores ternários:

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

Inlining talvez pudesse acelerar o processo um pouco, se você fizer isso muito.

Eu fiz isso no iOS 4, usando um bloco. Teve de lançar os elementos da minha matriz de id para o meu tipo de classe. Neste caso, foi uma classe chamada Score com uma propriedade chamada pontos.

Além disso, você precisa decidir o que fazer se os elementos de sua matriz não são o tipo certo, para este exemplo Acabei de voltar NSOrderedSame, porém no meu código I embora uma exceção.

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:. Esta é a classificação em ordem decrescente

A partir de iOS 4, você também pode usar blocos para a classificação.

Para este exemplo particular, eu estou supondo que os objetos em seu conjunto tem um método 'posição', que retorna uma 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: o "ordenado" array será autoreleased

.

Eu tentei de tudo, mas isso funcionou para mim. Em uma classe Eu tenho uma outra classe chamada "crimeScene", e quer ordenar por uma propriedade de "crimeScene".

Isso funciona como um encanto:

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

Existe um passo que falta em o Georg Schölly segunda resposta , mas ele funciona bem então.

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

Graças, está funcionando bem ...

Seus objetos Person precisa implementar um método, compare: digamos que leva outro objeto Person e NSComparisonResult retorno de acordo com a relação entre os 2 objetos.

Em seguida, você chamaria sortedArrayUsingSelector: com @selector(compare:) e isso deve ser feito.

Existem outras maneiras, mas, tanto quanto eu sei que não há Cacau-equiv da interface Comparable. Usando sortedArrayUsingSelector: é provavelmente a forma mais indolor de fazê-lo.

iOS 4 blocos você vai economizar:)

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 um pouco de descrição

Para NSMutableArray, use o método sortUsingSelector. Ele classifica-o-lugar, sem criar uma nova instância.

Você pode usar o seguinte método genérico para a sua finalidade. Ele deve resolver o seu 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 você está apenas classificar um conjunto de NSNumbers, você pode classificá-los com uma chamada:

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

Isso funciona porque os objetos na matriz (objetos NSNumber) implementar o método de comparar. Você poderia fazer a mesma coisa para objetos NSString, ou mesmo para uma matriz de objetos de dados personalizados que implementar um método de comparar.

Aqui está um código de exemplo usando blocos de comparação. Ele classifica uma série de dicionários onde cada dicionário inclui um número em um "sort_key" chave.

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

O código acima atravessa o trabalho de obter um valor inteiro para cada chave de classificação e compará-los, como uma ilustração de como fazê-lo. Desde objetos NSNumber implementar um método de comparar, poderia ser reescrita muito mais simples:

 #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 o corpo do comparador poderia mesmo ser destilada para baixo para uma linha:

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

I tendem a preferir declarações simples e um monte de variáveis ??temporárias porque o código é mais fácil de ler e mais fácil de depurar. O compilador otimiza afastado as variáveis ??temporárias de qualquer maneira, então não há nenhuma vantagem para a versão all-in-one-line.

Eu criei uma pequena biblioteca de métodos categoria, chamada Linq para ObjectiveC , que faz com que este tipo de coisa mais fácil. Usando o método de classificação com um seletor de chave, você pode escolher por birthDate da seguinte forma:

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

Eu usei sortUsingFunction :: em alguns dos meus projetos:

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

Você usa NSSortDescriptor para classificar um NSMutableArray com objetos personalizados

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

Classificando NSMutableArray é muito simples:

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

Ordenar usando NSComparator

Se quisermos classificar objetos personalizados precisamos fornecer NSComparator, que é usado para comparar objetos personalizados. O bloco retorna um valor NSComparisonResult para denotar a ordenação dos dois objetos. Portanto, a fim de classificar toda gama NSComparator é usado na seguinte forma.

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

Classifica Usando NSSortDescriptor
Vamos supor, por exemplo, que temos um array contendo instâncias de uma classe personalizada, empregado tem atributos nome, sobrenome e idade. O exemplo a seguir ilustra como criar um NSSortDescriptor que pode ser usado para classificar o conteúdo do array em ordem crescente pela chave de idade.

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

Ordenar usando comparações feitas sob encomenda
Os nomes são cordas, e quando você classificar cordas para apresentar ao usuário que você deve sempre usar uma comparação localizada. Muitas vezes você também quiser realizar uma comparação insensível caso. Aí vem um exemplo com (localizedStandardCompare :) para encomendar a matriz por nome e sobrenome.

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

Para referência e discussão detalhada por favor consulte: 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/

protocolos de Swift e marcas de programação funcional que muito fácil você só tem que fazer a sua classe em conformidade com o protocolo comparável, implementar os métodos exigidos pelo protocolo e, em seguida, usam o ordenado (por:) função de alta ordem para criar uma matriz de classificação sem necessidade de usar matrizes mutáveis ??pelo caminho.

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)

No meu caso, eu uso "sortedArrayUsingComparator" para classificar um array. Olhada no código abaixo.

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

Também meu objeto é,

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

Classificar disposição em Swift


Para Swifty Pessoa abaixo é uma técnica muito limpo para alcançar acima da meta para todo o mundo. Vamos ter uma classe de exemplo costume de User que tem alguns atributos.

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

Agora temos um array que precisamos para classificar com base createdDate quer ascendente e / ou descendente. Então, vamos adicionar uma função de comparação de data.

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

Agora vamos ter uma extension de Array para User. Em palavras simples permite adicionar alguns métodos apenas para aquelas de matriz que apenas têm objetos User nele.

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

Uso de Ordem Crescente

let sortedArray = someArray.sortUserByDate(.orderedAscending)

Uso de Ordem Descendente

let sortedArray = someArray.sortUserByDate(.orderedAscending)

Uso de mesma ordem

let sortedArray = someArray.sortUserByDate(.orderedSame)

Acima método em extension só será acessível se o Array é do tipo [User] || Array<User>

Você tem que criar sortDescriptor e então você pode classificar a NSMutableArray usando sortDescriptor como abaixo.

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

Use como este para objetos aninhados,

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

lastRoute é um objeto e esse objeto contém o objeto para que a objeto segurar os valores de cadeia Sobrenome.

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(@"===============================");
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top