Я - что-то вроде NSSortDescriptor n00b. Я думаю, тем не менее, что это - правильный инструмент для того, что я должен сделать:
У меня есть NSArray, состоящий из объектов с ключами, скажем, "называют" и "время". Вместо того, чтобы выразить словами его, вот пример:
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
Таким образом, значения отсортированы по "времени" и сгруппированы "именем". Прибытие сначала, потому что у него были самая маленькая временная стоимость и все значения для прибывшего друг после друга. Тогда прибывает C, у него была вторая самая маленькая временная стоимость из всех его значений. Я указал на значения, которые определяют, как имена отсортированы; в каждой группе имени сортировка ко времени.
Как я добираюсь от входа для вывода NSArray самым эффективным способом? (CPU - и мудрый памятью, не обязательно мудрый кодом.), Как я создал бы NSSortDescriptors для этого или использовал бы какой-либо другой метод? Я не хочу прокручивать свое собственное, если это не самый эффективный путь.
Метод 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];
}
Понятия не имею, является ли этот метод наиболее эффективным, но до тех пор, пока у вас не появятся основания полагать, что он вызывает проблемы, нет необходимости беспокоиться о последствиях для производительности. Это преждевременная оптимизация. Я бы не стал беспокоиться о производительности этого метода. Вы должны доверять фреймворку, в противном случае вы перепишете его (тем самым подорвёте смысл фреймворка) из-за необоснованной паранойи.
Я бы создал новый класс под названием itemgroup
, а затем добавить дополнительный 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;
}
Этот код кода сквозь несортированный массив, отслеживая минимальное время для каждой группы, а также настроек группа для каждого элемента. С этим завершенным, вы просто сортируете в группе . Время
и и
:
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; }
}
и, если вы действительно хотели быть Blazing быстро, вы даже можете изменить свойство Сеттер для вашего времени время
свойство Свойство для установки времени группы на лету:
@implementation ItemClass
- (void)setTime:(NSNumber *)newTime
{
if (newTime < group.time) { group.time = newTime; }
time = [newTime copy];
}
@end
Обратите внимание, что вы должны быть уверены, что группа
была установлена, прежде чем установить время. С этим на месте вам вообще не понадобится эта петля сортировки. SortDescriptors будет достаточно.
Я прошел через это, чтобы сделать небольшой код (не пытался запустить его или действительно пройтись по нему, так что может быть пара ошибок, но у него есть общая идея), чтобы сделать то, что вы ищете. С точки зрения производительности, это, вероятно, будет не лучшим вариантом, если вы начнете сталкиваться с огромными объемами данных. Я уверен, что есть лучший способ сделать это, но я чувствовал, что сделать это самым простым способом, как ответ на "временное исправление".
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;
}
В конце концов вы получите то, что ищете в отсортированном массиве данных
.