Objective-CのsortedArrayUsingComparatorを完全マスターする方法10選

Objective-CのsortedArrayUsingComparatorの詳細解説Objctive-C
この記事は約24分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Objective-Cを用いたプログラミングにおいて、配列の要素を特定の基準でソートする際に利用される「sortedArrayUsingComparator」は、その強力な柔軟性と高いパフォーマンスから、多くの開発者に利用されています。

このメソッドの持つ魅力を十分に引き出し、さらに効果的なプログラミングを行うためには、正確な知識と実践的なサンプルコードの理解が欠かせません。

本記事では、sortedArrayUsingComparatorの基本から応用、そして注意点やカスタマイズ方法まで、初心者から中級者までの読者がスキルアップを図るための詳細な情報を提供します。

Objective-Cの長い歴史と豊富な機能は、iOSやmacOSのアプリケーション開発においてその強みを発揮しています。

しかし、それだけに多岐にわたる機能やメソッドが存在し、特定のメソッドの使い方やベストプラクティスを知ることは容易ではありません。

そこで、この記事を通じて、sortedArrayUsingComparatorの強力な機能を存分に活用して、より品質の高いコードを書くための手助けをしたいと思います。

●Objective-CのsortedArrayUsingComparatorとは

Objective-CのsortedArrayUsingComparatorは、NSArrayのインスタンスメソッドとして提供されており、配列内の要素を任意の条件でソートするためのものです。

このメソッドは、Comparatorという比較を行うブロックを引数として受け取り、その比較結果に基づいて配列の要素をソートします。

ソートの基準となるComparatorブロック内では、2つの要素を引数として受け取り、これらが等しい、または一方が他方よりも前にくるべきであるか、後にくるべきであるかを表すNSComparisonResultの値(NSOrderedAscending、NSOrderedSame、NSOrderedDescending)を返します。

このようにして、配列の要素を任意の基準に基づいてソートすることが可能となります。

○sortedArrayUsingComparatorの基本

Objective-CのsortedArrayUsingComparatorの基本的な動作は、上述の通りComparatorブロックを用いて配列の要素をソートすることです。

このComparatorブロック内での処理が、ソートのキーとなるため、ここでの記述がsortedArrayUsingComparatorの動作を大きく左右します。

Comparatorブロックの記述方法や返すべき値について正確な理解が求められるため、初めてこのメソッドを使用する際は特に注意が必要です。

例えば、文字列の配列をアルファベット順にソートする場合、Comparatorブロック内では2つの文字列を比較し、その順序関係を表すNSComparisonResultの値を返す処理を記述します。

このような基本的な使い方から、より複雑なオブジェクトの属性に基づいてのソートなど、多岐にわたるソート条件を実現することが可能です。

また、sortedArrayUsingComparatorは、ソートされた新しい配列を返すため、元の配列は変更されません。

この点も、他のソートメソッドとの違いとして注意しておくとよいでしょう。

●sortedArrayUsingComparatorの使い方

Objective-Cでの配列のソートは非常に一般的なタスクです。

sortedArrayUsingComparatorはNSArrayクラスのメソッドの一つで、カスタムの比較ロジックを持ったブロックを使って、配列の要素をソートします。

このメソッドの主な利点は、ソートしたい要素の比較方法を自由に定義できる点です。

○サンプルコード1:基本的な配列のソート

このコードでは、文字列の配列をアルファベット順にソートするコードを表しています。

この例では、文字列同士を比較してアルファベット順にソートしています。

NSArray *unsortedArray = @[@"banana", @"apple", @"cherry"];
NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    return [obj1 compare:obj2];
}];

// ソート後の配列を出力
NSLog(@"%@", sortedArray);

上記のコードを実行すると、apple, banana, cherryの順番で文字列がソートされた配列が出力されることが期待されます。

○サンプルコード2:数値を比較してソート

このコードでは、数値の配列を昇順にソートするコードを表しています。

この例では、数値同士を比較して昇順にソートしています。

