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

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.

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

Swift 5 How can I hash two arrays

How can I make this code SWIFT accepting? I've got two arrays of type ANY one array's value should act as the key, the other one as the appropriate value:
let it_tt_ar = db.pair(keys: "int_test", values: "text_test");
func _pair<K : Hashable, V>(keys: [K], values: [V]) -> Dictionary<K,V> {
var result = Dictionary<K, V>();
for i in 0...(keys.count - 1) {
result[keys[i]] = values[i];
}
return result;
}
func pair (keys: String?, values: String?) -> Dictionary<Int32,Any> {
if let _keys = keys, let _values = values {
let result = _pair(keys: hashtable[_keys] as! [Int32], values: hashtable[_values]!);
return result;
} else {
return [:];
}
}
I can't get it working if the type of the key is unknown. I want to write it like this:
let it_tt_ar = db.pair<Int32,String>(keys: "int_test", values: "text_test");
or
let it_tt_ar = db.pair(keys: "int_test", values: "text_test", kt:(Int32.self,String.self));
... in the last case by catching kt: in the function
But there's seems no chance to win against SWIFT:
cannot specify generic functions
or
Int32 cannot fulfill the hashable protocol
It's terrible! You want to write application logic but 80% of the development time is wasted by got to have fulfill such rules!
It looks like you're trying to turn a pair of arrays into a dictionary, regardless of the type of the array (provided, of course, that the type of the key array element is hashable). Here is one way:
let k : [Int] = [1,2,3]
let v : [String] = ["one", "two", "three"]
func pair<Key, Value>(keyArray keys:[Key], valueArray values:[Value]) -> Dictionary<Key,Value> where Key:Hashable {
zip(keys,values).reduce(into: Dictionary<Key,Value>()) {
(dict, tuple) in dict[tuple.0] = tuple.1
}
}
let result = pair(keyArray: k, valueArray: v)
print(result) // [1: "one", 2: "two", 3: "three"], in some order
Found a solution that works for me:
var db = try DataBaseSqlite(dbfile: "test.db");
try db.select(sql: "int_test, real_test, text_test from stest");
var it = db.valueByKey(key: "int_test");
var rt = db.valueByKey(key: "real_test");
var tt = db.valueByKey(key: "text_test");
let it_tt_ar = db.pair(keys: "int_test", values: "text_test", kt: Int32.self);
let tt_it_ar = db.pair(keys: "text_test", values: "int_test", kt: String.self);
try db.close();
func _pair<K : Hashable, V>(keys: [K], values: [V]) -> Dictionary<K,V> {
var result = Dictionary<K, V>();
for i in 0...(keys.count - 1) {
result[keys[i]] = values[i];
}
return result;
}
func pair<T>(keys: String?, values: String?, kt: T.Type) -> Dictionary<T,Any> {
if let _keys = keys, let _values = values {
let result = _pair(keys: hashtable[_keys] as! [T], values: hashtable[_values]!);
return result;
} else {
return [:];
}
}
Due to lack of supporting a real hashtable in Swift (like c# does), my hashtable is just an Dictionary of <String,Array> which is automatically built up by the select method.
So from an application point of view I can write a more efficient and generic code to query sqlite databases.
dbValueByKey() returns a typed (requested) Array of the column values and pair() returns just a combination of two columns.

Parse String into an object in Swift

I have received this response from the server and I am sure there must be a more efficient way to convert it into an object.
I have the following response:
[
id=2997,rapidViewId=62,state=ACTIVE,name=Sprint7,startDate=2018-11-20T10:28:37.256Z,endDate=2018-11-30T10:28:00.000Z,completeDate=<null>,sequence=2992,goal=none
]
How do I convert it nicely into a well formed swift object in the simplest way?
Here is my attempt which gives me just the Sprint Value
if sprintJiraCustomField.count > 0 {
let stringOutput = sprintJiraCustomField.first?.stringValue // convert output to String
let name = stringOutput?.components(separatedBy: "name=") // get name section from string
let nameFieldRaw = name![1].components(separatedBy: ",") // split out to the comma
let nameValue = nameFieldRaw.first!
sprintDetail = nameValue// show name field
}
Not sure what format you want but the below code will produce an array of tuples (key, value) but all values are strings so I guess another conversion is needed afterwards
let items = stringOutput.components(separatedBy: ",").compactMap( {pair -> (String, String) in
let keyValue = pair.components(separatedBy: "=")
return (keyValue[0], keyValue[1])
})
This is a work for reduce:
let keyValueStrings = yourString.components(separatedBy: ",")
let dictionary = keyValueStrings.reduce([String: String]()) {
(var aggregate: [String: String], element: String) -> [String: String] in
let elements = element.componentsSeparatedByString("=")
let key = elements[0]
// replace nil with the value you want to use if there is no value
let value = (elements.count > 1) ? elements[1] : nil
aggregate[key] = value
return aggregate
}
This is a functional approach, but you can achieve the same using a for iteration.
So then you can use Swift’s basic way of mapping. for example you will have your custom object struct. First, you will add an init method to it. Then map your object like this:
init(with dictionary: [String: Any]?) {
guard let dictionary = dictionary else { return }
attribute = dictionary["attrName"] as? String
}
let customObjec = CustomStruct(dictionary: dictionary)
We already have some suggestion to first split the string at each comma and then split each part at the equals sign. This is rather easy to code and works well, but it is not very efficient as every character has to be checked multiple times. Writing a proper parser using Scanner is just as easy, but will run faster.
Basically the scanner can check if a given string is at the current position or give you the substring up to the next occurrence of a separator.
With that the algorithm would have the following steps:
Create scanner with the input string
Check for the opening bracket, otherwise fail
Scan up to the first =. This is the key
Consume the =
Scan up to the first , or ]. This is the value
Store the key/value pair
If there is a , consume it and continue with step 3
Consume the final ].
Sadly the Scanner API is not very Swift-friendly. With a small extension it is much easier to use:
extension Scanner {
func scanString(_ string: String) -> Bool {
return scanString(string, into: nil)
}
func scanUpTo(_ delimiter: String) -> String? {
var result: NSString? = nil
guard scanUpTo(delimiter, into: &result) else { return nil }
return result as String?
}
func scanUpTo(_ characters: CharacterSet) -> String? {
var result: NSString? = nil
guard scanUpToCharacters(from: characters, into: &result) else { return nil }
return result as String?
}
}
With this we can write the parse function like this:
func parse(_ list: String) -> [String: String]? {
let scanner = Scanner(string: list)
guard scanner.scanString("[") else { return nil }
var result: [String: String] = [:]
let endOfPair: CharacterSet = [",", "]"]
repeat {
guard
let key = scanner.scanUpTo("="),
scanner.scanString("="),
let value = scanner.scanUpTo(endOfPair)
else {
return nil
}
result[key] = value
} while scanner.scanString(",")
guard scanner.scanString("]") else { return nil }
return result
}

