Edited at

配列ソートあれこれ

More than 5 years have passed since last update.

ソートに関する覚書。

今回ソートに利用する配列はこちら。

NSArray *array = @[@"Apple", @"cocoa", @"apple", @"beta", @"Xcode", @"2013", @"35", @"Beta", @"0.9", @"70.4"];

※以降のサンプルコードは次の環境で作成しています。


  • Xcode 4.6.3

  • iOS SDK 6.1


文字コード順に取得したい

[NSArray sortedArrayUsingSelector:]でいけるみたい。

NSArray *array = @[@"Apple", @"cocoa", @"apple", @"beta", @"Xcode", @"2013", @"35", @"Beta", @"0.9", @"70.4"];

array = [array sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"%@", array);
/*
(
"0.9",
2013,
35,
"70.4",
Apple,
Beta,
Xcode,
apple,
beta,
cocoa
)
*/


Blocksを利用したソート

NSArrayに格納しているオブジェクトの要素(i.e. MyObject.name)をソート対象とする場合、Blocksで条件を拡張するとよい。

array = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {

NSStringCompareOptions compareOptions = (NSCaseInsensitiveSearch);
return [obj1 compare:obj2 options:compareOptions];
}];


比較オプション(NSStringCompareOptions)

ビット演算を利用して複数の条件を指定できるように定義されている。

typedef NS_OPTIONS(NSUInteger, NSStringCompareOptions)

NSCaseInsensitiveSearch = 1, // 1 << 0
NSLiteralSearch = 2, // 1 << 1
NSBackwardsSearch = 4, // 1 << 2
NSAnchoredSearch = 8, // 1 << 3
NSNumericSearch = 64, // 1 << 6
NSDiacriticInsensitiveSearch = 128, // 1 << 7
NSWidthInsensitiveSearch = 256, // 1 << 8
NSForcedOrderingSearch = 512, // 1 << 9
NSRegularExpressionSearch = 1024 // 1 << 10
};

先ほどのBlocks内の条件を下記に書き換えると、以下の様に出力される。

NSStringCompareOptions compareOptions = (NSNumericSearch | NSCaseInsensitiveSearch)

/*
(
"0.9",
35,
"70.4",
2013,
Apple,
apple,
beta,
Beta,
cocoa,
Xcode
)
*/


NSSortDescriptorによるソート

配列に格納されたクラスの複数要素に対するソート条件が必要なときに有効。

AppleのサンプルコードやSOFを見ているとCore DataやNSDictionaryなどのソートに力を発揮しそう。

今回は先ほどの配列とは別のデータを利用して説明する。

例として以下の定義に従うMyObjectクラスを作成して、


MyObject.h

@interface MyObject : NSObject

@property NSString *name;
@property int lifeTime;

- (id)initWithName:(NSString *)name withLifeTime:(int)lifeTime;
@end


このクラスから5つのインスタンスを生成して配列に格納する。

NSArray *nameArray = @[@"John", @"Mick", @"Tom", @"Sam", @"John"];

NSMutableArray *array = [NSMutableArray new];
for (int i = 0; i < 5; i++) {
[array addObject:[[MyObject alloc] initWithName:nameArray[i] withLifeTime:(arc4random() % 100)]];
}

配列内の各要素の出力結果はこのようになる。

John:   36

Mick: 44
Tom: 1
Sam: 61
John: 48

この配列のソート条件に


  • name(昇順)

  • lifeTime(昇順)

の2つのルールを追加する。

// ソート条件を定義

NSSortDescriptor *nameSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"self.name" ascending:YES];
NSSortDescriptor *lifeTimeSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"self.lifeTime" ascending:YES];

// 上記のソート条件を適用
array = (NSMutableArray *)[array sortedArrayUsingDescriptors:@[nameSortDescriptor, lifeTimeSortDescriptor]];

ソート後の配列は以下の様に出力される。

John:   36

John: 48 // nameが同一(NSOrderedSame)の場合はlifeTimeのソート条件に従っている
Mick: 44
Sam: 61
Tom: 1