Swiftでマスターするenumerated()の使い方10選

Swiftのenumerated()関数の効果的な使い方Swift
この記事は約24分で読めます。

 

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

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

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

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

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

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

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

はじめに

Swiftプログラミング言語を学ぶ過程で、配列やコレクションの要素を列挙する際に役立つenumerated()関数に出会うことがあります。

この関数は、配列の各要素とそのインデックスを同時に取得するのに非常に便利です。

この記事では、Swiftのenumerated()関数を使った効果的な使い方を、実用的な10のサンプルコードを通じて紹介します。

初心者から上級者まで、enumerated()を活用してより良いコーディングを目指すための情報を提供します。

それでは、Swiftとenumerated()関数の基本を見ていきましょう。

●Swiftとenumerated()関数とは

Swiftは、Appleが開発したプログラミング言語であり、iOS、macOS、watchOS、tvOSなどのアプリ開発に主に利用されています。

その特徴や、enumerated()関数の基本的な役割について説明します。

○Swift言語の特徴

Swiftは、CやObjective-Cに代わる新しい言語として2014年に発表されました。

ここではSwiftの主な特徴をいくつか挙げたものです。

  1. 型安全性:Swiftは、型に厳格で、コンパイル時に多くのエラーを検出することで、バグを減少させることができます。
  2. 高性能:最適化されたコンパイラのおかげで、Swiftは高速な実行速度を持っています。
  3. モダンな構文:Swiftは読みやすく、簡潔なコードを書くことができるモダンな構文を持っています。
  4. メモリ管理:ARC (Automatic Reference Counting) を採用しているため、メモリの管理が自動化され、メモリリークを大幅に減少させることができます。
  5. Playground:SwiftのPlaygroundは、コードの変更をリアルタイムで確認できる機能で、学習やデバッグに役立ちます。

○enumerated()関数の基本的な役割

enumerated()関数は、Swiftの配列やコレクションに関連する関数の一つで、配列の要素とその要素のインデックスをペアとして取得することができます。

具体的には、(index, element)という形のタプルを返します。

この関数は特に、配列の要素をループしながらそのインデックスも同時に扱いたい場合に役立ちます。

例えば、文字列の配列がある場合、それぞれの文字列とその位置(インデックス)を表示したい場合などにenumerated()を利用します。

●enumerated()の基本的な使い方

enumerated()関数の使用は非常にシンプルです。

配列やコレクションのインスタンスに対してこの関数を呼び出すだけで、各要素とそのインデックスのペアをタプルとして返してくれます。

○サンプルコード1:基本的なenumerated()の使用法

このコードでは、文字列の配列を用意し、その配列に対してenumerated()関数を使用しています。

この例では、各文字列とそのインデックスを取得して、結果をコンソールに表示しています。

let fruits = ["apple", "banana", "cherry"]
for (index, fruit) in fruits.enumerated() {
    print("インデックス\(index)の要素は\(fruit)です")
}

上記のコードを実行すると、次のような結果が得られます。

インデックス0の要素はappleです
インデックス1の要素はbananaです
インデックス2の要素はcherryです

このように、enumerated()関数を使用することで、配列の要素とそのインデックスを同時に取得することができます。

○サンプルコード2:配列の要素とインデックスの取得

このコードでは、整数の配列を用意し、その配列に対してenumerated()関数を使用しています。

この例では、各整数とそのインデックスを取得して、結果をコンソールに表示しています。

let numbers = [10, 20, 30, 40]
for (index, number) in numbers.enumerated() {
    print("インデックス\(index)の数値は\(number)です")
}

このコードを実行すると、次のように各整数の値とそのインデックスが表示されます。

インデックス0の数値は10です
インデックス1の数値は20です
インデックス2の数値は30です
インデックス3の数値は40です

enumerated()関数は、どんな型の配列に対しても使用することができるため、多岐にわたるシチュエーションでの利用が期待されます。

●enumerated()の応用例

enumerated()関数はSwiftの中でも非常に便利な関数の一つです。

基本的な使い方や配列の要素とインデックスの取得に関しては前述のセクションで解説しました。

今回は、さらに応用的な使い方を2つのサンプルコードを通じて紹介いたします。

特定の条件を満たす要素のインデックス取得や、辞書型の配列での利用など、より実践的なシーンでの使用例を考えます。

○サンプルコード3:特定の条件を満たす要素のインデックス取得

このコードではenumerated()関数を使って、特定の条件を満たす要素のインデックスを取得するコードを表しています。

