Xcode
iOS
Swift

配列の要素の重複をチェックする

More than 1 year has passed since last update.


配列の要素の重複を汎用的にチェックする

配列の要素の重複をチェックしたく以下の extension を実装してみた。

extension Array {

func hasDuplicatedElements(where predicate: @escaping (Self.Element, Self.Element) throws -> Bool) rethrows -> Bool {
var checkedOffsets: [Int: [Int]] = [:]
for lhs in self.enumerated() {
checkedOffsets[lhs.offset] = [lhs.offset]
for rhs in self.enumerated() {
let checked = checkedOffsets[rhs.offset]?.contains(rhs.offset) ?? false
guard !checked else { continue }
if try predicate(lhs.element, rhs.element) { return true }
checkedOffsets[lhs.offset]!.append(rhs.offset)
}
}
return false
}
}

こんな model と配列があったとして、

struct Model {

let id: String
}

let models = [Model(id: "x"), Model(id: "x")]

こんな感じで使える。ElementEquatable に準拠してなくても使える。

models.hasDuplicatedElements { (lhs, rhs) -> Bool in lhs.id == rhs.id } // true

すでに確認した要素同士の比較を避けているので、 for 文の回る回数は最大で 要素数の二乗 / 2 となるはず。

汎用的に書いたのでどこでも使えるぞ〜ってことで一旦満足した。


結局上のコードは捨てた

現状で配列の重複チェックなんて特定のモデル以外で書かないし、汎用的に書くことを諦めれば for 文の回る回数も最大で配列の要素数となるので、使うモデルクラスのみに extension を生やしてそれを使うことにした。

extension Array where Self.Element == Model {

func hasDuplicatedElements() -> Bool {
var ids = Set<String>()
for model in self {
if ids.contains(model.id) { return true }
ids.insert(model.id)
}
return false
}
}