iOS: сортировка массива NSDate по последнему году без учета года

У меня есть массив объектов NSDate. Эти объекты имеют годовую стоимость, но мне все равно. Я хочу отсортировать даты по самым последним, используя только месяц и день. Так, в июле, например, июньская дата будет около верхней части списка, а августовская дата будет около нижней, независимо от годов, содержащихся в этих объектах NSDate.

У меня проблемы с представлением способа сделать это. У кого-нибудь есть элегантное решение этой проблемы?

Спасибо!

РЕДАКТИРОВАТЬ: Очевидно, я не очень четко объяснил, что я ищу. Я хочу отсортировать массив так, чтобы самая последняя дата была первой, игнорируя год. Так, например, учитывая формат MM/dd/yy, если сегодня 06.05.14, и мой массив содержит эти даты:

01/04/14 04/05/14 21/07/14 30.12.14

Правильный порядок я хочу это:

04/05 01/04 30/30 07/21

3 ответа

Решение

Как указано здесь, вы можете получить компоненты даты NSDate и отсортировать их по этому значению.

NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];

NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];

Вы можете игнорировать любые значения, которые вы хотели бы.

РЕДАКТИРОВАТЬ: Просто объединяя два ответа. Кредит Драйс за код сортировки.

NSArray* sortedArray = [yourArray sortedArrayUsingComparator:^NSComparisonResult(NSDate* lhs, NSDate* rhs)
{
    NSDateComponents *lscomponents = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth fromDate:lhs];
    NSDateComponents *rhcomponents = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth fromDate:rhs];
    NSDateComponents *currentcomponents = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth fromDate:[NSDate date]];

    NSInteger lday = [lscomponents day];
    NSInteger lmonth = [lscomponents month];
    NSInteger rday = [rhcomponents day];
    NSInteger rmonth = [rhcomponents month];

    if(lmonth < [currentcomponents month])
        lmonth += 12;
    else if(lmonth == [currentcomponents month])
    {
        if(lday < [currentcomponents day])
            lmonth += 12;
    }

    if(rmonth < [currentcomponents month])
        rmonth += 12;
    else if(rmonth == [currentcomponents month])
    {
        if(rday < [currentcomponents day])
            rmonth += 12;
    }
    int diff = lmonth - rmonth;
    if (diff == 0)
        diff = lday - rday;

    if (diff < 0)
        return NSOrderedDescending;

    if (diff > 0)
        return NSOrderedAscending;

    return NSOrderedSame;    
}];

Вы можете сделать это намного быстрее, кэшируя календарь.

EDIT2:

Я обновил сортировку, чтобы учесть ваши индивидуальные потребности. Если месяц и день раньше, вы добавляете 12 к месяцу. Затем сравните на основе нового псевдомесяца. Это должно дать вам обычай сортировки сегодня.

Вы можете написать собственную функцию сравнения, которая сортирует по месяцам и дням. использование sortedArrayUsingComparator, например. Псевдокод (записанный из памяти, не скомпилированный и не опробованный):

NSArray* sortedArray = [yourArray sortedArrayUsingComparator:^NSComparisonResult(NSDate* lhs, NSDate* rhs)
{
    // Use NSDateComponents to get the month and day from each date.

    int diff = lhs.month - rhs.month;
    if (diff == 0)
        diff = lhs.day - rhs.day;

    if (diff < 0)
        return NSOrderedDescending;

    if (diff > 0)
        return NSOrderedAscending;

    return NSOrderedSame;    
}];

редактировать

Пример кода неполон, потому что NSdate не имеет дневных или месячных методов. Вам нужно сначала пройти через несколько обручей, чтобы получить компоненты, см. NSDate get year / month / day. Извините, сейчас нет на Mac, поэтому я не могу попробовать.

NSDate* today = [NSDate date];
NSDate* left = <left side value>;
NSDate* right = <right side value>;

NSDateFormatter* dateFormatter = [NSDateFormatter new];

// Set time zone as desired

/* Skip this
** Get an NSDate for Dec 31 this year, so we can calc it's day-of-year.  But note that it would probably suffice to just use the literal value 366 all the time.
[dateFormatter setDateFormat:@"yyyy"];
NSString* yearString = [dateFormatter stringFromDate:today];
NSString* dec31Dummy = [yearString stringByAppendingString:@"/12/31"];

[dateFormatter setDateFormat:@"yyyy/MM/dd"];
NSDate* dec31 = [dateFormatter dateFromString:dec31Dummy];
*/

// Date format for "day-of-year"
[dateFormatter setDateFormat:@"D"];

// Note that these are retrieving "day-of-year" values
NSString* todayString = [dateFormatter stringFromDate:today];
NSString* leftString = [dateFormatter stringFromDate:left];
NSString* rightString = [dateFormatter stringFromDate:right];
// NSString* dec31String = [dateFormatter stringFromDate:dec31];

// These convert the day-of-year values to numeric
int todayNum = [todayString intValue];
int leftNum = [leftString intValue];
int rightNum = [rightString intValue];
// int dec31Num = [dec31String intValue]; 
int dec31Num = 366;

// Adjust for dates that have already passed this year -- push them out into next year
If (leftNum < todayNum) leftNum += dec31Num;
if (rightNum < todayNum) rightNum += dec31Num;

int diff = leftNum - rightNum;
if (diff < 0) { 
    return NSOrderedDescending;
}
else if (diff > 0) {
    return NSOrderedAscending;
}
else {
    return NSOrderedSame;
}

Конечно, хотелось бы переместить инвариантную логику настройки из собственно компаратора.

Другие вопросы по тегам