【Swift】構造体の要素を検索してインデックスを取得する方法!

この記事からわかること

  • Swift構造体要素インデックス取得するには?
  • firstfilterfirstIndex使い方
  • Equatableプロトコルとは?
  • 構造体やクラス比較する方法

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

Swiftで構造体を扱うときにデータ内の特定の要素やそのインデックス番号を検索して取得する方法をまとめていきます。

構造体の中の要素を検索して取得する

扱う[構造体]

[
    Language(name : "HTML", version : 5),
    Language(name : "CSS", version : 3),
    Language(name : "Swift", version : 5)
]

配列内に特定の構造体の要素が格納されている際に指定の値を持つ要素を取得するにはfirstメソッドまたはfilterメソッドを使用します。

両者の違いは返り値として取得できる要素数の違いです。firstメソッドは最初にマッチした要素1つfilterメソッドはマッチした要素全てを返します。

firstメソッド

返り値は条件にマッチした要素1つ。ない場合はnilを返します。

array.first(where: { 条件式 })

filterメソッド

返り値は条件にマッチした要素全て。ない場合は空の配列を返します。

array.filter{ 条件式 }

firstメソッドの使い方

firstメソッドは配列に対して使用できるメソッドでfirst(where:)形式を使えば要素の値に条件式を渡してマッチする要素を検索することができます。

クロージャ内では$0疑似的な要素(構造体)に参照することができ、さらにプロパティにはドットシンタックスを使って$0. プロパティでアクセスできます。

条件で検索してマッチする構造体を取得

var all = [
    Language(name : "HTML", version : 5),
    Language(name : "CSS", version : 3),
    Language(name : "Swift", version : 5)
]
var searchLanguage = all.first(where: { $0.version < 4 })
// Optional(__lldb_expr_94.Language(name: "CSS", version: 3))

またwhereを省略して以下のように記述することも可能です。

var searchLanguage2 = all.first {
    $0.version < 4
}

filterメソッドの使い方

filterメソッドの使い方はfirstメソッドとは異なり条件式を渡す際のwhereは必要ありません。そのままクロージャの中に条件を指定しマッチした要素がコレクション形式で取得できます。

条件で検索してマッチする構造体を取得

var all = [
    Language(name : "HTML", version : 5),
    Language(name : "CSS", version : 3),
    Language(name : "Swift", version : 5)
]
var searchLanguage = all.filter({ $0.version > 4 })
// [__lldb_expr_5.Language(name: "HTML", version: 5), __lldb_expr_5.Language(name: "Swift", version: 5)]

以下の形式でもOKです。

var searchLanguage2 = all.filter {
    $0.version > 4
}

構造体の中の要素を検索してインデックスを取得する

配列内に特定の構造体の要素が格納されている際に指定の値(構造体)を持つ要素インデックスを取得するにはfirstIndexメソッドを使用します。

array.firstIndex(of:)

firstIndexメソッドはfirstIndex(of:)形式で使用します。引数ofには値を渡し、その値と完全に一致する要素のインデックス番号を返します。

条件で検索してマッチする構造体を取得

var all = [
    Language(name : "HTML", version : 5),
    Language(name : "CSS", version : 3),
    Language(name : "Swift", version : 5)
]

var item =  Language(name : "Swift", version : 5)
var index = all.firstIndex(of :item)

しかし上記のコードでは以下のようなエラーが発生してしまいます。

Referencing instance method 'firstIndex(of:)' on 'Collection' requires that 'Language' conform to 'Equatable'
// 翻訳:'Collection'でインスタンスメソッド'firstIndex(of :)'を参照するには、'Language'が'Equatable'に準拠している必要があります

これを解決するには構造体がEquatableプロトコルに準拠している必要があります。

struct Language:Equatable{
    var name:String
    var version:Int
}

var all = [
    Language(name : "HTML", version : 5),
    Language(name : "CSS", version : 3),
    Language(name : "Swift", version : 5)
]

var item =  Language(name : "Swift", version : 5)
var index = all.firstIndex(of :item)!

print(all[index]) // Language(name: "Swift", version: 5)

Equatableプロトコルとは?

Equatableプロトコルはオブジェクトを比較可能にできるようにするプロトコルです。

例えば構造体をインスタンス化した変数を2つ用意し、両者の値が同じかどうか識別しようとしてみます。

var ios = Language(name: "Swift", version: 5)
var sns = Language(name: "php", version: 8)

if ios == sns {
    print("同じ言語を使用します")
}

ですが上記のコードでは下記のような「'=='演算子に対応していないオペランドだよ」といったエラーが発生してしまいます。

Binary operator '==' cannot be applied to two 'Language' operands

これを解決するのがEquatableプロトコルです。定義元の構造体をEquatableプロトコルに準拠させることで比較可能になりエラーが発生しなくなります

struct Language:Equatable{
    var name:String
    var version:Int
}

比較するプロパティを変更する

Equatableプロトコルを準拠させた構造体の比較は全てのプロパティの値が一致しているかを識別しています。なので以下のように1つのプロパティの値が違うインスタンス同士は一致していないとみなされ何も出力されません。

var newsns = Language(name: "Swift", version: 5)
var oldsns = Language(name: "Swift", version: 4)

if newsns == oldsns {
    print("同じ言語を使用します")
}

言語名が同じであれば一致していることにしたい」場合はEquatableプロトコルに準拠させた構造体に==メソッドを静的メソッドとして実装することでカスタムできます。

struct Language:Equatable{
    var name:String
    var version:Int
    static func == (lhs: Language, rhs: Language) -> Bool {
        return lhs.name == rhs.name
    }
}

これで再度同じ比較を試してみると期待通り「同じ言語を使用します」が出力されました。

var newsns = Language(name: "Swift", version: 5)
var oldsns = Language(name: "Swift", version: 4)

if newsns == oldsns {
    print("同じ言語を使用します") // 同じ言語を使用します
}

クラスを比較できるようにする

インスタンス(オブジェクト)の比較は構造体だけでなくクラスでも同じです。クラスのインスタンス同士をそのまま比較しようとすると同様のエラーが出てしまいます。

class Language{
    var name:String = ""
    var version:Int = 0
}

var ios = Language()
ios.name = "Swift"
ios.version = 5
var sns = Language()
sns.name = "php"
sns.version = 8
//
if ios == sns {
    print("同じ言語を使用します")
}
エラー:binary operator '==' cannot be applied to two 'Language' operands

構造体の場合はEquatableプロトコルに準拠させるだけで解決できましたが、クラスの場合は比較対象にするプロパティを明示的に==メソッドを静的メソッドとして実装しなければいけません。

class Language:Equatable{
    static func == (lhs: Language, rhs: Language) -> Bool {
        return lhs.name == rhs.name
    }

    var name:String = ""
    var version:Int = 0
}

var ios = Language()
ios.name = "Swift"
ios.version = 5
var sns = Language()
sns.name = "php"
sns.version = 8
//
if ios == sns {
    print("同じ言語を使用します")
}

私がSwift UI学習に使用した参考書

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index