この例では、配列内の偶数を持つ要素のインデックスを取得しています。

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let evenIndices = numbers.enumerated().compactMap { (index, element) -> Int? in
    return element % 2 == 0 ? index : nil
}
print(evenIndices)

このコードを実行すると、配列numbers内の偶数要素のインデックスが出力されます。

具体的には、[1, 3, 5, 7]という結果が得られるでしょう。

enumerated()関数とcompactMapを組み合わせることで、条件を満たす要素のインデックスのみを簡潔に取得することが可能です。

○サンプルコード4:辞書型の配列での利用

Swiftでは、辞書型も配列の一種として扱うことができます。

このコードでは、辞書型の配列にenumerated()関数を使用し、各要素のインデックスとともに出力するコードを表しています。

この例では、都市名をキーとし、その人口を値とした辞書の配列を考えています。

let cities = [
    "Tokyo": 13_515_271,
    "Osaka": 2_691_185,
    "Yokohama": 3_753_640
]
for (index, (city, population)) in cities.enumerated() {
    print("Index \(index): \(city) has a population of \(population).")
}

上記のコードを実行すると、都市名とその人口、そしてその要素のインデックスが順番に出力されます。

例として、「Index 0: Tokyo has a population of 13515271.」などの形式で出力されることでしょう。

enumerated()関数を使うことで、辞書型の配列でもインデックスを簡単に取得することができます。

○サンプルコード5:2次元配列での活用

Swiftでは配列だけでなく、2次元配列も一般的に使用されます。

2次元配列は、配列の中にさらに配列が格納されている構造を持っています。

このような2次元配列においても、enumerated()関数は有効に活用できます。

ここでは、2次元配列を使ったenumerated()の使い方を表すサンプルコードを紹介します。

let matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

for (rowIndex, row) in matrix.enumerated() {
    for (colIndex, element) in row.enumerated() {
        print("行: \(rowIndex), 列: \(colIndex), 値: \(element)")
    }
}

このコードでは、matrixという2次元配列を使ってenumerated()を二回適用しています。

外側のループで行のインデックスと行の配列自体を取得し、内側のループで行の中の要素とそのインデックスを取得しています。

結果として、行と列のインデックスに応じた要素の値を表示することができます。

このサンプルコードを実行すると、行のインデックスと列のインデックス、そしてその位置にある要素の値が順番に表示されます。

具体的には、「行: 0, 列: 0, 値: 1」から始まり、「行: 2, 列: 2, 値: 9」という形で最後まで表示されます。

これにより、2次元配列の各要素にアクセスしながらその位置情報も同時に取得することが容易になります。

○サンプルコード6:filterとの組み合わせ

Swiftのfilter関数は、配列やコレクションの要素を特定の条件に基づいてフィルタリングするための関数です。

このfilterenumerated()を組み合わせることで、インデックス情報を保持したまま特定の条件を満たす要素をフィルタリングすることが可能です。

ここでは、enumerated()filterを組み合わせて使用するサンプルコードの一例を紹介します。

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let evenIndexedNumbers = numbers.enumerated().filter { (index, number) in index % 2 == 0 }.map { $0.1 }

print(evenIndexedNumbers)

このコードでは、numbersという配列から偶数インデックスの要素のみを取り出す処理を行っています。

まずenumerated()を使ってインデックスと要素の組を取得し、その後filterで偶数インデックスのものだけを選択します。

最後にmapを使用して、フィルタリングされた組から要素だけを取り出します。

このサンプルコードを実行すると、偶数インデックスの要素である[1, 3, 5, 7, 9]が表示されることが確認できます。

このように、enumerated()filterやその他の関数と組み合わせることで、様々な条件に基づいた処理を柔軟に行うことができます。

○サンプルコード7:mapとの組み合わせ

Swiftのenumerated()関数は、配列の各要素とそのインデックスを一緒に取得するための関数です。

これをmap関数と組み合わせることで、配列の各要素とそのインデックスを元に新しい配列を生成することが可能です。

ここでは、enumerated()mapを組み合わせた使用例を紹介します。

let numbers = [1, 2, 3, 4, 5]
let indexedNumbers = numbers.enumerated().map { (index, element) -> String in
    return "Index \(index): \(element)"
}
print(indexedNumbers)

このコードでは、numbersという整数の配列を使って、enumerated()を利用してインデックスとその要素を取得しています。

