【问题标题】:NSSortDescriptor - Sort descriptor based on another arrayNSSortDescriptor - 基于另一个数组的排序描述符
【发布时间】:2013-05-07 04:56:27
【问题描述】:

我有一个核心数据应用程序。我想获取一种对象UserUser 拥有userId 的属性。

我有另一个数组,userIds,[1, 4, 3, 5]。我想创建一个NSSortDescriptor,它根据数组中userIds 的顺序对我的Userobjects 进行排序。

这可能吗,我应该怎么做?

更新

我现在尝试了以下方法。

  1. 我向我的 User 对象添加了一个可转换的属性,我在其中存储了用户 ids 数组。

  2. 我尝试了以下排序描述符:

    sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"userId" ascending:YES     comparator:^NSComparisonResult(id obj1, id obj2) {
        NSUInteger idx1 = [self.user.followingIds indexOfObject:[obj1 valueForKey:@"userId"]];
        NSUInteger idx2 = [self.user.followingIds indexOfObject:[obj2 valueForKey:@"userId"]];
        return idx1 - idx2;
    }];
    

我收到以下错误:

Serious application error.  Exception was caught during Core Data change processing.  This  is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.   [<__NSCFNumber 0xa337400> valueForUndefinedKey:]: this class is not key value coding-compliant  for the key userId. with userInfo {
    NSTargetObjectUserInfoKey = 2502;
    NSUnknownUserInfoKey = userId;
}
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason:    '[<__NSCFNumber 0xa337400> valueForUndefinedKey:]: this class is not key value coding-   compliant for the key userId.'

更新 2

还想在User 对象之间添加关系Followers。任何想法应该如何看待这种关系?见附图。那是对的吗?

【问题讨论】:

  • 您是否将 userId 存储在您的数据库中。如果这样,请使用 amit answer 来完成它

标签: ios objective-c cocoa-touch core-data nssortdescriptor


【解决方案1】:

正如@MartinR 所说,您不能使用排序描述符来做到这一点。
要实现 O(N) 复杂度的可扩展解决方案(如果您真的需要):

//Not tested
NSArray* permutation = ...;//@[@1,@4,@3,@5] (user ids);
NSMutableDictionary* map = [[NSMutableDictionary alloc] initWithCapacity:[permutation count]];
for (NSUInteger i =0; i< [permutation count]; ++i) {
    map[permutation[i]] = @(i);
}
NSMutableArray* results = ...;//A mutable copy of the results
for (NSUInteger i = 0; i < [permutation count];) {
    id userId = [results[i] valueForKey:@"userId"];
    NSUInteger key = [map[userId] unsignedIntegerValue];//where we like this object to be
    if (key != i) {
        [results exchangeObjectAtIndex:i withObjectAtIndex:key];
    } else {
        ++i;
    }
}

分析:(如有错误请指正)

映射阶段需要N次操作完成:O(N)
数组副本需要 N 次操作:O(N)
循环迭代 N 次,但是:
它最多执行 N 次交换
并且最多进行 N 次比较
因此:O(N) + O(N) + O(N) + O(N) = 4*O(N) = O(N)

@MartinR 解决方案将基于限制为最小 O(N*lg(N)) 操作的基于比较的排序。
但是每次比较都需要 O(N) 来获得第一个索引,而 O(N) 来获得第二个索引。
因此: O(N*lg(N))*(O(N) + O(N)) = 2*O(N)*O(N*lg(N)) = O((N^2)*lg (N))

对于足够小的 N 没有真正的区别(N=10 ==> 我的解决方案大约 40 次操作,Martin 的解决方案大约 200 次操作)。
对于大 N,Martin 的解决方案无法很好地扩展:
N == 1000 ==> 我的解决方案有 ~4000 次操作和 ~2*1000*1000*8 =~ 16*10^6

【讨论】:

  • while (key != i) { ... } 循环在我看来很可疑,因为循环体中既没有修改 key 也没有修改 i
  • 现在应该按预期进行
  • 谢谢,这个解决方案的性能比@MartinR 的更好吗?以及如何/为什么会这样?
  • 现在看起来不错!你当然是对的,我的解决方案不能很好地扩展。另一个 O(N) 解决方案是创建从 userId 到相应对象 (NSDictionary *map = [[NSDictionary alloc] initWithObjects:results forKeys:[results valueForKey:@"userId"]]) 的映射 - 然后您可以使用简单的循环构建排序数组。 - 但在我看来,存储和排序 userId 而不是正确(有序)的关系可能不是最好的解决方案。
【解决方案2】:

这不能使用排序描述符来完成,您必须在获取结果后应用自定义比较器函数:

NSArray *userIds = ...; // e.g. @[@1, @4, @3, @5]
NSArray *results = ...; // result of fetch request
NSArray *sorted = [results sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    NSUInteger idx1 = [userIds indexOfObject:[obj1 valueForKey:@"userId"]];
    NSUInteger idx2 = [userIds indexOfObject:[obj2 valueForKey:@"userId"]];
    return idx1 - idx2;
}];

【讨论】:

  • 谢谢,我已经尝试过您的解决方案,但无法完全正常工作。请参阅我的更新答案。有什么想法吗?
  • @Anders:你在获取请求中使用了排序描述符吗?你不能这样做! 不可能在提取请求中使用这样的排序描述符。您必须先获取结果,然后按照我的回答中所述对它们进行排序。 - 另外我不知道你为什么使用可转换属性,这会使事情变得更复杂。
  • @MartinR 好的,您知道是否可以在 NSFechResultController 获取中“重新排序”结果?我在 tableview 中使用 NSFechResultController 作为我的数据源...
  • @Anders:不,您不能重新排序 FRC 的“fetchedObjects”。 - 你理论上可以做的是在cellForRowAtIndexPath 中的表视图行和 FRC 行之间“映射”。但是FRC委托方法didChangeObject:...也必须修改,所以这会很复杂(这只是一个想法!)。 - 如果您确实需要使用内置排序描述符无法完成的自定义排序顺序,那么最好删除 FRC。只需将结果提取到数组中并对其进行排序。如果有任何变化,请重新加载表格视图。
  • @MartinR 谢谢,将尝试数组路由。
【解决方案3】:

如果将消息 ID 添加到数据库中,请使用我用来对消息 ID 进行排序的想法

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"message_id" ascending:NO selector:@selector(localizedStandardCompare:)];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    [request setSortDescriptors:sortDescriptors];
    request.returnsObjectsAsFaults=NO;
 NSError *error = nil;
    NSArray *results = [managedObjectContext executeFetchRequest:request error:&error];

【讨论】:

  • 谢谢,您的localizedStandardCompare 方法看起来如何?
  • 它会相互比较字符串。它解决了你的问题吗
  • 在头文件 NSString.h 中定义。当文件名或其他字符串出现在适合 Finder 式排序的列表和表格中时,它看起来像 /*localizedStandardCompare:,在 10.6 中添加。此方法的确切行为可能会在未来的版本中进行调整,并且在不同的本地化下会有所不同,因此客户端不应依赖于字符串的确切排序顺序。 */ - (NSComparisonResult)localizedStandardCompare:(NSString *)string NS_AVAILABLE(10_6, 4_0);
  • 简单的解决方案是,当您将 userId 插入 DB 时,请根据 userIdArray 插入它,然后当您检索它时,它会自动出现,因为您已存储排序。
猜你喜欢
  • 1970-01-01
  • 2014-10-25
  • 2016-05-15
  • 1970-01-01
  • 2013-06-24
  • 1970-01-01
  • 2021-07-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多