Нет функции высшего порядка, которая делает именно то, что вы ищете. Ближайшим является first(where:)
, но проблема в том, что в результате получается просто Bool
, и у вас нет способа точно определить sh данные, связанные с найденным случаем.
Вы можете напишите что-то вроде:
extension Sequence {
func findFirst<T>(where predicate: (Element) throws -> T?) rethrows -> T? {
for element in self {
if let result = try predicate(element) {
return result
}
}
return nil
}
}
, а затем используйте это как:
let dictionaries = [
[
"dictDynamicKey" : ["dynamicValue1", "dynamicValue2", "dynamicValue3"]
],
[
"dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]
],
]
let desiredValue = "dynamicValue2"
extension Sequence {
func findFirst<T>(where predicate: (Element) throws -> T?) rethrows -> T? {
for element in self {
if let result = try predicate(element) {
return result
}
}
return nil
}
}
let result = dictionaries.findFirst(where: { dict in
dict.findFirst(where: { key, values in
values.contains(desiredValue) ? key : nil
})
})
print(result as Any) // => Optional("dictDynamicKey")
Но это, вероятно, сложнее, чем, вероятно, стоит. Я бы порекомендовал решение Мэтта.
Масштабированное решение
Вы не уточнили это, но я подозреваю, что вам, вероятно, придется делать это несколько раз. В этом случае линейный поиск становится очень медленным. Выполняя поиск ключей по их значениям, вы не пользуетесь ключевым преимуществом словарей: постоянным доступом к значению по его ключу. Ваш код:
- Линейный поиск по массиву словарей, вводит
O(dictionaries.count)
фактор - Для каждого dict в массиве в # 1, линейный поиск по ключу пары / значение, которое вводит
O(dict.count)
коэффициент - Для каждой пары ключ / значение в dict в # 2, линейный поиск по массиву значений, который вводит
O(valueArray.count)
фактор.
Общая сложность времени умножается до O(dictionaries.count * averageDict.count * averageValueArray.count)
, что очень медленно и очень быстро.
Вместо этого вы можете потратить некоторые вычислительные затраты заранее, чтобы создать новую структуру данных, которая лучше возможность обслуживать виды запросов, которые вы хотите выполнить на нем. В этом случае вы можете «инвертировать» словарь.
extension Dictionary {
func inverted<T>() -> [T: Key] where Dictionary.Value == [T] {
let invertedKeyValuePairs = self
.lazy
.flatMap { oldKey, oldValues in
oldValues.map { oldValue in (key: oldValue, value: oldKey) as (T, Key) }
}
return Dictionary<T, Key>(uniqueKeysWithValues: invertedKeyValuePairs)
}
}
// Example usage:
let valuesByKeys = [
"a": [1, 2, 3],
"b": [4, 5, 6]
]
let keysPerValue = valuesByKeys.inverted()
keysPerValue.forEach { key, value in print("key: \(key), value: \(value)") }
// Which results in:
// key: 3, value: a
// key: 4, value: b
// key: 5, value: b
// key: 1, value: a
// key: 6, value: b
// key: 2, value: a
При такой реализации inverted
вы можете инвертировать каждый dict вашего входного набора и объединить их все вместе:
let invertedDictionary = Dictionary(uniqueKeysWithValues: dictionaries.flatMap { $0.inverted() })
invertedDictionary.forEach { key, value in print("key: \(key), value: \(value)") }
// Result:
key: dynamicValue6, value: dictAnotherDynamicKey
key: dynamicValue1, value: dictDynamicKey
key: dynamicValue2, value: dictDynamicKey
key: dynamicValue3, value: dictDynamicKey
key: dynamicValue4, value: dictAnotherDynamicKey
key: dynamicValue5, value: dictAnotherDynamicKey
Вы можете сохранить и поделиться этим словарем, который может дать постоянное время (O(1)
) доступ к ключу, который был связан с любым желаемым значением:
print(invertedDictionary[desiredValue] as Any) // => Optional("dictDynamicKey")