NSArray *unsortedNumberArray = @[@3, @1, @2];
NSArray *sortedNumberArray = [unsortedNumberArray sortedArrayUsingComparator:^NSComparisonResult(NSNumber *num1, NSNumber *num2) {
    return [num1 compare:num2];
}];

// ソート後の配列を出力
NSLog(@"%@", sortedNumberArray);

上記のコードを実行すると、1, 2, 3の順番で数値がソートされた配列が出力されることが期待されます。

○サンプルコード3:オブジェクトの属性に基づくソート

Objective-Cでの配列のソートは、プリミティブなデータだけではなく、オブジェクトの属性に基づいても実行できます。

特に、多くの情報を持ったオブジェクトのコレクションをソートする際に役立ちます。

例として、Personというクラスを考えます。

このクラスはnameageという2つの属性を持っています。

次に、このクラスのオブジェクトの配列を作成し、age属性に基づいてソートする方法を紹介します。

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
@end

@implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
    self = [super init];
    if (self) {
        _name = name;
        _age = age;
    }
    return self;
}
@end

NSArray *people = @[
    [[Person alloc] initWithName:@"Yamada" age:30],
    [[Person alloc] initWithName:@"Tanaka" age:25],
    [[Person alloc] initWithName:@"Sato" age:35]
];

NSArray *sortedPeople = [people sortedArrayUsingComparator:^NSComparisonResult(Person *person1, Person *person2) {
    if (person1.age < person2.age) {
        return NSOrderedAscending;
    } else if (person1.age > person2.age) {
        return NSOrderedDescending;
    } else {
        return NSOrderedSame;
    }
}];

このコードではPersonクラスを使って3つのオブジェクトを持つ配列を作成しています。

この例ではsortedArrayUsingComparatorを利用して、age属性に基づいて昇順にソートしています。

このコードを実行すると、25歳のTanaka、30歳のYamada、35歳のSatoの順にsortedPeople配列に格納されることが確認できます。

○サンプルコード4:条件を変更して逆順にソート

時として、降順にソートしたい場合もあるでしょう。

そのための方法を紹介しておきます。

NSArray *descSortedPeople = [people sortedArrayUsingComparator:^NSComparisonResult(Person *person1, Person *person2) {
    if (person1.age > person2.age) {
        return NSOrderedAscending;
    } else if (person1.age < person2.age) {
        return NSOrderedDescending;
    } else {
        return NSOrderedSame;
    }
}];

こちらのコードでは、先ほどとは逆の条件でage属性を比較しています。

具体的には、person1.ageperson2.ageより大きい場合にNSOrderedAscendingを返すように変更しています。

このコードを実行すると、35歳のSato、30歳のYamada、25歳のTanakaの順にdescSortedPeople配列に格納されることが確認できます。

●sortedArrayUsingComparatorの応用例

Objective-Cにおける「sortedArrayUsingComparator」は、配列内の要素を独自の条件でソートする際に非常に役立つメソッドです。

ここでは、その使い方の中でも特に応用的な例を2つ取り上げ、それぞれ詳しく解説します。

○サンプルコード5:日付データのソート

日付データを正確にソートすることは、アプリケーション開発の中で頻繁に必要とされるタスクの1つです。

このコードでは、NSDate型のオブジェクトが含まれる配列を、日付の古いものから新しいものへとソートするコードを表しています。

この例では、NSDate型の配列を「sortedArrayUsingComparator」を使って日付順にソートしています。

NSArray *datesArray = @[/* いくつかのNSDateオブジェクト */];
NSArray *sortedDates = [datesArray sortedArrayUsingComparator:^NSComparisonResult(NSDate *date1, NSDate *date2) {
    return [date1 compare:date2];
}];

このコードにおいて、Comparatorブロック内で「compare:」メソッドを使用して、2つの日付オブジェクトを比較しています。

結果として、古い日付から新しい日付の順にソートされた新しい配列が得られます。

実行すると、sortedDatesには日付の古いものから新しいものへと順番にソートされたNSArrayが格納されることになります。

○サンプルコード6:カスタムクラスのインスタンスをソート

