Справка, сортирующая NSArray через два свойства (с NSSortDescriptor?)

Я - что-то вроде 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 для этого или использовал бы какой-либо другой метод? Я не хочу прокручивать свое собственное, если это не самый эффективный путь.

8
задан Jaanus 3 February 2010 в 07:16
поделиться

3 ответа

Метод 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];
}

Понятия не имею, является ли этот метод наиболее эффективным, но до тех пор, пока у вас не появятся основания полагать, что он вызывает проблемы, нет необходимости беспокоиться о последствиях для производительности. Это преждевременная оптимизация. Я бы не стал беспокоиться о производительности этого метода. Вы должны доверять фреймворку, в противном случае вы перепишете его (тем самым подорвёте смысл фреймворка) из-за необоснованной паранойи.

18
ответ дан 5 December 2019 в 04:42
поделиться

Я бы создал новый класс под названием 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 будет достаточно.

3
ответ дан 5 December 2019 в 04:42
поделиться

Я прошел через это, чтобы сделать небольшой код (не пытался запустить его или действительно пройтись по нему, так что может быть пара ошибок, но у него есть общая идея), чтобы сделать то, что вы ищете. С точки зрения производительности, это, вероятно, будет не лучшим вариантом, если вы начнете сталкиваться с огромными объемами данных. Я уверен, что есть лучший способ сделать это, но я чувствовал, что сделать это самым простым способом, как ответ на "временное исправление".

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;

}

В конце концов вы получите то, что ищете в отсортированном массиве данных .

1
ответ дан 5 December 2019 в 04:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: