Array.map() создает '[T]', а не ожидаемый тип контекстного результата '[String: Any?]'
Я пишу расширение, чтобы соединить значения словаря между FirebaseDatabase и Eureka.
private extension Dictionary {
func firebaseFriendlyDictionary() -> [String: Any?] {
return self.map({ (key: String, value: Any?) -> (String, Any?) in
if value is NSDate {
return (key, (value as! NSDate).timeIntervalSince1970)
}
return (key, value)
})
}
}
Но я получаю эту ошибку, когда пытаюсь построить:
map produces '[T]', not the expected contextual result type '[String: Any?]'
Ответы
Ответ 1
Ваша проблема заключается в том, что map
всегда возвращает Array
, даже если применяется к Dictionary
. Ваше сообщение об ошибке в основном означает, что вы объявили свой метод как возвращающий Dicitonary
, но оператор внутри возвращает a Array
([T]
- означает Array с объектами некоторого типа T). В вашем случае массив, возвращаемый map
, будет содержать кортежи (подробнее о них здесь). В этом случае он выглядит как пара значений ключа, но не эквивалентен паре ключ-значение внутри Словаря. В основном, кортежи позволяют вам возвращать более одного значения/объекта из метода. Вы можете думать о них как о анонимных структурах.
По моему мнению, нет необходимости использовать map
для выполнения того, что вам нужно - решение, предоставленное г-ном Xcoder, - это путь.
Ответ 2
Если вы действительно хотите использовать что-то функциональное, например map
, вам действительно нужен метод reduce
.
Я продемонстрирую. Чтобы сделать все как можно более ясным, я думаю, что это поможет, если мы отделим трансформацию, которой подвергаются ваши ценности, в свою собственную функцию:
func dateToSeconds(_ thing:Any?) -> Any? {
guard let date = thing as? Date else {return thing}
return date.timeIntervalSince1970
}
Хорошо, так вот наш тестовый словарь:
let d1 : [String:Any?] = ["1":Date(), "2":"two", "3":15, "4":true]
Теперь мы готовы применить reduce
. Он передает два параметра в свою функцию. Первый - это "аккумулятор", в котором мы продолжаем формировать конечный результат, который в этом случае является другим словарем. Второй - это элемент исходного словаря, представленный как набор пар ключ-значение, называемый key
и value
:
let d2 = d1.reduce([String:Any?]()) { (dict, tuple) in
var dict = dict
dict[tuple.key] = dateToSeconds(tuple.value)
return dict
}
И когда мы исследуем d2
, мы видим, что мы получили правильный ответ:
d2 // ["3": {some 15}, "2": {some "two"}, "1": {some 1486228695.557882}, "4": {some true}]
Ответ 3
Я не мог понять, как исправить эту ошибку, и мне не удалось достичь желаемого результата с расширением или map()
, но у меня есть альтернативное решение проблемы, используя функцию:
Объявление словаря:
var date = NSDate()
var swiftDict:[String : Any?] = ["1": date, "2": "two", "3": 15, "4": true]
Функция:
func firebaseFriendlyDictionary(_ dict: [String : Any?]) -> [String : Any?]{
var Dict = dict
for (key, value) in dict
{
if (value is NSDate){
Dict[key] = (value as! NSDate).timeIntervalSince1970
}
}
return Dict
}
Использование:
swiftDict = firebaseFriendlyDictionary(swiftDict)
Тестирование:
Предполагая, что мы имеем дату 2017-02-04 16:42:46 +0000
, результат равен 1486226566.2349629
, что верно.
Почему не сопоставление Словаря? Как показал Лозиоватый в своем превосходном ответе, map
всегда возвращает Array
, в этом случае Array
Кортежи ([T]
). На мой взгляд, в этом контексте функция карты не нужна, плюс она требует большего количества кода.
Надеюсь, это поможет!