Objective-Cにおいて、独自のクラスを定義してインスタンスを作成することはよくあります。

このコードでは、カスタムクラスのインスタンスが含まれる配列を特定の属性に基づいてソートするコードを表しています。

この例では、Personクラスのインスタンスを年齢の若い順にソートしています。

まず、Personクラスを次のように定義します。

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end

次に、Personのインスタンスを年齢の若い順にソートします。

NSArray *peopleArray = @[/* いくつかのPersonオブジェクト */];
NSArray *sortedPeople = [peopleArray sortedArrayUsingComparator:^NSComparisonResult(Person *person1, Person *person2) {
    if (person1.age < person2.age) {
        return NSOrderedAscending;
    } else if (person1.age > person2.age) {
        return NSOrderedDescending;
    } else {
        return NSOrderedSame;
    }
}];

こちらのコードでは、Comparatorブロック内で年齢を比較し、それに応じてソートの順序を決定しています。

このコードを実行すると、sortedPeopleには年齢の若いものから順にソートされたPersonのインスタンスが格納されることになります。

○サンプルコード7:辞書型データのキーに基づくソート

まず、辞書型データのキーに基づくソートの方法を見てみましょう。

このコードでは、辞書型の配列に含まれる各辞書の特定のキーを基準にしてデータをソートする方法を表しています。

この例では、sortedArrayUsingComparatorを使用して、nameキーに基づいて辞書をアルファベット順に並べ替えます。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray *dictArray = @[
            @{@"name": @"Alice", @"age": @24},
            @{@"name": @"Bob", @"age": @28},
            @{@"name": @"Charlie", @"age": @22}
        ];

        NSArray *sortedArray = [dictArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            NSString *name1 = [obj1 objectForKey:@"name"];
            NSString *name2 = [obj2 objectForKey:@"name"];
            return [name1 compare:name2];
        }];

        NSLog(@"Sorted Array: %@", sortedArray);
    }
    return 0;
}

このコードを実行すると、辞書がnameキーのアルファベット順(Alice, Bob, Charlieの順)にソートされた配列が出力されます。

この手法は、任意のキーに基づいてソートする際に有効です。

○サンプルコード8:複数の条件を組み合わせてソート

次に、複数の条件を組み合わせてソートする方法を見ていきます。

このコードでは、まずnameキーに基づいてソートし、それが同じ場合にはageキーに基づいてさらにソートします。

この例では、まず名前をアルファベット順にソートし、同じ名前の場合は年齢が若い順にソートしています。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray *dictArray = @[
            @{@"name": @"Charlie", @"age": @25},
            @{@"name": @"Bob", @"age": @28},
            @{@"name": @"Alice", @"age": @24},
            @{@"name": @"Bob", @"age": @22}
        ];

        NSArray *sortedArray = [dictArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            NSString *name1 = [obj1 objectForKey:@"name"];
            NSString *name2 = [obj2 objectForKey:@"name"];
            NSComparisonResult nameResult = [name1 compare:name2];

            if (nameResult == NSOrderedSame) {
                NSNumber *age1 = [obj1 objectForKey:@"age"];
                NSNumber *age2 = [obj2 objectForKey:@"age"];
                return [age1 compare:age2];
            }

            return nameResult;
        }];

        NSLog(@"Sorted Array: %@", sortedArray);
    }
    return 0;
}

このコードを実行すると、nameキーに基づいてソートされた後、nameが同じ辞書についてはageキーに基づいてさらにソートされた結果が出力されます。

この方法は、複数の基準に基づいてデータを整理したい場合に特に有用です。

○サンプルコード9:ブロック変数を活用した動的なソート

Objective-Cにおいて、ブロック変数は関数のような役割を果たし、一連の処理をカプセル化することができます。

sortedArrayUsingComparatorでは、このブロック変数を活用して、動的にソート条件を指定することができます。