その後、map関数を使用して、各要素とそのインデックスを文字列に変換し、新しい配列indexedNumbersを生成しています。

この例では、元の数字配列を元にして、”Index 0: 1″、”Index 1: 2″というような形式の文字列の配列を作成しています。

このコードを実行すると、Indexed Numbers配列は、[“Index 0: 1”, “Index 1: 2”, “Index 2: 3”, “Index 3: 4”, “Index 4: 5”]となります。

○サンプルコード8:reduceとの組み合わせ

reduce関数は、配列の全要素を特定のルールに従って1つの値にまとめるための関数です。

これをenumerated()と組み合わせることで、インデックス情報を活用しながら配列の要素を結合することができます。

ここでは、enumerated()reduceを組み合わせた使用例を紹介します。

let words = ["apple", "banana", "cherry"]
let combinedWords = words.enumerated().reduce("") { (result, value) -> String in
    let (index, word) = value
    return result + "\(index + 1). \(word) "
}
print(combinedWords)

このコードでは、wordsという文字列の配列を使って、enumerated()を利用してインデックスとその要素を取得しています。

その後、reduce関数を使用して、各要素とそのインデックスを利用して1つの文字列にまとめています。

この例では、元の文字列配列を元にして、”1. apple 2. banana 3. cherry”という形式の文字列を作成しています。

このコードを実行すると、combinedWordsは、”1. apple 2. banana 3. cherry”という文字列となります。

enumerated()を活用することで、配列の要素だけでなく、そのインデックス情報も取得して処理を行うことが可能です。

○サンプルコード9:sortとの組み合わせ

Swiftのenumerated()関数とsort()関数を組み合わせることで、配列内の要素をソートする際に、それぞれの要素がどのような順序でソートされたか、つまりソート前のインデックスを保持したままソートを行うことができます。

このコードでは、Swiftのenumerated()関数を使って、配列の要素とそのインデックスを取得し、次にsort()関数を使って要素をソートします。この例では、整数の配列を昇順にソートし、ソートされた要素とその元々のインデックスを表示しています。

let numbers = [5, 3, 8, 1, 4]
let sortedWithIndex = numbers.enumerated().sorted { $0.element < $1.element }
for (index, element) in sortedWithIndex {
    print("要素: \(element), 元のインデックス: \(index)")
}

このコードを実行すると、以下のような出力が得られます。

要素が昇順に並んでいることが確認できますが、”元のインデックス”として表示される部分は、ソート前のインデックスを示しています。

要素: 1, 元のインデックス: 3
要素: 3, 元のインデックス: 1
要素: 4, 元のインデックス: 4
要素: 5, 元のインデックス: 0
要素: 8, 元のインデックス: 2

このようなソート方法は、例えば元のデータの順序とソート後のデータの順序を比較分析したいときなどに非常に役立ちます。

○サンプルコード10:combinationsとの組み合わせ

Swiftのenumerated()関数をSwiftの標準ライブラリには含まれていないcombinations関数と組み合わせることで、配列内の要素の組み合わせを取得する際に、それぞれの要素のインデックスも同時に取得できます。

このコードでは、Swiftのenumerated()関数とcombinations関数を組み合わせて、配列の要素の組み合わせとそのインデックスを取得しています。この例では、文字列の配列から2つの要素の組み合わせを取得し、その組み合わせと元々のインデックスを表示しています。

extension Array {
    func combinations(of count: Int) -> [[Element]] {
        guard count > 0 else { return [[]] }
        guard let first = self.first else { return [] }
        let head = [first]
        let subcombinations = Array(self.dropFirst()).combinations(of: count - 1)
        var result: [[Element]] = subcombinations.map { head + $0 }
        result += Array(self.dropFirst()).combinations(of: count)
        return result
    }
}

let words = ["apple", "banana", "cherry"]
let combinedWithIndex = words.enumerated().combinations(of: 2)
for combination in combinedWithIndex {
    print("組み合わせ: \(combination.map { $0.element }), インデックス: \(combination.map { $0.offset })")
}

このコードを実行すると、以下のような出力が得られます。

組み合わせは2つの文字列を示しており、”インデックス”として表示される部分は、元の配列でのインデックスを示しています。

組み合わせ: [“apple”, “banana”], インデックス: [0, 1]
組み合わせ: [“apple”, “cherry”], インデックス: [0, 2]
組み合わせ: [“banana”, “cherry”], インデックス: [1, 2]

