我有点像NSSortDescriptor n00b。不过,我认为它是我需要做的正确工具:
我有一个由带有键的对象组成的NSArray,比如“name”和“time”。这是一个例子,而不是用语言表达:
input:
name: time
B: 4
C: 8
B: 5
C: 4
A: 3
C: 2
A: 1
A: 7
B: 6
desired output:
name: time
A: 1 <---
A: 3
A: 7
C: 2 <---
C: 4
C: 8
B: 4 <---
B: 5
B: 6
因此,值按“时间”排序,并按“名称”分组。 A首先是因为他的时间值最小,而A的所有值都是相继的。然后是C,他的所有价值都是第二小的时间价值。我已经指出了确定名称排序方式的值;在每个名称组中,按时间排序。
如何以最有效的方式从输入到输出NSArray? (cpu-和内存方面,不一定是代码方式。)我如何为此构造NSSortDescriptors,或使用任何其他方法?我不想自己动手,除非这是最有效的方式。
答案 0 :(得分:20)
我的解决方案是:
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"time" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, sortDescriptor2, nil];
你可以尝试一下
答案 1 :(得分:17)
sortedArrayUsingDescriptors:
NSArray
方法可以满足您的大部分需求:
第一个描述符指定用于对接收者内容进行排序的主键路径。任何后续描述符用于进一步细化具有重复值的对象的排序。有关其他信息,请参阅NSSortDescriptor。
还需要使用NSPredicate
进行一些过滤:
NSSortDescriptor *timeSD = [NSSortDescriptor sortDescriptorWithKey: @"time" ascending: YES];
NSMutableArray *sortedByTime = [UnsortedArray sortedArrayUsingDescriptors: timeSD];
NSMutableArray *sortedArray = [NSMutableArray arrayWithCapacity:[sortedByTime count]];
while([sortedByTime count])
{
id groupLead = [sortedByTime objectAtIndex:0];
NSPredicate *groupPredicate = [NSPredicate predicateWithFormat:@"name = %@", [groupLead name]];
NSArray *group = [sortedByTime filteredArrayUsingPredicate: groupPredicate];
[sortedArray addObjectsFromArray:group];
[sortedByTime removeObjectsInArray:group];
}
我不知道这是否是最有效的方法,但在您有理由相信它会导致问题之前,无需担心性能影响。这是不成熟的优化。我不会对这种方法的性能有任何担忧。你必须相信这个框架,否则你最终会因为毫无根据的偏执而重写它(从而破坏了框架的重点)。
答案 2 :(得分:3)
我会创建一个名为ItemGroup
的新类,然后在项目类中添加一个名为group
的额外ivar:
@interface ItemGroup : NSObject
{
NSNumber * time;
}
@property (nonatomic, copy) time;
@end
@interface ItemClass : NSobject
{
NSString * name;
NSNumber * time;
ItemGroup * group;
}
@property (nonatomic, copy) NSString * name;
@property (nonatomic, copy) NSNumber * time;
@property (nonatomic, assign) ItemClass * group; // note: must be assign
@end
然后,您可以执行以下操作:
NSMutableDictionary * groups = [NSMutableDictionary dictionaryWithCapacity:0];
for (ItemClass * item in sourceData)
{
ItemGroup * group = [groups objectForKey:item.name];
if (group == nil)
{
group = [[ItemGroup alloc] init];
[groups setObject:group forKey:item.name];
[group release];
group.time = item.time;
}
else if (item.time < group.time)
{
group.time = item.time;
}
item.group = group;
}
此代码循环遍历未排序的数组,跟踪每个组的最短时间,并设置每个项目的组。完成后,您只需对group.time
和time
:
NSSortDescriptor * groupSorter;
groupSort = [NSSortDescriptor sortDescriptorWithKey:@"group.time" ascending:YES];
NSSortDescriptor * timeSorter;
timeSort = [NSSortDescriptor sortDescriptorWithKey:@"time" ascending:YES];
NSArray * sortDescriptors = [NSArray arrayWithObjects:groupSort, timeSort, nil];
NSArray * sorted = [sourceData sortedArrayUsingDescriptors:sortDescriptors];
这应该可以解决问题!
更新 :请注意,如果您能够直接将这些组分配到门外,则可以获得更多更好的性能。像这样:
@interface ItemGroup : NSObject
{
NSString * name;
NSNumber * time;
}
@property (nonatomic, copy) NSString * name;
@property (nonatomic, copy) NSSNumber * time;
@end
@interface ItemClass : NSObject
{
ItemGroup * group;
NSNumber * time;
}
@property (nonatomic, retain) ItemGroup * group;
@property (nonatomic, copy) NSNumber * time;
@end
现在,如果你在某个地方维护一个组列表(如果需要的话,它们甚至可以放在某个数组中):
ItemGroup * group_A = [[ItemGroup alloc] init];
group_A.name = @"A";
ItemGroup * group_B = [[ItemGroup alloc] init];
group_B.name = @"B";
...
而不是设置数据项的名称,而是设置他们的组:
someItem.group = group_A;
someItem.time = GetSomeRandomTimeValue();
[sourceData addObject:someItem];
....
这将大大简化用于设置组时间的循环:
for (ItemClass * item in sourceData)
{
if (item.time < group.time) { group.time = item.time; }
}
而且,如果你真的想快速开始,你甚至可以修改time
属性的属性设置器来动态设置组时间:
@implementation ItemClass
- (void)setTime:(NSNumber *)newTime
{
if (newTime < group.time) { group.time = newTime; }
time = [newTime copy];
}
@end
请注意,在设置时间之前,您必须确保已设置group
。有了这个,你根本不需要那个排序循环。 sortDescriptors就足够了。
答案 3 :(得分:1)
我经历了一些代码(没有尝试运行它或者真的重复它,所以可能有一些错误,但它有一般的想法)来做你想要的。性能方面,如果您开始遇到大量数据,它可能不是最好的。我确信有更好的方法可以做到这一点,但我觉得这样做是作为“临时修复”答案的最基本方式。
NSMutableArray *copiedarray = [YourFirstArray mutableCopy];
NSMutableArray *sortedarray = [[NSMutableArray alloc] init];
NSMutableArray *tempgroup = nil;
NSSortDescriptor * groupSorter = [NSSortDescriptor sortDescriptorWithKey:@"time" ascending:YES];
NSInteger i;
NSInteger savedlowest = -1;
NSString *savedname = @"";
while ([copiedarray count] > 0) {
///reset lowest time and group
savedlowest = -1;
savedname = @"";
///grab the lowest time and group name
for (ii = 0;ii < [copiedarray count]; ii++) {
if (savedlowest==-1 || ((YourClass *)([copiedarray objectAtIndex:ii])).time<savedlowest)) {
savedname = ((YourClass *)([copiedarray objectAtIndex:ii])).name;
savedlowest = ((YourClass *)([copiedarray objectAtIndex:ii])).time;
}
}
//we have the lowest time and the type so we grab all those items from the group
tempgroup = [[NSMutableArray alloc] init];
for (ii = [copiedarray count]-1;ii > -1; ii--) {
if ([((YourClass *)([copiedarray objectAtIndex:ii])).name isEqualToString:savedname]) {
///the item matches the saved group so we'll add it to our temporary array
[tempgroup addObject:[copiedarray objectAtIndex:ii]];
///remove it from the main copied array for "better performance"
[copiedarray removeObjectAtIndex:ii];
}
}
[tempgroup sortUsingDescriptors:[NSArray arrayWithObject:groupSorter]];
[sortedarray addObjectsFromArray:tempgroup];
[tempgroup release];
tempgroup = nil;
}
最后,您最终会在sortedarray
找到您想要的内容。
答案 4 :(得分:1)
您可以使用NSSortDescriptor。这些描述符非常有用,因为它们允许您进行多个键排序以及单键排序。区分大小写和不敏感也很容易实现。我找到了一个详细的例子HERE
答案 5 :(得分:0)
如果你必须进行更复杂的排序,只需要“升序”就可以处理(比如将NSString排序为浮点数),你可能想要这样做:
NSDictionary *d = [self dictionaryFromURL:[NSURL URLWithString:urlStringValue]];
NSSortDescriptor *distanceSort = [[NSSortDescriptor alloc] initWithKey:@"distance" ascending:YES comparator:^(id left, id right) {
float v1 = [left floatValue];
float v2 = [right floatValue];
if (v1 < v2)
return NSOrderedAscending;
else if (v1 > v2)
return NSOrderedDescending;
else
return NSOrderedSame;
}];
NSSortDescriptor *nameSort = [NSSortDescriptor sortDescriptorWithKey:@"company_name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:distanceSort, nameSort, nil];
[distanceSort release];
NSArray *sortedObjects = [[d allValues] sortedArrayUsingDescriptors:sortDescriptors];
ILog();
return sortedObjects;