読み込み中...

Swiftで学ぶソートの方法12選

Swiftを使用したソート方法のイラスト Swift
この記事は約23分で読めます。

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

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

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

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

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

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

はじめに

プログラミングの世界で「ソート」は非常に一般的な操作の一つです。

ソートとは、データをある基準に従って順序付けすることを指します。

例えば、数字のリストを小さい順や大きい順に並べ替えたり、文字列をアルファベット順に並べ替えるのが典型的なソートの例です。

Swift言語を使ったソートの方法を12の実例を元に徹底解説します。初心者でもわかりやすい内容とサンプルコードで、Swiftのソートテクニックをマスターしましょう。

●Swiftのソートとは

SwiftはAppleが開発したプログラミング言語で、iOSやmacOSのアプリケーション開発で主に使用されています。

Swiftは安全性や高速性を重視して設計されており、その中でもソートの操作は非常に効率的に実行されます。

○Swiftの基本的なソートのメソッド

Swiftの標準ライブラリには、配列やコレクションをソートするためのメソッドが豊富に提供されています。

主に使用されるメソッドは「sort()」と「sorted()」です。

前者は元の配列自体をソートするもので、後者は新しいソートされた配列を返すものです。

これらのメソッドを使うことで、簡単にデータのソートが可能です。

○Swiftにおけるソートの重要性

アプリケーション開発において、データの整理や表示、検索の効率化のためにソートは頻繁に使用されます。

特に大量のデータを扱う場合、適切なソートアルゴリズムを選択することで、アプリケーションのパフォーマンス向上やユーザーエクスペリエンスの向上が期待できます。

Swiftでは、標準ライブラリによって最適化されたソートメソッドが提供されているため、効率的なソート処理を簡単に実現することができます。

●Swiftでのソート方法

Swift言語では、様々なソートの方法が提供されています。

それぞれのソート方法には独特の特徴や利点があり、適切なソート方法を選ぶことで、効率的なコードを書くことができます。

今回はSwiftでの主なソート方法を12の実例を交えて紹介していきます。

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

Swiftでは、配列を簡単にソートするためのメソッドが提供されています。

下記のサンプルコードは、整数の配列を昇順にソートする基本的な例です。

var numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]
numbers.sort()
print(numbers)

このコードでは、整数を要素とする配列numbersをsortメソッドを用いて昇順にソートしています。

sortメソッドを使用することで、元の配列自体がソートされた状態になります。

このコードを実行すると、[1, 1, 2, 3, 4, 5, 5, 6, 9]という昇順にソートされた配列が出力されます。

○サンプルコード2:数値配列の昇順・降順ソート

数値の配列をソートする際には、昇順だけでなく降順にソートすることもできます。

下記のサンプルコードでは、整数の配列を昇順と降順にソートする方法を表しています。

var numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]
let ascendingNumbers = numbers.sorted()
print(ascendingNumbers) // 昇順

let descendingNumbers = numbers.sorted(by: >)
print(descendingNumbers) // 降順

このコードでは、整数を要素とする配列numbersをsortedメソッドを用いて昇順および降順にソートしています。

sortedメソッドは、新しいソートされた配列を返すので、元の配列は変更されません。

このコードを実行すると、まず昇順にソートされた[1, 1, 2, 3, 4, 5, 5, 6, 9]が出力され、次に降順にソートされた[9, 6, 5, 5, 4, 3, 2, 1, 1]が出力されます。

○サンプルコード3:文字列配列のアルファベット順ソート

Swiftでは、文字列の配列も簡単にソートできます。

特に、アルファベット順にソートする場合、独自のソート関数を書く必要はありません。

Swiftの標準ライブラリが提供するメソッドを利用することで、簡単に文字列をアルファベット順にソートできます。

ここでは、文字列の配列をアルファベット順にソートするサンプルコードを紹介します。

var words = ["banana", "apple", "cherry", "date"]
words.sort()
print(words)

このコードでは、文字列を要素とする配列wordsをsortメソッドを用いてアルファベット順にソートしています。

このコードを実行すると、["apple", "banana", "cherry", "date"]というアルファベット順にソートされた配列が出力されます。

Swiftのsortメソッドは非常に便利で、様々なデータ型に対応しています。

このように、文字列の配列をソートする場合も、わずかなコードで簡単に実現できます。

○サンプルコード4:カスタムオブジェクトのソート

