9

私は少し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

したがって、値は「時間」でソートされ、「名前」でグループ化されます。A の時間値が最も小さかったため、A が最初に表示され、A のすべての値が次々に表示されます。次に C です。彼はすべての値の中で 2 番目に小さい時間値を持っていました。名前の並べ替え方法を決定する値を示しました。各名前グループ内では、並べ替えは時間順です。

入力から出力 NSArray に最も効率的な方法で取得するにはどうすればよいですか? (CPUおよびメモリに関して、必ずしもコードに関してではありません。)これのためにNSSortDescriptorsをどのように構築するか、または他の方法を使用しますか?それが最も効率的な方法でない限り、私は自分自身を転がしたくありません。

4

6 に答える 6

21

私の解決策は次のとおりです。

    NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"time" ascending:YES];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, sortDescriptor2, nil];

あなたはそれを試すことができます

于 2010-12-24T11:07:23.143 に答える
18

このメソッドは、必要なことのほとんどを行います。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];
}

これが最も効率的な方法かどうかはわかりませんが、それが問題を引き起こしていると信じる理由が得られるまでは、パフォーマンスへの影響を心配する必要はありません。時期尚早の最適化です。このメソッドのパフォーマンスについて心配する必要はありません。フレームワークを信頼する必要があります。そうしないと、根拠のないパラノイアのためにフレームワークを書き直すことになります (したがって、フレームワークの要点が損なわれます)。

于 2010-02-03T12:12:43.620 に答える
3

という名前の新しいクラスを作成し、アイテム クラスにItemGroup呼び出される ivar を追加groupします。

@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 で十分です。

于 2010-02-03T07:24:53.833 に答える
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

于 2010-02-03T09:23:36.423 に答える
0

より複雑な並べ替えを行う必要がある場合は、「昇順」だけで処理できます (たとえば、NSString を float であるかのように並べ替えます)、次のようにする必要があります。

    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;
于 2011-06-16T18:29:56.117 に答える