Сортувати NSArray з рядків або об'єктів дати


86

У мене є NSArrayрядок дат (тобто NSString), такий: "Четвер, 21 травня 09 19:10:09 -0700"

Мені потрібно відсортувати NSArrayдату. Я думав NSDateспершу перетворити рядок дати на об’єкт, але там застряг, як відсортувати за NSDateоб’єктом.

Дякую.


Перераховано, оскільки це пов’язано з основою Foundation, а не специфічно для iPhone.
Quinn Taylor

Відповіді:


110

Зберігайте дати як NSDateоб’єкти в масиві NS (Mutable), потім використовуйте -[NSArray sortedArrayUsingSelector:або -[NSMutableArray sortUsingSelector:]та передайте @selector(compare:)як параметр. -[NSDate compare:]Метод замовлення дати в порядку зростання для вас. Це простіше, ніж створення NSSortDescriptor, і набагато простіше, ніж написання власної функції порівняння. ( NSDateОб'єкти знають, як порівняти себе один з одним принаймні настільки ефективно, наскільки ми могли сподіватися на досягнення за допомогою спеціального коду.)


11
Незрозуміло, чи ставилося вихідне запитання про сортування масиву дат чи сортування масиву об’єктів, що мають поле дати. Це найпростіший підхід, якщо це перший, але якщо другий, NSSortDescriptor - це шлях.
smorgan

@smorgan як NSSortDescriptor обробляє нульові дати?
Еш

Дякуємо за вашу допомогу, я думаю, нам слід прочитати більше в Документах Apple, але це здається настільки нудним, поки ви ним не скористаєтесь.
Abo3atef

1
Не могли б ви показати це у фрагменті коду як приклад? Заздалегідь спасибі.
uplearnedu.com

115

Якщо у мене є NSMutableArrayоб'єкт із полем "beginDate" типу, NSDateя використовую наступне NSSortDescriptor:

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

3
Я знаю, що минуло багато часу (коли попередня відповідь була прийнята, NSSortDescriptor ще не могло бути), але для подальшого використання ця відповідь повинна бути правильною.
Vive

1
Це чудове рішення. вирішив мою проблему кількома рядками коду. Подякуйте тонну @vinzenzweber
Пратюша Терлі

1
Справді ... Я насправді був шокований, що це спрацювало під час першого запуску! Я думав, initWithKey зарезервований для словників, де визначено ключ, але все, що вам потрібно зробити, це посилатися на властивість у масиві об’єктів? Хай світить світло вінцензвеберскавицькі !!
whyoz

NSSortDescriptor існує з OS X 10.3, наприкінці 2003 року. Це трохи інший сценарій, коли дата - це поле в іншому об'єкті. За допомогою ще більш сучасного API ви також можете використовувати -[NSMutableArray sortUsingComparator:](10.6+) і передати блок, який повертає результат дзвінка -compare:в два поля. Можливо, це було б швидше, ніж виклик -valueForKey:кожного об’єкта. Ви навіть можете використовувати -sortWithOptions:usingComparator:і передавати параметри для сортування одночасно.
Quinn Taylor

як вищезгаданий NSSortDescriptor обробляє нульові дати? Чи потрібно нам говорити десекриптору, що з ними робити, якщо так, то як?
Ash

17

Ви також можете використовувати щось на зразок наступного:

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

Але це припускає, що дата зберігається як NSDateдоситьNString , що не повинно бути проблемою зробити / зробити. Бажано, я також рекомендую зберігати дані в необробленому форматі. Полегшує маніпулювання у подібних ситуаціях.


9

Ви можете використовувати блоки для сортування на місці:

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

Я пропоную вам перетворити всі ваші рядки на дати перед сортуванням, щоб не робити перетворення більше разів, ніж є елементи дати. Будь-який алгоритм сортування надасть вам більше перетворень на сьогоднішній день, ніж кількість елементів у масиві (іноді значно більше)

трохи більше про сортування блоків: http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html


Це варіант тієї самої неоптимальної ідеї, представленої @notoop. У будь-якому випадку перейти до NSDate - це, безумовно, шлях.
Quinn Taylor

В основному він просто поставив мою відповідь з відповіддю @ notoop ..... зовсім непотрібно подвійно розміщувати тих, якщо ви запитуєте мене ....
TheCodingArt

Це набагато повільніше, ніж аналіз рядків дати в масиві NSDate, а потім сортування масиву NSDate, тому що додатковий синтаксичний аналіз рядка буде в 2 * (n - 1) разів. Розбір рядків, як правило, важка робота для процесора.
CarlLee

5

У моєму випадку це спрацювало так:

    NSArray * aUnsorted = [dataToDb allKeys];
    NSArray * arrKeys = [aUnsorted sortedArrayUsingComparator: ^ NSComparisonResult (id obj1, id obj2) {
        NSDateFormatter * df = [[NSDateFormatter alloc] init];
        [df setDateFormat: @ "dd-MM-yyyy"];
        NSDate * d1 = [df dateFromString: (NSString *) obj1];
        NSDate * d2 = [df dateFromString: (NSString *) obj2];
        повернення [d1 порівняти: d2];
    }];

У мене був словник, де всі ключі містять дати у форматі dd-MM-yyyy. І allKeys повертає ключі словника несортованими, і я хотів представити дані в хронологічному порядку.


5

Можна використовувати sortedArrayUsingFunction:context:. Ось зразок:

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

Використовуючи a NSMutableArray, ви можете використовувати sortArrayUsingFunction:context:замість нього.


9
Це погана ідея - функція порівняння повинна бути швидкою і не повинна створювати об’єкти NSDate із рядків для кожного порівняння. Якщо ви вже використовуєте - [NSDate порівняння:], просто збережіть дати в масиві та використовуйте -sortedArrayUsingSelector: безпосередньо.
Quinn Taylor

Я думаю, що це хороше рішення, якщо ви сортуєте об’єкти, які вже мають поле NSDate. Немає?
GnarlyDog

1
Якщо масив уже містить NSDateоб’єкти, ви можете скористатися моєю відповіддю вище, або навіть використовувати -[NSMutableArray sortUsingComparator:], яка була додана в 10.6.
Quinn Taylor

Якщо ви використовуєте масив словника, в якому є об'єкт ключа-значення для дати як NSString, а також деякі інші об'єкти ключа-значення, тоді це рішення є найкращим на даний момент. Просто потрібно трохи змінити функцію dateSort. Пальці вгору! :)
Ерфан

4

Після того, як у вас є NSDate, ви можете створити NSSortDescriptorз, initWithKey:ascending:а потім використовувати sortedArrayUsingDescriptors:для сортування.


Цей підхід буде працювати, але використання -sortedArrayUsingSelector: трохи простіше і не вимагає створення додаткового об'єкта.
Quinn Taylor

Попередньо редагуючи, у запитанні згадувався "ключ" дати ", тому я припустив, що це справді масив об'єктів / словників, що мають дату як одне поле, а не масив необроблених дат. У такому випадку sortedArrayUsingSelector: є більш складним, оскільки для виконання пошуку потрібно написати спеціальну процедуру сортування.
smorgan

Так, вибачте за будь-яку плутанину - я очистив це посилання, оскільки він просто казав, що зберігає свій масив клавішею "дата", і зрозумів, що це просто зробило питання ще більш заплутаним. Ви безумовно праві в відносній складності, враховуючи більш складну структуру даних.
Quinn Taylor


1

Змініть це

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

До

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

Просто змініть КЛЮЧ: це має бути Dateзавжди

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.