Swiftでは、カスタムオブジェクトの配列もソートすることができます。

ただし、カスタムオブジェクトをソートする際は、どのプロパティを基準にソートするかを明示的に指定する必要があります。

ここでは、カスタムオブジェクトを年齢でソートするサンプルコードを紹介します。

struct Person {
    var name: String
    var age: Int
}

var people = [Person(name: "Yamada", age: 35), Person(name: "Tanaka", age: 28), Person(name: "Suzuki", age: 40)]
people.sort(by: { $0.age < $1.age })
print(people)

このコードでは、Personという構造体を定義し、nameとageという2つのプロパティを持っています。

そして、Personの配列peopleをageプロパティの値を基準にしてソートしています。

このコードを実行すると、年齢の昇順にソートされたPersonの配列が出力されます。

○サンプルコード5:クロージャを使ったソート条件の指定

Swiftでの配列ソートは非常に柔軟で、特定の条件に基づいてソートを行うためにクロージャを使用することができます。

クロージャを使用することで、自分だけのソート条件を簡単に定義できます。

ここでは、整数の配列を偶数が先に、奇数が後になるようにソートするサンプルコードを紹介します。

var numbers = [3, 4, 2, 5, 1, 6]
numbers.sort(by: { (a, b) -> Bool in
    if a % 2 == 0 && b % 2 != 0 {
        return true
    } else if a % 2 != 0 && b % 2 == 0 {
        return false
    } else {
        return a < b
    }
})
print(numbers)

このコードの中で、sort(by:)メソッドはクロージャを引数として受け取ります。

このクロージャでは、2つの引数aとbを比較して、aがbより前にくるべきかどうかをBoolで返します。

この例では、偶数を先頭に移動させ、それ以外の場合は数値を昇順にソートしています。

このコードの出力結果は、[2, 4, 6, 1, 3, 5]となり、偶数が先に並び、奇数が後に続くことが確認できます。

○サンプルコード6:ソートの安定性を保つ方法

ソートの安定性とは、元の配列の同じ要素の順序がソート後も保持される性質を指します。

Swiftのsort()メソッドは安定ではないため、安定ソートを実現したい場合は、独自の方法で実装する必要があります。

ここでは、Tupleを使用してソートの安定性を保つサンプルコードを紹介します。

struct Item {
    var value: Int
    var order: Int
}

let array = [3, 1, 4, 1, 5]
let items = array.enumerated().map { Item(value: $1, order: $0) }

let sortedItems = items.sorted {
    if $0.value != $1.value {
        return $0.value < $1.value
    } else {
        return $0.order < $1.order
    }
}

let sortedArray = sortedItems.map { $0.value }
print(sortedArray)

このコードでは、Item構造体に要素の値と元の配列でのインデックスを保持しています。

そして、元の配列でのインデックスを使用して、同じ要素の順序をソート後も維持するようにしています。

このコードの出力結果は、[1, 1, 3, 4, 5]となり、元の配列の順序を保ったままソートされていることが確認できます。

○サンプルコード7:部分的なソートの実装

Swiftでは、配列全体をソートするだけでなく、部分的なソートも可能です。

部分的なソートは、配列の一部分だけを指定してソートを行う方法で、全体のソートよりも高速に動作する場合があります。

特に大きなデータセットで、特定の範囲だけをソートしたい場合に非常に便利です。

ここでは、Swiftで部分的なソートを行うサンプルコードを紹介します。

var numbers = [3, 8, 2, 5, 7, 1, 6, 4]
let middleIndex = numbers.count / 2

let partiallySorted = numbers.prefix(middleIndex).sorted() + numbers.suffix(from: middleIndex)
print(partiallySorted)

このコードでは、配列numbersの最初の半分の要素だけをソートしています。

prefixメソッドを使用して、配列の最初の半分を取得し、suffixメソッドを使用して、それ以降の要素を取得しています。

この2つの部分配列を組み合わせることで、部分的にソートされた配列を生成します。

このコードを実行すると、[2, 3, 8, 5, 7, 1, 6, 4]という結果が得られます。

○サンプルコード8:複数の条件でのソート

時々、1つの条件だけでなく、複数の条件で配列をソートする必要が出てきます。

Swiftでは、複数の条件でソートすることも簡単に実現できます。

ここでは、名前のアルファベット順と年齢の昇順の2つの条件でソートするサンプルコードを紹介します。