Swift: Unable to save tuples having more number of records in NSUserDefaults

I am trying to store tuple in NSUserDefaults. My tuple is below.
var primaryNumberAndName: [(name:String , number: String, numberType: String, imageProfile: UIImage, imageLogo: UIImage)] = []
Adding datas to tuples
for (var i : Int = 0; i < primaryNameArr.count; i++)
{
primaryNumberAndName.append(name: primaryNameArr[i] as! String,
number: primaryNumberArr[i] as! String,
numberType: primaryNumberTypeArr[i] as! String,
imageProfile: UIImage(named: "profile_man.png")!,
imageLogo: UIImage(named: "Our_Logo.png")!)
}
Coding:
var dictOfObjectsData = NSKeyedArchiver.archivedDataWithRootObject(primaryNumberAndName)
myUserDefaul.setObject(dictOfObjectsData, forKey: "PrimaryContacts")
Error:
Cannot invoke 'archivedDataWithRootObject' with an argument list of type {[(name:String , number: String, numberType: String, imageProfile: UIImage, imageLogo: UIImage)]}
Kindly guide me how to solve this.
One option could be to convert tuple into Dictionary before saving it. Something like this:
let primaryNumberKey = "primaryNumber"
let nameKey = "name"
func serializeTuple(tuple: AccessTuple) -> myDictionary {
return [
primaryNumberKey : tuple.primaryNumber,
nameKey : tuple.name
]
}
func deserializeDictionary(dictionary: myDictionary) -> myTuple {
return myTuple (
dictionary[primaryNumberKey] as String!,
dictionary[nameKey] as String!
)
}
This is how you can use it:
// Writing to defaults
let myConvertedDictionary = serializeTuple(myTuple)
NSUserDefaults.standardUserDefaults().setObject(myConvertedDictionary, forKey: "UserDetailslKey")
// Reading from defaults
let myFetchedDictionary = NSUserDefaults.standardUserDefaults().dictionaryForKey("UserDetailslKey") as myDictionary
let myFetchedTuple = deserializeDictionary(myFetchedDictionary)
You can now archive Dictionary the regular way!