NSArray *array = @[@"apple", @"banana", @"cherry", @"date", @"elderberry"];
NSString *searchTerm = @"e";
NSArray *sortedArray = [array sortedArrayUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) {
    NSRange range1 = [str1 rangeOfString:searchTerm];
    NSRange range2 = [str2 rangeOfString:searchTerm];

    if (range1.location == NSNotFound && range2.location == NSNotFound) {
        return [str1 compare:str2];
    } else if (range1.location != NSNotFound && range2.location == NSNotFound) {
        return NSOrderedAscending;
    } else if (range1.location == NSNotFound && range2.location != NSNotFound) {
        return NSOrderedDescending;
    } else {
        return [str1 compare:str2];
    }
}];

NSLog(@"%@", sortedArray);

このコードでは、文字列配列から「e」という文字を含む要素を優先してソートする処理を表しています。

この例では、searchTermとして指定した「e」を含む文字列が先頭に来るようにソートされます。

このコードの実行をすると、出力される配列は、[“apple”, “cherry”, “elderberry”, “banana”, “date”] となり、「e」を含む文字列が最初に位置していることが確認できます。

○サンプルコード10:メソッドチェーンを利用したソート処理の簡略化

Objective-Cでは、メソッドチェーンを利用して複数のメソッドを連続して呼び出すことで、コードの簡略化や可読性の向上が図れます。

この方法をsortedArrayUsingComparatorで実現する方法を見てみましょう。

NSArray *numbers = @[@5, @1, @9, @3, @7];
NSArray *sortedNumbers = [[numbers sortedArrayUsingComparator:^NSComparisonResult(NSNumber *num1, NSNumber *num2) {
    return [num1 compare:num2];
}] subarrayWithRange:NSMakeRange(1, 3)];

NSLog(@"%@", sortedNumbers);

このコードでは、数値の配列を昇順にソートした後、インデックス1から3つの要素を取得する処理を表しています。

この例では、sortedArrayUsingComparatorでソートを行った後に、subarrayWithRangeメソッドをチェーンしています。

このコードを実行すると、出力される配列は、[@3, @5, @7] となり、ソートされた数値の中央3つの要素を取得することができます。

●注意点と対処法

Objective-CのsortedArrayUsingComparatorを使用する際の注意点とその対処法について解説します。

○特定のデータ型のソートにおける問題点

Objective-Cでの配列のソートは非常に柔軟性がありますが、sortedArrayUsingComparatorを使用する際には、特定のデータ型に対して適切な比較ロジックを実装することが重要です。

例えば、NSStringのオブジェクトとNSNumberのオブジェクトを同じ配列内でソートしようとすると、データ型の違いから正しくソートできない場合があります。

このコードでは、NSStringとNSNumberの混在した配列をソートする例を表しています。

この例では、NSStringとNSNumberを正しくソートできないため、エラーが発生する可能性があります。

NSArray *mixedArray = @[@"Apple", @3, @"Orange", @1, @"Banana", @2];
NSArray *sortedArray = [mixedArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    return [obj1 compare:obj2];
}];

上記のコードでは、compare:メソッドはNSStringとNSNumberの両方に存在しますが、混在したデータ型をこのように比較することは推奨されません。

その結果、ランタイムエラーが発生する可能性があります。

対処法としては、ソート前にデータ型を確認し、異なるデータ型が混在している場合は、適切にソートロジックを分岐させることが求められます。

○Comparatorブロック内でのエラーハンドリング

sortedArrayUsingComparatorのComparatorブロック内でのエラーハンドリングは、ソート処理中に予期せぬエラーが発生した場合に備えるために重要です。

下記のコードは、Comparatorブロック内でエラーハンドリングを実施して、エラーが発生した場合にその情報を取得し、適切に処理する例を表しています。

NSArray *numberArray = @[@3, @1, @2];
NSArray *sortedArray = [numberArray sortedArrayUsingComparator:^NSComparisonResult(NSNumber *num1, NSNumber *num2) {
    @try {
        return [num1 compare:num2];
    }
    @catch (NSException *exception) {
        NSLog(@"Exception occurred: %@, %@", exception, [exception userInfo]);
        return NSOrderedSame;
    }
}];

