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
    }
}