Sortuj NSArray ciągów dat lub obiektów

86

Mam NSArrayciąg zawierający daty (np. NSString) w następujący sposób: „Thu, 21 May 09 19:10:09 -0700”

Muszę posortować NSArraywedług daty. NSDateNajpierw pomyślałem o przekonwertowaniu ciągu daty na obiekt, ale utknąłem na tym, jak sortować według NSDateobiektu.

Dzięki.

poczta 14
źródło
Zmieniono tag, ponieważ jest to związane ze strukturą Foundation, a nie specyficzne dla iPhone'a.
Quinn Taylor

Odpowiedzi:

110

Przechowuj daty jako NSDateobiekty w tablicy NS (Mutable), a następnie użyj -[NSArray sortedArrayUsingSelector:lub -[NSMutableArray sortUsingSelector:]i przekaż @selector(compare:)jako parametr. Ta -[NSDate compare:]metoda uporządkuje daty w kolejności rosnącej. Jest to prostsze niż tworzenie NSSortDescriptori znacznie prostsze niż pisanie własnej funkcji porównania. ( NSDateobiekty potrafią porównywać się ze sobą co najmniej tak skutecznie, jak moglibyśmy mieć nadzieję, że uda się to osiągnąć za pomocą niestandardowego kodu).

Quinn Taylor
źródło
11
Nie jest jasne, czy pierwotne pytanie dotyczyło sortowania tablicy dat, czy sortowania tablicy obiektów, które mają pole daty. Jest to najłatwiejsze podejście, jeśli jest to pierwsze, ale jeśli jest to drugie, NSSortDescriptor jest najlepszym rozwiązaniem.
smorgan
@smorgan jak NSSortDescriptor obsługuje daty zerowe?
Ash,
Dziękuję za pomoc, myślę, że powinniśmy czytać więcej w Apple Docs, ale wydaje się to nudne, dopóki go nie użyjesz.
Abo3atef
1
Czy możesz to pokazać we fragmencie kodu jako przykład? Z góry dziękuję.
uplearnedu.com
115

Jeśli mam NSMutableArrayobiekty z polem „beginDate” typu NSDate, używam NSSortDescriptorponiższego:

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"beginDate" ascending:TRUE];
[myMutableArray sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
vinzenzweber
źródło
3
Wiem, że minęło dużo czasu (kiedy poprzednia odpowiedź została zaakceptowana, nie mogło być jeszcze NSSortDescriptor), ale do przyszłego użytku ta powinna być właściwą odpowiedzią.
Vive
1
To świetne rozwiązanie. rozwiązałem mój problem w kilku linijkach kodu.Thanx a ton @vinzenzweber
Pratyusha Terli
1
Naprawdę ... Byłem zszokowany, że zadziałało przy pierwszym uruchomieniu! Myślałem, że initWithKey jest zarezerwowane dla słowników, w których klucz jest zdefiniowany, ale wszystko, co musisz zrobić, to odwołać się do właściwości w tablicy obiektów? Niech świeci światło Vinzenzweberskavitchski !!
whyoz
NSSortDescriptor istnieje od OS X 10.3, pod koniec 2003 roku. To jest nieco inny scenariusz, w którym data jest polem w innym obiekcie. Dzięki jeszcze nowocześniejszemu interfejsowi API równie dobrze możesz użyć -[NSMutableArray sortUsingComparator:](10.6+) i przekazać blok, który zwraca wynik wywołania -compare:dwóch pól. Prawdopodobnie byłoby to szybsze niż wywołanie -valueForKey:każdego obiektu. Możesz nawet używać -sortWithOptions:usingComparator:i przekazywać opcje do sortowania jednocześnie.
Quinn Taylor
jak powyższy NSSortDescriptor obsługuje daty zerowe? Czy musimy mówić profanatorowi, co z nimi zrobić, jeśli tak, to jak?
Ash,
17

Możesz także użyć czegoś podobnego do następującego:

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

Ale to zakłada, że ​​data jest przechowywana jako NSDateraczej aNString , co nie powinno stanowić problemu. Zalecam również przechowywanie danych w formacie surowym. Ułatwia manipulowanie w takich sytuacjach.

TheCodingArt
źródło
9

Możesz użyć bloków do sortowania w miejscu:

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

Proponuję przekonwertować wszystkie ciągi na daty przed sortowaniem, aby nie wykonywać konwersji więcej razy niż jest to w przypadku elementów daty. Dowolny algorytm sortowania zapewni więcej konwersji ciągów do daty niż liczba elementów w tablicy (czasami znacznie więcej)

trochę więcej o sortowaniu bloków: http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html

Kostiantyn Sokolinskyi
źródło
Jest to wariant tego samego suboptymalnego pomysłu przedstawionego przez @notoop. W każdym razie przejście na NSDate jest zdecydowanie najlepszym rozwiązaniem.
Quinn Taylor
Po prostu umieścił moją odpowiedź z odpowiedzią @ notoop ..... całkiem niepotrzebne jest podwójne publikowanie tych, jeśli mnie o to poprosisz ....
TheCodingArt
Jest to znacznie wolniejsze niż parsowanie ciągów dat do tablicy NSDate, a następnie sortowanie tablicy NSDate, ponieważ będzie 2 * (n - 1) razy dodatkowe parsowanie ciągu. Przetwarzanie ciągów jest zwykle ciężkim zadaniem dla procesora.
CarlLee
5

W moim przypadku zadziałało:

    NSArray * aUnsorted = [dataToDb allKeys];
    NSArray * arrKeys = [aUnsorted sortArrayUsingComparator: ^ NSComparisonResult (id obj1, id obj2) {
        NSDateFormatter * df = [[NSDateFormatter assign] init];
        [df setDateFormat: @ "dd-MM-yyyy"];
        NSDate * d1 = [df dateFromString: (NSString *) obj1];
        NSDate * d2 = [df dateFromString: (NSString *) obj2];
        return [d1 porównaj: d2];
    }];

Miałem słownik, w którym wszystkie klucze miały daty w formacie dd-MM-yyyy. AllKeys zwraca klucze słownika nieposortowane, a ja chciałem przedstawić dane w porządku chronologicznym.

Javier Calatrava Llavería
źródło
5

Możesz użyć sortedArrayUsingFunction:context:. Oto próbka:

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

Używając a NSMutableArray, możesz użyć sortArrayUsingFunction:context:zamiast tego.

notnoop
źródło
9
To zły pomysł - funkcja porównania powinna być szybka i nie powinna tworzyć obiektów NSDate z ciągów dla każdego porównania. Jeśli już używasz - [porównanie NSDate:], po prostu zapisz daty w tablicy i użyj -sortedArrayUsingSelector: bezpośrednio.
Quinn Taylor
Myślę, że jest to dobre rozwiązanie, jeśli sortujesz obiekty, które mają już pole NSDate. Nie?
GnarlyDog
1
Jeśli tablica zawiera już NSDateobiekty, możesz skorzystać z mojej odpowiedzi powyżej, a nawet użyć -[NSMutableArray sortUsingComparator:], która została dodana w 10.6.
Quinn Taylor
Jeśli używasz tablicy słownika, w której znajduje się obiekt klucz-wartość dla daty jako NSString wraz z kilkoma innymi obiektami klucz-wartość, to rozwiązanie to jest jak dotąd najlepsze. Wystarczy trochę zmodyfikować funkcję dateSort. Kciuki w górę! :)
Erfan
4