struct Person {
    var name: String
    var age: Int
}

var people = [
    Person(name: "Taro", age: 30),
    Person(name: "Hanako", age: 25),
    Person(name: "Taro", age: 20),
    Person(name: "Jiro", age: 28)
]

people.sort {
    if $0.name != $1.name {
        return $0.name < $1.name
    } else {
        return $0.age < $1.age
    }
}
print(people)

このコードでは、まず名前で比較します。

名前が同じ場合は、年齢で比較しています。

このように、複数の条件を組み合わせてソートを行うことができます。

このコードを実行すると、[Hanako, Jiro, Taro(20歳), Taro(30歳)]という順序でソートされた結果が得られます。

○サンプルコード9:高度なソートアルゴリズムの導入

Swiftでのソートをさらに深く理解し、高度なアルゴリズムを導入する方法を見ていきましょう。

標準的なソートメソッドは一般的なケースに適していますが、特定の要件やパフォーマンスの最適化を求める場合には、カスタムのソートアルゴリズムの導入が考えられます。

例として、マージソートのような分割統治アルゴリズムをSwiftで実装する方法を考えてみましょう。

マージソートは、安定な外部ソートの一つで、大量のデータやリンクされたリストのソートに適しています。

マージソートの基本的な考え方は、データを最小の単位まで分割し、それを再帰的にマージして整列させていく方法です。

func mergeSort<T: Comparable>(_ array: [T]) -> [T] {
    guard array.count > 1 else { return array }

    let middleIndex = array.count / 2
    let leftArray = mergeSort(Array(array[0..<middleIndex]))
    let rightArray = mergeSort(Array(array[middleIndex..<array.count]))

    return merge(leftArray, rightArray)
}

func merge<T: Comparable>(_ left: [T], _ right: [T]) -> [T] {
    var leftIndex = 0
    var rightIndex = 0
    var sorted: [T] = []

    while leftIndex < left.count && rightIndex < right.count {
        if left[leftIndex] < right[rightIndex] {
            sorted.append(left[leftIndex])
            leftIndex += 1
        } else {
            sorted.append(right[rightIndex])
            rightIndex += 1
        }
    }

    while leftIndex < left.count {
        sorted.append(left[leftIndex])
        leftIndex += 1
    }

    while rightIndex < right.count {
        sorted.append(right[rightIndex])
        rightIndex += 1
    }

    return sorted
}

let sampleArray = [7, 2, 6, 3, 9]
let sortedArray = mergeSort(sampleArray)
print(sortedArray)

このコードではmergeSort関数を使って配列を分割し、merge関数を使ってそれらの小さな配列を再結合しています。

mergeSort関数を呼び出すと、指定した配列がマージソートされた状態で返されます。

この例を実行すると、[2, 3, 6, 7, 9]という順序でソートされた配列が出力されます。

○サンプルコード10:外部ライブラリを使ったソート拡張

Swiftのエコシステムには、多くの便利な外部ライブラリがあります。

これらのライブラリを利用することで、Swiftのソート機能を拡張したり、新しいアルゴリズムを簡単に導入することができます。

例として、Swiftで利用可能なソート関連のライブラリ「SwiftSortExtensions」を導入する方法を紹介します。

まず、Swiftのパッケージ管理システムであるSwift Package Managerを使用してライブラリをプロジェクトに追加します。

dependencies: [
    .package(url: "https://github.com/YourLibraryName/SwiftSortExtensions.git", from: "1.0.0")
]

ライブラリを追加した後、該当のソート関数を使用することで、Swiftのソート能力を拡張することができます。

例えば、このライブラリには、特定の条件での高速ソートや新しいソートアルゴリズムなど、様々な拡張機能が含まれています。

○サンプルコード11:非同期でのソート処理

非同期処理は、特に大量のデータを扱う際や、UIがブロックされないようにするために非常に有効です。

Swiftでは、非同期処理を行うための多くの方法が提供されており、ソート処理もその例外ではありません。

非同期でのソート処理を実装するには、DispatchQueueを使用します。

これは、非同期的にタスクをキューに追加して実行するためのAPIです。

ここでは、Swiftで非同期に配列をソートするサンプルコードを紹介します。

import Dispatch

func asyncSort<T: Comparable>(_ array: [T], completion: @escaping ([T]) -> Void) {
    DispatchQueue.global().async {
        let sortedArray = array.sorted()
        DispatchQueue.main.async {
            completion(sortedArray)
        }
    }
}

