====== NSArrayをFinder風の並び順でソートする ====== こんなファイルがあったとする。 file-a file-1 file-01 file-2 file-10 file-b file-ab 辞書式(普通の昇順ソート)で並べ替えると file-01 file-1 file-10 file-2 file-a file-ab file-b となるわけだが、Finderでソートすると file-1 file-01 file-2 file-10 file-a file-ab file-b となる。連続した数字部分を数値と見なし、数値順で並べてくれる。個人的にはこの方が分かりやすくて好きだ。 で、この数値順ソートをCocoaで実現するにはどーしたらいいのかなー?と思って調べていたら、[[http://developer.apple.com/library/mac/#qa/qa1159/|Technical Q&A QA1159: Sorting Like the Finder]]という、そのまんまの記事がADCにあった。流石林檎様、分かっていらっしゃる。 上記Q&Aのコードを改造して''NSMutableArray''のカテゴリメソッドにするとスマートに使えて良い感じ。 ''NSString''配列のソートはもちろん、任意のオブジェクトの場合はソートに使う''NSString''インスタンス変数名を''sortByFinderOrderWithStringObjectKey:''に渡せばおk。 #include #include static CFComparisonResult CompareLikeTheFinder(const void *val1, const void *val2, void *context) { SInt32 compareResult; CFStringRef lhsStr; CFStringRef rhsStr; CFIndex lhsLen; CFIndex rhsLen; UniChar lhsBuf[MAXPATHLEN]; UniChar rhsBuf[MAXPATHLEN]; // val1 is the left-hand side CFString. // val2 is the right-hand side CFString. if (context) { NSString *key = (NSString *)context; lhsStr = (CFStringRef)[(NSObject *)val1 valueForKey:key]; rhsStr = (CFStringRef)[(NSObject *)val2 valueForKey:key]; } else { lhsStr = (CFStringRef)val1; rhsStr = (CFStringRef)val2; } lhsLen = CFStringGetLength(lhsStr); rhsLen = CFStringGetLength(rhsStr); // Get the actual Unicode characters (UTF-16) for each string. CFStringGetCharacters(lhsStr, CFRangeMake(0, lhsLen), lhsBuf); CFStringGetCharacters(rhsStr, CFRangeMake(0, rhsLen), rhsBuf); // Do the comparison. UCCompareTextDefault( kUCCollateComposeInsensitiveMask | kUCCollateWidthInsensitiveMask | kUCCollateCaseInsensitiveMask | kUCCollateDigitsOverrideMask | kUCCollateDigitsAsNumberMask | kUCCollatePunctuationSignificantMask, lhsBuf, lhsLen, rhsBuf, rhsLen, NULL, &compareResult ); // Return the result. Conveniently, UCCompareTextDefault // returns -1, 0, or +1, which matches the values for // CFComparisonResult exactly. return (CFComparisonResult)compareResult; } static void SortCFMutableArrayLikeTheFinder(CFMutableArrayRef array, CFStringRef key) { CFArraySortValues( array, CFRangeMake(0, CFArrayGetCount(array)), CompareLikeTheFinder, key ); } @interface NSMutableArray (PKAdditions) - (void)sortByFinderOrder; - (void)sortByFinderOrderWithStringObjectKey:(NSString *)key; @end @implementation NSMutableArray (PKAdditions) - (void)sortByFinderOrder { [self sortByFinderOrderWithStringObjectKey:nil]; } - (void)sortByFinderOrderWithStringObjectKey:(NSString *)key { SortCFMutableArrayLikeTheFinder(self, key); } @end 実際のファイル名やディレクトリ名をソートする場合は、''- [NSFileManager displayNameAtPath:(NSString *)path]''で得られる名前をソートしないとFinder順にはならない。なぜかというと、Finderから見えるディレクトリ名はローカライズ(~/Documents → 書類 みたいなの)された物なので。