Gdy już masz NSDate, możesz utworzyć NSSortDescriptorz, initWithKey:ascending:a następnie użyć sortedArrayUsingDescriptors:do sortowania.

smorgan
źródło
To podejście będzie działać, ale użycie -sortedArrayUsingSelector: jest nieco prostsze i nie wymaga tworzenia dodatkowego obiektu.
Quinn Taylor
Przed edycją pytanie dotyczyło „klucza„ daty ””, więc założyłem, że jest to naprawdę tablica obiektów / słowników, które mają datę jako jedno pole, a nie tablicę nieprzetworzonych dat. W takim przypadku sortArrayUsingSelector: jest bardziej złożona, ponieważ wymaga napisania niestandardowej procedury sortowania, aby przeprowadzić wyszukiwanie.
smorgan
Tak, przepraszam za zamieszanie - wyczyściłem to odniesienie, ponieważ właśnie mówił, że przechowuje swoją tablicę z kluczem „data”, i zdałem sobie sprawę, że to tylko sprawiło, że pytanie było bardziej zagmatwane. Zdecydowanie masz rację co do względnej złożoności, biorąc pod uwagę bardziej złożoną strukturę danych.
Quinn Taylor,
2

Swift 3.0

myMutableArray = myMutableArray.sorted(by: { $0.date.compare($1.date) == ComparisonResult.orderedAscending })
Abhishek Jain
źródło
1

Zmień to

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

Do

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

Po prostu zmień KLUCZ: musi być Datezawsze

user2339385
źródło