Tana Gone
Tana Gone
~1 min read

Categories

多数のメンバを持つタイプのメンバへのアクセスを抽象化(汎用化)した下記のsortBy関数は、KeyPathで実現されています。時にはname, 時にはageで配列をソートしたい場合にsortBy関数がどちらの場合でも利用できます。なお、People型のInt型プロパティへのKeyPath<T, V>は\People.ageと表記されます。

class Person {
  var name: String
  var age: Int

  init(name: String, age: Int) {
    self.name = name
    self.age = age
  }
}

func sortBy<T, V: Comparable>(_ keyPath: KeyPath<T, V>, _ array: [T]) -> [T] {
    array.sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] }
}
let p = [Person(name: "A", age: 30), Person(name: "B", age: 25)]
let sorted = sortBy(\Person.age, p)
sorted.forEach { e in
  print(e.age) // 25, 30
}

プロパティへのアクセスを変数に格納し、subscript(keyPath:)メソッドで取り出すのであればさほど便利さは無い。

let people = [Person(name: "Alice", age: 25), Person(name: "Bob", age: 30)]
let keyPathToAge = \Person.age

// すべての年齢を取得
let ages = people.map { $0[keyPath: keyPathToAge] }
print(ages) // => [25, 30]

次のコードと同じであり、KeyPathを使った事で記述量が減るわけでも、簡潔な記述となる訳でもない。

let ages = people.map { e in e.age }

次のようにKeyPathを引数に取る関数を作れるところにメリットがある。

func extract<T>(from people: [Person], using keyPath: KeyPath<Person, T>) -> [T] {
    return people.map { $0[keyPath: keyPath] }
}

let names = extract(from: people, using: \Person.name)
let ages = extract(from: people, using: \Person.age)
print(names) // => ["Alice", "Bob"]
print(ages)  // => [25, 30]