このように、enumerated()関数を他の関数と組み合わせることで、さまざまなデータの処理や分析を行う際の補助として利用できます。

○サンプルコード9:sortとの組み合わせ

Swiftのenumerated()関数とsort()関数を組み合わせることで、配列内の要素をソートする際に、それぞれの要素がどのような順序でソートされたか、つまりソート前のインデックスを保持したままソートを行うことができます。

このコードでは、Swiftのenumerated()関数を使って、配列の要素とそのインデックスを取得し、次にsort()関数を使って要素をソートします。

この例では、整数の配列を昇順にソートし、ソートされた要素とその元々のインデックスを表示しています。

let numbers = [5, 3, 8, 1, 4]
let sortedWithIndex = numbers.enumerated().sorted { $0.element < $1.element }
for (index, element) in sortedWithIndex {
    print("要素: \(element), 元のインデックス: \(index)")
}

このコードを実行すると、次のような出力が得られます。

要素が昇順に並んでいることが確認できますが、”元のインデックス”として表示される部分は、ソート前のインデックスを表しています。

要素: 1, 元のインデックス: 3
要素: 3, 元のインデックス: 1
要素: 4, 元のインデックス: 4
要素: 5, 元のインデックス: 0
要素: 8, 元のインデックス: 2

このようなソート方法は、例えば元のデータの順序とソート後のデータの順序を比較分析したいときなどに非常に役立ちます。

○サンプルコード10:combinationsとの組み合わせ

Swiftのenumerated()関数をSwiftの標準ライブラリには含まれていないcombinations関数と組み合わせることで、配列内の要素の組み合わせを取得する際に、それぞれの要素のインデックスも同時に取得できます。

このコードでは、Swiftのenumerated()関数とcombinations関数を組み合わせて、配列の要素の組み合わせとそのインデックスを取得しています。

この例では、文字列の配列から2つの要素の組み合わせを取得し、その組み合わせと元々のインデックスを表示しています。

extension Array {
    func combinations(of count: Int) -> [[Element]] {
        guard count > 0 else { return [[]] }
        guard let first = self.first else { return [] }
        let head = [first]
        let subcombinations = Array(self.dropFirst()).combinations(of: count - 1)
        var result: [[Element]] = subcombinations.map { head + $0 }
        result += Array(self.dropFirst()).combinations(of: count)
        return result
    }
}

let words = ["apple", "banana", "cherry"]
let combinedWithIndex = words.enumerated().combinations(of: 2)
for combination in combinedWithIndex {
    print("組み合わせ: \(combination.map { $0.element }), インデックス: \(combination.map { $0.offset })")
}

このコードを実行すると、次のような出力が得られます。

組み合わせは2つの文字列を表しており、”インデックス”として表示される部分は、元の配列でのインデックスを表しています。

組み合わせ: ["apple", "banana"], インデックス: [0, 1]
組み合わせ: ["apple", "cherry"], インデックス: [0, 2]
組み合わせ: ["banana", "cherry"], インデックス: [1, 2]

このように、enumerated()関数を他の関数と組み合わせることで、さまざまなデータの処理や分析を行う際の補助として利用できます。

●注意点と対処法

Swiftでenumerated()関数を用いる際、注意すべき点とそれに対する対処法を解説します。

enumerated()関数は非常に便利なものですが、適切に使わないと想定外の結果やパフォーマンスの低下を招くことがあります。

ここでは、そのようなトラブルを避けるためのヒントを提供します。

○enumerated()の返り値に関する注意

enumerated()関数は、配列の各要素とそのインデックスをタプルとして返します。

しかし、返されるインデックスは配列の「位置」としてのインデックスです。

例えば、配列がスライスされたり、フィルタリングされた結果の配列にenumerated()を適用すると、オリジナルの配列とは異なるインデックスが返されることがあります。

このコードでは配列をスライスしてからenumerated()を使用しています。

この例では最初の2つの要素をスキップして、次の2つの要素に対してenumerated()を適用しています。

let numbers = [1, 2, 3, 4, 5]
for (index, value) in numbers[2...3].enumerated() {
    print("Index: \(index), Value: \(value)")
}

このコードの実行結果を見ると、インデックスは0から始まることがわかりますが、実際の配列の位置とは異なります。

Index: 0, Value: 3
Index: 1, Value: 4

対処法として、実際のインデックスが必要な場合、オフセットを追加するか、オリジナルの配列で直接インデックスを計算する必要があります。

○大きなデータを扱う際の注意点

