Расширение для последовательностей словарей, где значения Equatable

Я попытался реализовать следующий метод для удаления двойных записей в массиве словарей путем сравнения их конкретных ключей. Однако этот метод расширения не будет работать из-за ошибки:

Двоичный оператор == не может быть применен к двум операндам Equatable

Очевидно, что они равны и имеют один и тот же тип (Iterator.Element.Value), так почему же это не работает?

Я вижу, что он рассматривает Equatable как определенный тип, а не как ограничение. Я не мог заставить его работать с универсальным типом или написав where Iterator.Element == [String: Any], Iterator.Element.Value: Equatable.

У вас есть какие-нибудь идеи о том, как решить эту проблему?

extension Sequence where Iterator.Element == [String: Equatable] {
    public func removeDoubles(byKey uniqueKey: String) -> [Iterator.Element] {
        var uniqueValues: [Iterator.Element.Value] = []
        var noDoubles: [Iterator.Element] = []
        for item in self {
            if let itemValue = item[uniqueKey] {
                if (uniqueValues.contains { element in
                    return itemValue == element
                }) {
                    uniqueValues.append(itemValue)
                    noDoubles.append(item)
                }
            }
        }
        return noDoubles
    }
}

person Suryu    schedule 27.02.2017    source источник


Ответы (1)


[String: Equatable] — это сопоставление строк с любым типом Equatable. Мы не обещаем, что каждое значение будет одного и того же эквивалентного типа. Тем не менее, на самом деле невозможно создать такой словарь (поскольку Equatable имеет связанный тип), поэтому это расширение не может применяться к какому-либо фактическому типу в Swift. (Тот факт, что вы не получаете здесь ошибку, является ошибкой в ​​компиляторе.)

Чтобы это работало, вам понадобится функция SE-0142, который принят, но не реализован. В настоящее время вы не можете ограничивать расширение на основе ограничений типа таким образом.

Есть много способов добиться того, что вы пытаетесь сделать. Один простой способ - передать вашу функцию равенства:

extension Sequence {
    public func removeDoubles(with equal: (Iterator.Element, Iterator.Element) -> Bool) -> [Iterator.Element] {
        var noDoubles: [Iterator.Element] = []
        for item in self {
            if !noDoubles.contains(where: { equal($0, item) }) {
                noDoubles.append(item)
            }
        }
        return noDoubles
    }
}

let noDupes = dict.removeDoubles(with: { $0["name"] == $1["name"] })

Это немного отличается от вашего кода в том, как он ведет себя, когда name отсутствует, но небольшие изменения могут получить то, что вы хотите.

Тем не менее, необходимость в этом настоятельно предполагает неправильную модель данных. Если у вас есть эта последовательность словарей, и вы пытаетесь построить на ней расширение, вы почти наверняка имели в виду последовательность структур. Тогда это становится более простым. Суть словаря — это произвольное сопоставление ключей со значениями. Если у вас есть небольшой набор известных разрешенных ключей, это действительно структура.

person Rob Napier    schedule 27.02.2017
comment
Не уверен, как здесь поможет SE-0142, я думаю, вы имели в виду Параметризованные расширения? :) - person Hamish; 27.02.2017
comment
Я имею в виду параметризованные расширения, но я, хотя SE-0142 предоставил это. Вероятно, неправильно прочитал 142. - person Rob Napier; 27.02.2017
comment
Спасибо за помощь. Проблема в том, что это все JSON и мне нужно объединить два из них — локальный и удаленный, который пришел с сервера. После слияния он сохраняется в Core Data, и кажется, что для создания других типов структур требуется слишком много работы, поскольку это NSManagedObjects, поэтому я не могу использовать их перед их вставкой. В контексте памяти и хранения их всех, а также сравнения тоже было слишком много для этой простой цели, поэтому я продолжил попытки этого расширения. - person Suryu; 28.02.2017