'(key: String, value: Any)' is not convertible to '[String : Any]'

Viewed 738

I'm trying to refactor this code:

var indices = [String:[Int:Double]]()
apps.forEach { app in indices[app] = [Int:Double]()}
    
var index = 0
timeSeries.forEach { entry in
    entry.apps.forEach{ (arg: (key: String, value: Double)) in
        let (app, value) = arg
        indices[app]?[index] = value
    }
        
    index += 1
}

so I have the signature:

var parameters = timeSeries.map{ entry in entry.apps as [String:Any] }
var indices = getIndices(with: apps, in: parameters) as? [String:[Int:Double]] ?? [String:[Int:Double]]()

and the method:

func getIndices(with source: [String], in entryParameters: [[String:Any]]) -> [String:[Int:Any]] {
    var indices = [String:[Int:Any]]()
    source.forEach { item in indices[item] = [Int:Any]() }
    
    var index = 0
    entryParameters.forEach { (arg: (key: String, value: Any)) in
        let (key, value) = arg
        indices[key]?[index] = value
        
        index += 1
    }

    return indices
}

But this (only in the method, not the original, which works fine) gives: '(key: String, value: Any)' is not convertible to '[String : Any]' at the entryParameters line

The reason I must use Any is because the other source is [String:[Int:Bool]]

edit: some more details:

timeSeries is [TimeSeriesEntry]

// this will need to be defined per-model, so in a different file in final project
struct TimeSeriesEntry: Codable, Equatable {
    let id: String
    let uid: String
    let date: Date
    let apps: [String:Double]
    let locations: [String:Bool]
    
    func changeApp(app: String, value: Double) -> TimeSeriesEntry {
        var apps = self.apps
        apps[app] = value
        
        return TimeSeriesEntry(id: self.id, uid: self.uid, date: self.date, apps: apps, locations: self.locations)
    }
}

notes:

changed calling signature, thanks impression. problem remains.

2 Answers

The problem is that every value in entryParameters is a dictionary so when you do entryParameters.forEach you get dictionary type in the closure not (key, value).

You will get (key,value) when you call the forEach on this dictionary. So your method should look something like this:

func getIndices(with source: [String], in entryParameters: [[String:Any]]) -> [String:[Int:Any]] {
    var indices = [String:[Int:Any]]()
    source.forEach { item in indices[item] = [Int:Any]() }

    var index = 0
    entryParameters.forEach { entry in
        entry.forEach {(arg: (key: String, value: Any)) in
            let (key, value) = arg
            indices[key]?[index] = value
        }

        index += 1
    }

    return indices
}

I just tested this briefly in Playground.

var double = [String:[Int:Double]]()
double["Taco"] = [2: 3.2]

func myFunc(double: [String:[Int:Double]]) {
    print(double.count) //Prints 1
    print(double["Taco"]!) //Prints [2:3.2]
}

func myFunc2(all: [String:Any]) {
    print(all.count) //Prints 1
    print(all["Taco"]!) //Prints [2:3.2]
}


myFunc(double: double)
myFunc2(all: double as [String:Any])

I have my initial [String:[Int:Double]](). Inside this dictionary I set double["Taco"] = [2: 3.2]. I can use 2 different functions, one that is taken as the original type [String:[Int:Double]] and it is easy to use as the functions take in the same parameter type. However, now I created a function that takes in a dictionary of [String:Any]. Now, to USE this method, we MUST typecast variables as [String:Any] when calling the method, as you can see below.

Related