Swift Dictionary default value

A pattern I've gotten used to with Python's defaultdicts is a dictionary that returns a default value if the value for a given key has not been explicitly set. Trying to do this in Swift is a little verbose.
var dict = Dictionary<String, Array<Int>>()
let key = "foo"
var value: Array<Int>! = dict[key]
if value == nil {
value = Array<Int>()
dict[key] = value
}
I realize I can make a class that does this, but then the actual Dictionary has to be accessed through a property to use any of the other normal Dictionary methods
class DefaultDictionary<A: Hashable, B> {
let defaultFunc: () -> B
var dict = Dictionary<A, B>()
init(defaultFunc: () -> B) {
self.defaultFunc = defaultFunc
}
subscript(key: A) -> B {
get {
var value: B! = dict[key]
if value == nil {
value = defaultFunc()
dict[key] = value
}
return value
}
set {
dict[key] = newValue
}
}
}
Is there a better pattern for this?
This changed in Swift 4, and there's now a way to read a key's value or provide a default value if the key isn't present. For example:
let person = ["name": "Taylor", "city": "Nashville"]
let name = person["name", default: "Anonymous"]
This is particularly useful when modifying dictionary values, because you can write code like this:
var favoriteTVShows = ["Red Dwarf", "Blackadder", "Fawlty Towers", "Red Dwarf"]
var favoriteCounts = [String: Int]()
for show in favoriteTVShows {
favoriteCounts[show, default: 0] += 1
}
I covered this change and others in my article What's new in Swift 4.
Using Swift 2 you can achieve something similar to python's version with an extension of Dictionary:
// Values which can provide a default instance
protocol Initializable {
init()
}
extension Dictionary where Value: Initializable {
// using key as external name to make it unambiguous from the standard subscript
subscript(key key: Key) -> Value {
mutating get { return self[key, or: Value()] }
set { self[key] = newValue }
}
}
// this can also be used in Swift 1.x
extension Dictionary {
subscript(key: Key, or def: Value) -> Value {
mutating get {
return self[key] ?? {
// assign default value if self[key] is nil
self[key] = def
return def
}()
}
set { self[key] = newValue }
}
}
The closure after the ?? is used for classes since they don't propagate their value mutation (only "pointer mutation"; reference types).
The dictionaries have to be mutable (var) in order to use those subscripts:
// Make Int Initializable. Int() == 0
extension Int: Initializable {}
var dict = [Int: Int]()
dict[1, or: 0]++
dict[key: 2]++
// if Value is not Initializable
var dict = [Int: Double]()
dict[1, or: 0.0]
Unless I'm misunderstanding defaultdict in Python, I don't see how nil coalescing wouldn't work for you. Let's say you had a dictionary of type [Int:Int], and you wanted it to return 0 by default. With nil coalescing it looks like this:
let dict = [1:10, 2:8, 3:64]
let valueForKey = dict[4] ?? 0
You mentioned in a comment that that wouldn't work because it wouldn't update the dictionary. I don't understand the problem, though: why would you need to update the dictionary if you knew that every instance of nil would be replaced by your default? Maybe I'm missing something here but it seems like defaults and nil coalescing are (in practice) the same.
You can change the syntax a little, if it makes things more clear:
extension Dictionary {
subscript(key: Key, or r: Value) -> Value {
get { return self[key] ?? r }
set { self[key] = newValue }
}
}
In this case, the example above could be written like this:
let dict = [1:10, 2:8, 3:64]
let valueForKey = dict[4, or: 0]
In this case, mutating methods can work on the keys, like this:
var dict = [2: 8, 3: 64, 1: 10]
dict[2, or: 0]++
dict // [2: 9, 3: 64, 1: 10]
dict[4, or: 0]++
dict // [2: 9, 3: 64, 1: 10, 4: 1]
This extension is similar to the default subscript in Swift 4, with the difference that it will actually store the default value in the dictionary.
(It's also similar to QByte's answer, with the difference that it uses an autoclosure to prevent accessing the default when not needed).
extension Dictionary {
subscript(key: Key, setDefault defaultValue: #autoclosure () -> Value) -> Value {
mutating get {
return self[key] ?? {
let value = defaultValue()
self[key] = value
return value
}()
}
}
}
Note that no setter is defined for the subscript as the standard default subscript already fulfills this purpose.
Example:
var items = [String: ComplexItem]()
let item1 = items["milk", setDefault: ComplexItem()]
let item2 = items["milk", setDefault: ComplexItem()]
Here the ComplexItem is only created once because the dictionary retained it after the first access.