let sampleArray = [7, 2, 6, 3, 9]
asyncSort(sampleArray) { sortedArray in
    print(sortedArray)
}

このコードでは、asyncSort関数内でDispatchQueue.global().asyncを使い、ソート処理をバックグラウンドスレッドで実行しています。

ソートが完了したら、DispatchQueue.main.asyncを使用して、結果をメインスレッドに返します。

この例を実行すると、コンソールに[2, 3, 6, 7, 9]という順にソートされた配列が表示されます。

○サンプルコード12:ソートのエラーハンドリング

プログラムの中でエラーは避けられないものです。

特にソートを行う際、入力データに不正な値が含まれている場合や、ソートに必要なリソースが不足している場合など、様々なエラーが発生する可能性があります。

Swiftでは、エラーハンドリングを行うための強力な仕組みが提供されています。

ここでは、Swiftでソート処理中に発生する可能性があるエラーを取り扱うサンプルコードを紹介します。

enum SortError: Error {
    case invalidInput
    case outOfResources
}

func safeSort<T: Comparable>(_ array: [T?]) throws -> [T] {
    guard !array.contains(nil) else { throw SortError.invalidInput }

    // 仮想的なリソースチェック。実際には、リソースの可用性を確認するロジックが必要です。
    guard array.count < 10000 else { throw SortError.outOfResources }

    return array.compactMap { $0 }.sorted()
}

let sampleArray: [Int?] = [7, 2, nil, 3, 9]
do {
    let sortedArray = try safeSort(sampleArray)
    print(sortedArray)
} catch SortError.invalidInput {
    print("不正な入力が含まれています。")
} catch SortError.outOfResources {
    print("リソースが不足しています。")
} catch {
    print("未知のエラーが発生しました。")
}

このコードでは、safeSort関数内でエラーチェックを行い、エラーが発生した場合は適切なエラータイプをスローしています。

do-catchステートメントを使用して、この関数を呼び出し、発生したエラーをキャッチして適切に処理しています。

この例を実行すると、コンソールに「不正な入力が含まれています。」と表示されます。

●ソートの応用例

ソートは、プログラミングにおける基本的な操作の一つとして広く利用されていますが、その応用範囲は非常に広いです。

特に、Swiftの豊富なライブラリと組み合わせることで、様々な高度な機能を実現することが可能です。

○サンプルコード13:ソートを使ったランキング機能の作成

ランキング機能は、ソートを活用する典型的な例の一つです。

ユーザーのスコアやアクティビティを基にして、上位から順に並べることで、競争意識を高めたり、ユーザー間の交流を促進することができます。

ここでは、Swiftでスコアを基にランキングを生成するサンプルコードを紹介します。

struct User {
    let name: String
    let score: Int
}

let users = [
    User(name: "田中", score: 250),
    User(name: "山田", score: 320),
    User(name: "鈴木", score: 290)
]

let rankedUsers = users.sorted { $0.score > $1.score }

for (index, user) in rankedUsers.enumerated() {
    print("\(index + 1)位: \(user.name) - \(user.score)点")
}

このコードを実行すると、ユーザーがスコア順にランク付けされ、「1位: 山田 – 320点」、「2位: 鈴木 – 290点」、「3位: 田中 – 250点」という結果が表示されます。

○サンプルコード14:ソートを使ったデータのフィルタリング

ソートは、データのフィルタリングにも利用できます。

例えば、特定の条件を満たすデータだけを抽出して、それを元にソートするといったことが行えます。

ここでは、特定のスコア以上のユーザーだけを抽出し、その上でスコア順にソートするサンプルコードを紹介します。

let thresholdScore = 280
let filteredUsers = users.filter { $0.score >= thresholdScore }
let sortedFilteredUsers = filteredUsers.sorted { $0.score > $1.score }

for user in sortedFilteredUsers {
    print("\(user.name) - \(user.score)点")
}

このコードを実行すると、「山田 – 320点」、「鈴木 – 290点」という、280点以上のスコアを持つユーザーだけがスコア順に表示されます。

●Swiftにおけるソートの注意点と対処法

Swiftでのソート操作は強力で使い勝手がよい一方、注意すべきポイントも存在します。

特に大量のデータのソートや特定の条件下でのソートにおいては、不意の問題が発生することもあります。

