Classificar NSArray de strings de data ou objetos

86

Eu tenho um NSArrayque contém strings de data (ou seja, NSString) como este: "Thu, 21 May 09 19:10:09 -0700"

Eu preciso classificar a NSArraydata. Pensei em converter a string de data em um NSDateobjeto primeiro, mas fiquei preso lá em como classificar pelo NSDateobjeto.

Obrigado.

postalservice14
fonte
Retagged porque está relacionado à estrutura Foundation e não é específico do iPhone.
Quinn Taylor

Respostas:

110

Armazene as datas como NSDateobjetos em um array NS (mutável), então use -[NSArray sortedArrayUsingSelector:ou -[NSMutableArray sortUsingSelector:]e passe @selector(compare:)como o parâmetro. O -[NSDate compare:]método ordenará as datas em ordem crescente para você. Isso é mais simples do que criar um NSSortDescriptore muito mais simples do que escrever sua própria função de comparação. (os NSDateobjetos sabem como se comparar uns aos outros pelo menos tão eficientemente quanto poderíamos alcançar com o código personalizado.)

Quinn Taylor
fonte
11
Não está claro se a pergunta original era sobre classificar uma matriz de datas ou classificar uma matriz de objetos que têm um campo de data. Esta é a abordagem mais fácil se for a primeira, mas se for a última, NSSortDescriptor é o caminho a percorrer.
smorgan
@smorgan como o NSSortDescriptor lida com datas nulas?
Ash
Obrigado pela ajuda, acho que deveríamos ler mais no Apple Docs, mas parece tão chato, até você usá-lo.
Abo3atef
1
Você poderia mostrar isso em um código de snippet como exemplo? Desde já, obrigado.
uplearnedu.com
115

Se eu tiver um NSMutableArrayde objetos com um campo "beginDate" do tipo NSDate, estou usando um NSSortDescriptorcomo abaixo:

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"beginDate" ascending:TRUE];
[myMutableArray sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
vinzenzweber
fonte
3
Eu sei que muito tempo se passou (quando a resposta anterior foi aceita, não poderia haver NSSortDescriptor ainda), mas para uso futuro esta deve ser a resposta adequada.
Vive
1
Esta é uma solução incrível. resolvi meu problema em algumas linhas de código. Obrigado a ton @vinzenzweber
Pratyusha Terli
1
Sério ... Fiquei realmente chocado com isso funcionou na primeira execução! Achei que initWithKey fosse reservado para dicionários onde a chave é definida, mas tudo que você precisa fazer é referenciar uma propriedade em uma matriz de objetos. Deixe a luz brilhar vinzenzweberskavitchski !!
whyoz
NSSortDescriptor existe desde o OS X 10.3, no final de 2003. Este é um cenário ligeiramente diferente, onde a data é um campo em outro objeto. Com uma API ainda mais moderna, você também pode usar -[NSMutableArray sortUsingComparator:](10.6+) e passar um bloco que retorna o resultado da chamada -compare:nos dois campos. Provavelmente seria mais rápido do que chamar -valueForKey:cada objeto. Você também pode usar -sortWithOptions:usingComparator:e passar opções para classificar simultaneamente.
Quinn Taylor
como o NSSortDescriptor acima lida com datas nulas? Precisamos dizer ao profanador o que fazer com eles em caso afirmativo, como?
Ash
17

Você também pode usar algo como o seguinte:

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

Mas isso pressupõe que a data seja armazenada como NSDateumNString , o que não deve ser problema de fazer / fazer. De preferência, recomendo também armazenar os dados em seu formato bruto. Facilita a manipulação em situações como essa.

TheCodingArt
fonte
9

Você pode usar blocos para classificar no local:

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

Eu sugiro que você converta todas as suas strings em datas antes de classificar para não fazer a conversão mais vezes do que os itens de data. Qualquer algoritmo de classificação fornecerá mais conversões de string para data do que o número de itens na matriz (às vezes, substancialmente mais)

um pouco mais sobre a classificação de blocos: http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html

Kostiantyn Sokolinskyi
fonte
Esta é uma variante da mesma ideia abaixo do ideal apresentada por @notoop. Em qualquer caso, converter primeiro para NSDate é definitivamente o caminho a percorrer.
Quinn Taylor
Ele basicamente colocou minha resposta com a de @notoop ... desnecessário postar duas vezes, se você me perguntar ...
TheCodingArt
Isso é muito mais lento do que analisar strings de data na matriz de NSDate e, em seguida, classificar a matriz de NSDate, pois haverá 2 * (n - 1) vezes a análise extra da string. Analisar strings geralmente é um trabalho pesado na CPU.
CarlLee,
5

O que funcionou no meu caso foi o seguinte:

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

Eu tinha um dicionário, onde todas as chaves eram datas no formato dd-MM-aaaa. E allKeys retorna as chaves do dicionário desordenadas, e eu queria apresentar os dados em ordem cronológica.

Javier Calatrava Llavería
fonte
5

Você pode usar sortedArrayUsingFunction:context:. Aqui está um exemplo:

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

Ao usar um NSMutableArray, você pode usar em seu sortArrayUsingFunction:context:lugar.

notnoop
fonte
9
Esta é uma má ideia - uma função de comparação deve ser rápida e não deve ter que criar objetos NSDate a partir de strings para cada comparação. Se você já estiver usando - [NSDate compare:], apenas armazene as datas na matriz e use -sortedArrayUsingSelector: diretamente.
Quinn Taylor
Eu acho que esta é uma boa solução se você estiver classificando objetos que já têm um campo NSDate. Não?
GnarlyDog
1
Se o array já contém NSDateobjetos, você pode usar minha resposta acima, ou mesmo usar -[NSMutableArray sortUsingComparator:], que foi adicionado em 10.6.
Quinn Taylor
Se você estiver usando uma matriz de dicionário em que há um objeto de valor-chave para data como NSString junto com alguns outros objetos de valor-chave, então esta solução é a melhor até agora. Só precisa de uma pequena modificação dentro da função dateSort. Afirmativo! :)
Erfan
4

Depois de ter um NSDate, você pode criar um NSSortDescriptorcom initWithKey:ascending:e usar sortedArrayUsingDescriptors:para fazer a classificação.

smorgan
fonte
Essa abordagem funcionará, mas usar -sortedArrayUsingSelector: é um pouco mais simples e não requer a criação de um objeto extra.
Quinn Taylor
Antes da edição, a questão mencionava "uma chave de 'data'", então presumi que fosse realmente uma matriz de objetos / dicionários que tinham uma data como um campo, não uma matriz de datas brutas. Nesse caso, SortArrayUsingSelector: é mais complexo, pois requer a escrita de uma rotina de classificação personalizada para fazer a pesquisa.
smorgan
Sim, desculpe por qualquer confusão - eu limpei essa referência, já que ele estava apenas dizendo que estava armazenando seu array com uma chave de 'data', e percebi que isso tornava a questão mais confusa. Você está definitivamente correto sobre a complexidade relativa, dada uma estrutura de dados mais complexa.
Quinn Taylor
2

Swift 3.0

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

Mude isso

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

Para

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

Basta mudar a CHAVE: deve ser Datesempre

user2339385
fonte