0

I have an array of NSDate objects, and I want to know how many entries each month has, from now through five months back.

Is this possible to do using an NSPredicate?

4

2 回答 2

2

与马蒂亚斯的方法有些不同,尽管我没有看到任何好的方法来做到这一点NSPredicate,因为这里需要日期算术。

这最初从每个日期创建日期组件并计算使用NSCountedSet. (您可能可以在计数集上使用谓词。)然后创建另一个NSDateComponents对象来描述您感兴趣的月份和年份,并使用简单地查询该集合countForObject:

NSArray * dates = @[/* Your dates here */];

NSCalendar * cal = [NSCalendar currentCalendar];

/* Convert the dates into date components objects for month and year. */
NSMutableArray * months = [NSMutableArray array];
for( NSDate * date in dates ){

    NSDateComponents * month = [cal components:(NSMonthCalendarUnit|NSYearCalendarUnit)
                                      fromDate:date];
    [months addObject:month];
}

/* Count the unique components. */
NSCountedSet * monthsCounts = [NSCountedSet setWithArray:months];

/* Use a predicate here on the set? */

/* A components object for month arithmetic. */
NSDateComponents * monthDelta = [NSDateComponents new];

/* Get month names for the current locale. */
NSDateFormatter * formatter = [NSDateFormatter new];
NSArray * monthNames = [formatter monthSymbols];

for( NSInteger i = 0; i > -NUM_MONTHS; i-- ){

    /* Couting backwards from the current month, get a date components
     * object for each month and year.
     */
    [monthDelta setMonth:i];
    NSDateComponents * currMonth;
    currMonth = [cal components:(NSMonthCalendarUnit|NSYearCalendarUnit)
                       fromDate:[cal dateByAddingComponents:monthDelta
                                                     toDate:[NSDate date]
                                                    options:0]];
    /* Get the count from the counted set and display. */
    NSUInteger count = [monthsCounts countForObject:currMonth];
    NSLog(@"%lu dates in %@", count,
          [monthNames objectAtIndex:[currMonth month] - 1]);
}
于 2013-07-24T21:15:54.087 回答
1

您必须测试数组中的日期 6 次。

第一:匹配 7 月 1 日或之后且 8 月 1 日之前的所有日期。
第二:匹配 6 月 1 日或之后和 7 月 1 日之前的所有日期。
第三:匹配 5 月 1 日或之后和 6 月 1 日之前的所有日期。
等等

在一个循环中,您创建两个日期。第一个日期是月初的午夜。第二个日期正好是 1 个月(!)之后。然后从数组中过滤掉第一个日期或第二个日期之前或之后的所有日期。在循环的下一次迭代中,您使 startDate 比前一个 startDate 早一个月。

很多日期计算,但在 NSDateComponents 的帮助下,这没什么大不了的。

由于我不确定如何在数组中使用 NSPredicate 和 NSDates 我使用了另一种方法,但这基本上是它的工作原理:

NSDateComponents *oneMonthOffset = [[NSDateComponents alloc] init];
oneMonthOffset.month = 1;

NSDateComponents *thisMonth = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit fromDate:[NSDate date]];
thisMonth.day = 1;
NSDate *startOfCurrentMonth = [calendar dateFromComponents:thisMonth];

for (NSInteger offset = 0; offset > -5; offset--) {
    NSDateComponents *startDateOffset = [[NSDateComponents alloc] init];
    startDateOffset.month = offset;
    NSDate *startDate = [calendar dateByAddingComponents:startDateOffset toDate:startOfCurrentMonth options:0];
    NSDate *endDate = [calendar dateByAddingComponents:oneMonthOffset toDate:startDate options:0];
    NSIndexSet *matchingIndexes = [array indexesOfObjectsPassingTest:^BOOL(NSDate *date, NSUInteger idx, BOOL *stop) {
        if ([date compare:startDate] != NSOrderedAscending && [date compare:endDate] == NSOrderedAscending) {
            // date is not before startDate (i.e. is on startDate or later than startDate) and date is before endDate
            return YES;
        }
        return NO;
    }];
    NSLog(@"%d dates after %@ and before %@", [matchingIndexes count], [startDate descriptionWithLocale:[NSLocale currentLocale]], [endDate descriptionWithLocale:[NSLocale currentLocale]]);
}

如果您的日期数组非常大,您可能希望先对其进行排序,然后只扫描相关范围。此代码当前测试数组中的所有 NSDates。

于 2013-07-24T16:43:37.697 回答