ここでは、Swiftでのソートに関連する一般的な問題とその対処法について深く掘り下げていきます。

○不安定なソートに対する注意

ソートアルゴリズムには、安定性という重要な特性があります。

安定なソートアルゴリズムは、同じキーを持つ要素の相対的な順序を保持します。

しかし、Swiftの一部のソートアルゴリズムは安定ではありません。

このため、安定性が求められる場面での使用には注意が必要です。

例えば、次のような名前と年齢のペアの配列があるとします。

struct Person {
    let name: String
    let age: Int
}

var people = [Person(name: "田中", age: 30), Person(name: "山田", age: 25), Person(name: "田中", age: 20)]

年齢でソートした後に名前でソートすると、安定でないソートを使うと年齢のソート結果が保持されない可能性があります。

この問題を回避するためには、複数のキーでのソートを1回の操作で行うことが推奨されます。

people.sort {
    if $0.name != $1.name {
        return $0.name < $1.name
    } else {
        return $0.age < $1.age
    }
}

○大量データのソート時のメモリ消費とその対策

大量のデータをソートする際には、メモリの消費が増加します。

特に、SwiftのArrayは値型であるため、ソートの際にはデータのコピーが発生することがあります。

大量のデータのソートを効率的に行うためには、次のような対策を取ることが考えられます。

  1. 適切なアルゴリズムの選択:データの量や特性に応じて最適なソートアルゴリズムを選択します。
  2. インプレースソートの利用:メモリの追加消費なしにデータをソートする方法です。
  3. 外部ソートの利用:メモリ外のストレージを利用してソートを行います。大量のデータの場合、全てのデータをメモリ上にロードするのではなく、部分的にロードしてソートを行う方法です。

Swiftの標準ライブラリには、多くのソートアルゴリズムが組み込まれていますが、特定の要件に合わせて最適なアルゴリズムを選択することで、効率的なソートを実現することができます。

●Swiftでのソートカスタマイズ方法

Swiftでのソート処理は非常に便利ですが、標準のソートメソッドだけでは足りない場面もあります。

特定のソート条件や独自のソートアルゴリズムを取り入れたい場合、Swiftの柔軟性を活かしてカスタマイズが可能です。

ここでは、Swiftでのソート処理をカスタマイズする方法を詳しく解説していきます。

○サンプルコード15:カスタムソートアルゴリズムの実装方法

場合によっては、独自のソートアルゴリズムを実装する必要が出てくることがあります。

下記のサンプルコードでは、独自のバブルソートアルゴリズムをSwiftで実装しています。

// バブルソートの実装
func bubbleSort<T: Comparable>(_ array: inout [T]) {
    let n = array.count
    for i in 0..<(n - 1) {
        for j in 0..<(n - i - 1) {
            if array[j] > array[j + 1] {
                // 要素を交換する
                array.swapAt(j, j + 1)
            }
        }
    }
}

var numbers = [64, 34, 25, 12, 22, 11, 90]
bubbleSort(&numbers)
print(numbers)  // 結果:[11, 12, 22, 25, 34, 64, 90]

このコードでは、バブルソートという基本的なソートアルゴリズムを用いて、数値の配列をソートしています。

バブルソートは、隣接する要素を比較して、順序が逆であれば交換することを繰り返すことで、配列をソートするアルゴリズムです。

上記のサンプルコードを実行すると、numbers配列はソートされ、[11, 12, 22, 25, 34, 64, 90]という結果が得られます。

このように、Swiftの強力な言語機能を活用することで、独自のソートアルゴリズムを簡単に実装することができます。

まとめ

Swiftでのソート処理は非常に多様で、初心者から上級者まで、様々なニーズに応じて使用することができます。

標準のソートメソッドだけでなく、カスタマイズや独自のソートアルゴリズムの実装もSwiftの柔軟性を活かして容易に行うことができます。

本記事では、Swift言語を使ったソートの基本から応用、カスタマイズ方法までを徹底解説しました。

これにより、読者の皆様がSwiftのソートテクニックをより深く理解し、日々のプログラミング作業に役立てることを期待しています。

最後に、Swiftの進化は日々続いています。

新しいバージョンやライブラリが登場することで、更に便利なソート方法や技術が登場するかもしれません。

常に最新の情報を追い求め、より効率的なソート技術を探求していきましょう。

Swiftでのプログラミングが、皆様のクリエイティブな表現の一助となることを心より願っています。