この例では、ソート処理中に何らかのエラーが発生した場合、@catchブロックでそのエラー情報を取得しています。

そして、エラーが発生した場合のソートの結果としてNSOrderedSameを返しています。

これにより、エラーが発生してもアプリがクラッシュすることなく、安全にソート処理を完了させることができます。

●カスタマイズ方法

Objective-CのsortedArrayUsingComparatorメソッドは、配列のソート処理をカスタマイズして実行するための非常に有用なメソッドです。

デフォルトのソート条件だけでなく、独自の比較ロジックを実装することで、さまざまなニーズに対応するソートを実現できます。

ここでは、sortedArrayUsingComparatorメソッドをさらに強力に利用するためのカスタマイズ方法について、詳細に解説します。

○sortedArrayUsingComparatorの拡張方法

Objective-CのsortedArrayUsingComparatorメソッドは、既定の動作だけでなく、独自の動作を拡張することができます。

たとえば、特定の条件でのソートを頻繁に使用する場合、その条件を独自のカスタム比較関数として実装し、再利用可能にすることが考えられます。

このコードでは、文字列の長さに基づいてソートを行うカスタム比較関数を実装しています。

この例では、文字列の長さを比較して、長い文字列を前にするようにソートしています。

NSArray *words = @[@"apple", @"banana", @"cherry", @"date"];
NSArray *sortedWords = [words sortedArrayUsingComparator:^NSComparisonResult(NSString *word1, NSString *word2) {
    if ([word1 length] > [word2 length]) {
        return NSOrderedAscending;
    } else if ([word1 length] < [word2 length]) {
        return NSOrderedDescending;
    } else {
        return NSOrderedSame;
    }
}];
NSLog(@"%@", sortedWords);

上記のコードを実行すると、出力結果は[@"banana", @"cherry", @"apple", @"date"]となります。

このように、文字列の長さを基準にソートするカスタム比較関数をsortedArrayUsingComparatorメソッドに適用して、期待した順序にソートされた配列を取得できます。

○独自の比較ロジックを実装する

sortedArrayUsingComparatorメソッドの最大の利点は、任意の比較ロジックを実装できることです。

これにより、標準のソート方法だけでなく、独自のニーズや要件に合わせたソートが可能になります。

たとえば、特定の文字列が含まれる要素を優先してソートしたい場合や、複数の属性を持つオブジェクトの特定の属性を基準にソートしたい場合など、柔軟なソート条件を指定できます。

このコードでは、文字列の中に”apple”が含まれているかどうかを基準にソートを行っています。

この例では、”apple”が含まれている文字列を前にするようにソートしています。

NSArray *fruits = @[@"apple pie", @"orange juice", @"banana split", @"apple juice"];
NSArray *sortedFruits = [fruits sortedArrayUsingComparator:^NSComparisonResult(NSString *fruit1, NSString *fruit2) {
    BOOL containsApple1 = [fruit1 containsString:@"apple"];
    BOOL containsApple2 = [fruit2 containsString:@"apple"];

    if (containsApple1 && !containsApple2) {
        return NSOrderedAscending;
    } else if (!containsApple1 && containsApple2) {
        return NSOrderedDescending;
    } else {
        return [fruit1 compare:fruit2];
    }
}];
NSLog(@"%@", sortedFruits);

このコードを実行すると、出力結果は[@"apple pie", @"apple juice", @"banana split", @"orange juice"]となります。

“apple”を含む文字列が前にソートされていることが確認できます。

まとめ

Objective-CのsortedArrayUsingComparatorメソッドは、カスタムのソートロジックを簡単に実装できる強力なツールです。

このメソッドを使用することで、標準のソート条件だけでなく、独自のソート条件を柔軟に実装することができます。

今回の記事を通じて、基本的な使い方から応用例、カスタマイズ方法まで、sortedArrayUsingComparatorメソッドの魅力と可能性を深く探ることができたかと思います。

日々の開発において、このメソッドを効果的に活用し、より高度なプログラミングスキルの習得を目指してください。