Swift - matching class dynamically

I am trying to write an extension method on Dictionary with the following signature:
func firstNonNilObjectForKey(keys: [Key], ofClass aClass: Any.Type = String.self) -> Value?
This is meant to help me deal with JSON dictionaries, where I often need the first non-null value, but sometimes the JSON includes null itself as value, which is converted to NSNull in Cocoa.
The usage would be something like:
let dict: [String:AnyObject] = [...]
let myValue = dict.firstNonNilObjectForKey([ "key1", "key2" ]) as? String
The issue is implementational - how to match the class:
if let value = self[key] {
if value is aClass { ... } <--- error: aClass is not a type
if let castedValue = value as? aClass { ... } <--- error: ditto
let type = value.dynamicType
if type == aClass { ... } <--- no error, but doesn't handle subclasses!
// Cannot use value.isKindOfClass() since value may be Any
}
I have thought of an alternative:
func firstNonNilObjectForKey<ReturnValueType>(keys: [Key]) -> ReturnValueType?
which allows to be implemented as
if let value = self[key] as? ReturnValueType { ... }
But:
- Here I cannot set the default type (I mostly need String.Type).
- I'm not really sure how to invoke this:
let value = dict.firstNonNilObjectForKey([ "key1", "key2" ]) as? String <--- error: Cannot invoke 'firstNonNilObjectForKey' with an argument list of type '([String])'
let value = dict.firstNonNilObjectForKey<String>([ ... ]) <--- error: Cannot explicitly specialize a generic function
I hope this isn't a duplicate, but most similar questions here are simply handling a situation where you are matching against a known class.
I'm not sure I got the requirements exactly, but can something like this work for you?
extension Dictionary {
func firstNonNilObjectForKey(keys: [Key]) -> String? {
return self.firstNonNilObjectForKey(keys, ofClass: String.self)
}
func firstNonNilObjectForKey<T>(keys: [Key], ofClass aClass: T.Type) -> T? {
for k in keys {
if let v = self[k] as? T {
return v
}
}
return nil
}
}
let dict = ["key1": 2, "key2": "Foo"]
let aString = dict.firstNonNilObjectForKey(["key1", "key2"]) // "Foo"
let anInt = dict.firstNonNilObjectForKey(["key1", "key2"], ofClass: Int.self) // 2
The main gotcha here is that I'm using overloading as opposed to default parameters, which don't seem to get along well with the swift type checker.