XS、S、M、L、XL、XXL の値をランダムな順序で持つ NSArray があります。
Objective-Cでこれを適切にソートするにはどうすればよいですか?
出発点としてこのコードを見ましたが、運がありません:
XS、S、M、L、XL、XXL の値をランダムな順序で持つ NSArray があります。
Objective-Cでこれを適切にソートするにはどうすればよいですか?
出発点としてこのコードを見ましたが、運がありません:
「参照」順序として機能する順序付きセットを作成する場合は、NSMutableSet
メソッドintersectSet:
を使用して配列から順序付きリストを作成できます。
編集 David Rönnqvistは良い点を指摘しました。同じ「サイズ」の複数のインスタンスを確認する必要があります。
//This is my "reference" set, declared only once in my class
NSOrderedSet *reference = [NSOrderedSet orderedSetWithObjects:@"XS",@"S",@"M",@"L",@"XL", nil];
//This is the array I'm trying to sort, which may or may not contain all the sizes
NSArray *randomArray = [NSArray arrayWithObjects:@"XL", @"XS", @"M", @"XS", nil];
//Create a mutable ordered set from my reference
NSMutableOrderedSet *ordered = [NSMutableOrderedSet orderedSetWithOrderedSet:reference];
//Interset with the array
[ordered intersectSet:[NSSet setWithArray:randomArray]];
//Look for multiple instances of the same size
NSMutableArray *result = [NSMutableArray array];
for (NSString *sortedSize in ordered){
for (NSString *randomSize in randomArray){
if ([randomSize isEqualToString:sortedSize]){
[result addObject:randomSize];
}
}
}
上記の結果は、次を含む配列でした。
@"XS", @"XS", @"M", @"XL"
これを行うメソッドを作成する場合は、カウントをordered
並べ替えている配列と比較することもできます。それらが等しい場合は、反復を気にする必要はありません。
これを行うには、正しい順序で値を含む参照配列を作成し、参照配列内の各要素が表示されるインデックスに基づいて入力配列を並べ替えます。具体的には:
NSArray *reference = @[ @"XS", @"S", @"M", @"L", @"XL", @"XXL" ];
NSArray *inputArray = @[ @"S", @"M", @"L", @"L", @"M", @"S", @"XXL", @"S" ];
NSArray *sortedArray = [inputArray sortedArrayUsingComparator:
^NSComparisonResult(id a, id b) {
NSUInteger aindex = [reference indexOfObject:a];
NSUInteger bindex = [reference indexOfObject:b];
if (aindex > bindex)
return (NSComparisonResult)NSOrderedDescending;
if (aindex < bindex)
return (NSComparisonResult)NSOrderedAscending;
return (NSComparisonResult)NSOrderedSame;
}];
NSLog(@"%@", sortedArray);
indexOfObject:
これは機能しますが、必要以上に頻繁に呼び出すという欠点があります。がクイックソートsortedArrayUsingComparator
を使用すると仮定すると、O(N*logN) 回の比較が行われ、各比較が2 回呼び出されます。次に、O(N) 時間で実行されます。全体的に、良くありません。indexOfObject:
indexOfObject:
これを改善するにはいくつかの方法があります。たとえば、decorate-sort-undecorateイディオムを使用できます。ここでは、配列要素の並べ替えキー (この場合は参照配列インデックス) を事前に計算し、それらで配列要素を装飾し、それらを使用して並べ替えてから、元に戻します。
この場合、値を参照配列内のインデックスに変換してから (装飾を解除するよりも) 変換し直す方が簡単です (装飾を解除するよりも)。同じ考えだけど。外観は次のとおりです。
// First get the indices
NSMutableArray *indices = [NSMutableArray arrayWithCapacity:[inputArray count]];
for (id elem in inputArray) {
[indices addObject:@([reference indexOfObject:elem])];
}
// Then sort the list of indices
[indices sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"self"
ascending:YES]]];
// Finally, translate indices back to source elements
NSMutableArray *results = [NSMutableArray arrayWithCapacity:[inputArray count]];
for (NSNumber *index in indices) {
[results addObject:reference[[index unsignedIntegerValue]]];
}
NSLog(@"%@", results);
これに取り組むもう 1 つの方法は、代わりに参照配列を参照辞書にし、値 ("XS"、"S" など) を並べ替え順序 (0、1...) にマップすることです。これは事実上、最初のソリューションと同じですが、参照配列のインデックスが事前に計算され、辞書に格納されています。O(N) 時間で実行されるとは異なりindexOfObject:
、O(1) 時間でキー実行によって辞書値を検索します。
これは、オブジェクト リテラルが登場する前は考えられないほど見苦しかったでしょうが、今では非常に洗練されています。小さな参照セットの場合、これは私の好みのオプションです。
NSDictionary *reference = @{
@"XS": @0,
@"S": @1,
@"M": @2,
@"L": @3,
@"XL": @4,
@"XXL": @5
};
NSArray *inputArray = @[ @"S", @"M", @"L", @"L", @"M", @"S", @"XXL", @"S" ];
NSArray *sortedArray = [inputArray sortedArrayUsingComparator:
^NSComparisonResult(id a, id b) {
return [reference[a] compare:reference[b]];
}];
NSLog(@"%@", sortedArray);
sortedArrayUsingComparator メソッドを使用するのが簡単な方法だと思います。
- (NSArray *)sortedArrayUsingComparator:(NSComparator)cmpt
コードは次のようになります。
NSArray * randomArray = [NSArray arrayWithObjects: @"S", @"L", @"M", @"L", @"XL", @"M", nil];
NSMutableDictionary *dictionary = [[ NSMutableDictionary alloc]
init];
[ dictionary setObject: [NSNumber numberWithInt:1] forKey:@"S"];
[ dictionary setObject: [NSNumber numberWithInt:2] forKey:@"M"];
[ dictionary setObject: [NSNumber numberWithInt:3] forKey:@"L"];
[ dictionary setObject: [NSNumber numberWithInt:4] forKey:@"XL"];
NSArray *sortedArray = [randomArray sortedArrayUsingComparator:
^(id obj1, id obj2) {
NSNumber *value1 = [dictionary objectForKey:obj1];
NSNumber *value2 = [dictionary objectForKey:obj2];
return [value1 compare:value2];
}];
for (NSString *item in sortedArray)
{
NSLog(@"%@", item);
}
これはコンソールに出力されます: S M M L L XL
ご覧のとおり、C# ソリューションで言及されているものを使用する代わりにenum
、NSDictionnary を使用しているため、ブロック内のメソッドをNSNumber
直接使用できます。compare
comparison