enumerated()関数を大量のデータに適用する際には、パフォーマンスの低下を招く可能性があります。

特に、計算コストの高い操作をループ内で行う場合や、大きなデータセットで頻繁にenumerated()を使用する場合には注意が必要です。

対処法として、不必要にenumerated()を使用することを避け、必要な時だけ使用するようにしましょう。

また、計算コストの高い操作は、ループの外部で実行するか、必要最小限の回数だけ実行するように心掛けましょう。

○コードの最適化に関するアドバイス

enumerated()を使用する際、コードの読みやすさと効率のバランスを取ることが重要です。

例えば、enumerated()と他の関数(例: mapfilter)を組み合わせる際、どの関数を先に適用するかによって、実行速度やメモリの使用量が変わることがあります。

対処法として、実際のパフォーマンスを確認しながら、最適な組み合わせを見つけることが大切です。

必要に応じて、プロファイリングツールを使用して、コードのボトルネックを特定し、最適化を行いましょう。

●カスタマイズ方法

Swiftでのコーディング時、デフォルトのenumerated()関数だけでなく、カスタマイズして使いやすくする方法があります。

これは、具体的な要件やシチュエーションに応じて、enumerated()関数の動作を変更したり、新しい機能を追加する際に役立ちます。

ここでは、独自のenumerated()関数の実装方法を中心に、そのカスタマイズ手法をご紹介します。

○独自のenumerated()関数の実装

通常のenumerated()関数は、配列のインデックスとその要素をタプルとして取得できる非常に便利な関数です。

しかし、場合によっては、これだけでは対応できない要件が生じることもあります。

例えば、インデックスを2つ飛ばしで取得したい、あるいは特定の条件を満たすインデックスのみを取得したいといった場面が考えられます。

そこで、ここではカスタマイズしたenumerated()関数の実装方法を解説していきます。

□インデックスを2つ飛ばしで取得

下記のサンプルコードは、インデックスを2つ飛ばしで取得するカスタムenumerated()関数を表しています。

extension Array {
    func customEnumerated() -> [(index: Int, element: Element)] {
        var result: [(index: Int, element: Element)] = []
        for (index, element) in self.enumerated() {
            if index % 2 == 0 {
                result.append((index, element))
            }
        }
        return result
    }
}

let sampleArray = ["a", "b", "c", "d", "e"]
let enumeratedArray = sampleArray.customEnumerated()
print(enumeratedArray)

このコードでは、配列のcustomEnumerated()関数を使って、インデックスを2つ飛ばしで取得しています。

この例では、[“a”, “b”, “c”, “d”, “e”]という配列から、インデックスが0、2、4の要素のみが取得されることになります。

結果として、[(0, "a"), (2, "c"), (4, "e")]という配列が得られることが期待されます。

□特定の条件を満たすインデックスのみ取得

次に、ある条件を満たす要素のインデックスのみを取得するカスタムenumerated()関数を考えます。

extension Array where Element: Equatable {
    func conditionalEnumerated(condition: (Element) -> Bool) -> [(index: Int, element: Element)] {
        var result: [(index: Int, element: Element)] = []
        for (index, element) in self.enumerated() {
            if condition(element) {
                result.append((index, element))
            }
        }
        return result
    }
}

let numbers = [1, 2, 3, 4, 5]
let evenEnumerated = numbers.conditionalEnumerated(condition: { $0 % 2 == 0 })
print(evenEnumerated)

このコードでは、整数の配列から偶数の要素とそのインデックスのみを取得しています。

この例では、[1, 2, 3, 4, 5]という配列から、偶数の2と4、それらのインデックスのみが取得されることになります。

結果として、[(1, 2), (3, 4)]という配列が得られることが期待されます。

まとめ

Swiftのenumerated()関数は、配列のインデックスとその要素をタプルとして取得するための非常に強力なツールです。

本記事では、その基本的な使用法から、さらに応用的な使い方、そしてカスタマイズ方法までを詳細に解説しました。

特に、標準のenumerated()関数だけでなく、独自にカスタマイズして実装することで、多様なシチュエーションに対応可能なことを紹介しました。

このカスタマイズ方法を駆使することで、より柔軟で効果的なコーディングが実現できるでしょう。

Swiftでのプログラミングを進める上で、enumerated()関数は必須の知識と言えるでしょう。

初心者から上級者まで、本記事の内容を活用して、より効率的かつ高品質なコーディングを目指していただければと思います。