Adding 'Key:Value' in Multidimensional Dictionary is Overwriting - ios

I declared and initialized a [[String:[String:String] dictionary. It's empty in the beginning, and I am trying to add multiple values under the parent keys.
var dictionary = [String:[String:String]
// some checks
dictionary[i] = ["name" : name]
dictionary[i] = ["from" : country]
dictionary[i] = ["age" : age]
When I do that, I end up having only age key as a child under [key: [String:String]. So it's overwriting when I use this approach.
What is the appropriate way of doing

Your code is creating a new dictionary on each line and assigning this in the dictionary for key i, so you end up with the last dictionary ["age" : age]
You need to create an inner dictionary, assign the values to this and then assign this to your outer dictionary;
var innerDict = [String:String]()
innerDict["name"] = name
innerDict["from"] = from
innerDict["age"] = age
dictionary[i] = innerDict
I would suggest, however, that you look at creating a Struct and put that in your outer dictionary rather than using a dictionary of dictionaries

func insert(key:String, value:String, at k:String) {
var d = dictionary[k] ?? [String:String]()
d[key] = value
dictionary[k] = d
}
And here's how to test it:
insert("name", value: name, at: i)
insert("from", value: country, at: i)
insert("age", value: age, at: i)

You can use optional chaining to assign to the inner dictionary, but you need to create the inner dictionary first.
// create the dictionary of dictionaries
var dictionary = [String:[String:String]]()
// some example constants to make your code work
let i = "abc"
let name = "Fred"
let country = "USA"
let age = "28"
// Use nil coalescing operator ?? to create
// dictionary[i] if it doesn't already exist
dictionary[i] = dictionary[i] ?? [:]
// Use optional chaining to assign to inner dictionary
dictionary[i]?["name"] = name
dictionary[i]?["from"] = country
dictionary[i]?["age"] = age
print(dictionary)
Output:
["abc": ["age": "28", "from": "USA", "name": "Fred"]]
Using these techniques, here's my version of #matt's insert(_:value:at:) function:
func insert(key:String, value:String, at k:String) {
dictionary[k] = dictionary[k] ?? [:]
dictionary[k]?[key] = value
}

Related

How to get all "title" values from dictionary of array in swift 5

Swift 5
I want to get array of all values from "title" key
// Create variable
var arrSportsList:[[String:String]] = []
// viewDidLoad code
arrSportsList = [
["title":"Cricket"],
["title":"Soccer"],
["title":"American Football"],
["title":"Ice Hockey"],
["title":"Tennis"],
["title":"Baseball"],
["title":"Basketball"],
]
I want to use this title in picker view.
You can use compactMap:
let titleArr = arrSportsList.compactMap { $0["title"] }
It transforms each dictionary to the value associated with the key title, and removes the dictionaries which don't have a title key.
I also suggest you to create a class/struct to store these sports, instead of dictionaries:
struct Sport {
let title: String
// other properties
}
Use compactMap(_:) method to get the title value from all dictionaries. If any dictionary doesn't contain title key it will be ignored
var arrSportsList:[[String:String]] = []
// viewDidLoad code
arrSportsList = [
["title1":"anothergame"],
["title":"Cricket"],
["title":"Soccer"],
["title":"American Football"],
["title":"Ice Hockey"],
["title":"Tennis"],
["title":"Baseball"],
["title":"Basketball"],
]
let titleArr = arrSportsList.compactMap { $0["title"] }
print(titleArr)//Cricket,Soccer,American Football,Ice Hockey,Tennis,Baseball,Basketball
Easiest way
let titleArr = arrSportsList.compactMap { $0["title"] }
Or you can loop through the dictionary array and can get the titles Like
let titleArr = Array<String>();
for dict in arrSportsList {
if let title = dict["title"] {
titleArr.append(title);
}
}
In case of understanding more, you can use
// Create variable
var arrSportsList:[[String:String]] = []
// viewDidLoad code
arrSportsList = [
["title":"Cricket"],
["title":"Soccer"],
["title":"American Football"],
["title":"Ice Hockey"],
["title":"Tennis"],
["title":"Baseball"],
["title":"Basketball"],
]
var titleArray: [String]
for (key, value) in arrSportsList {
print (key) // "title"
print (value) //Cricket, Soccer, American Football, Ice Hockey, Tennis, Baseball, Basketball
titleArray.append(value)
}
print (titleArray) //Cricket, Soccer, American Football, Ice Hockey, Tennis, Baseball, Basketball

Sorting array of dictionaries by value after for loop?

I've seen a lot of answers to similar questions but none of the methods have worked so far.
if let users = snapshot.value!["users"] as? Dictionary<String,AnyObject> {
each.users = Int(users.count)
var pointsArray = [Dictionary<String,Int>]()
for (key, value) in users {
let uid = key
let points = value["points"] as! Int
pointsArray.append([uid : points])
}
I'm then needing to sort pointsArray by the "points", sort it from high to low, grab the 0th (highest) element, then grab the uid to use.
I've tried:
var myArr = Array(pointsArray.keys)
var sortedKeys = sort(myArr) {
var obj1 = dict[$0] // get ob associated w/ key 1
var obj2 = dict[$1] // get ob associated w/ key 2
return obj1 > obj2
}
This gives me
Value of type [ Dictionary <String,Int>] has no member keys.
I guess that's cause I'm trying to run the sort on my array of dicts vs the dicts themselves? How do I switch this up to get into the actual dictionaries vs. running the sort on the array?
Right - you're not properly accessing the keys of the dictionaries.
Here's a working code:
var pointsArray = [Dictionary<String, Int>]()
pointsArray.append(["1" : 10])
pointsArray.append(["2" : 45])
pointsArray.append(["3" : 30])
// sort by points
let sorted = pointsArray.sort({ $0.first!.1 > $1.first!.1 })
print(sorted) // [["2": 45], ["3": 30], ["1": 10]]
Array(pointsArray.keys) - this doesn't work, because pointsArray is an array, therefore it doesn't have keys property. The contents of pointsArray are dictionaries and they have keys. So you can access the keys of the first dictionary like this: pointsArray[0].keys

Swift dictionary map - init in closure

I have Swift dictionary:
private var params = [String : AnyObject]()
This contains query items like:
"lat" = "40"
"lon" = "100"
I would like to map this dictionary to NSURLQueryItem array. I want to make it "swifty":
params.map{NSURLQueryItem.init}
But I get an error. Even if I change the map to be [String:String?]. I know I can do something like this elegant one-liner. Can anybody tell how?
You just need to add a failable initializer to NSURLQueryItem that receives a tuple like this (String, AnyObject)
extension NSURLQueryItem {
convenience init?(tuple: (String, AnyObject)) {
guard let value = tuple.1 as? String else { return nil }
self.init(name: tuple.0, value: value)
}
}
That's it!
let params: [String:AnyObject] = ["lat": "40", "lon": "100"]
let queryItems = params.flatMap(NSURLQueryItem.init)
Does your value for the dictionary need to be an optional? In a dictionary, when you assign its key as nil, the entry is deleted.
var params = [String:String?]()
params["lat"] = "40"
params["lon"] = "100"
params["key"] = "hey"
print(params) //result: ["lat": Optional("40"), "lon": Optional("100"), "key": Optional("hey")]
params["key"] = nil
print(params) //result: ["lat": Optional("40"), "lon": Optional("100")]
I suggest using a non optional-value dictionary. I have successfully written the code below:
import UIKit
var params = [String:String]()
params["lat"] = "40"
params["lon"] = "100"
let nsurl = params.map() {NSURLQueryItem.init(name: $0, value: $1)}
print(nsurl)
//Result:
//[<NSURLQueryItem 0x7f8252d29730> {name = lat, value = 40}, <NSURLQueryItem 0x7f8252d29700> {name = lon, value = 100}]
I hope this helps
To you can create one expression like this:
(1...100).map(String.init)
The class has to support it, the String has one initializer with the following signature:
public init(stringInterpolationSegment expr: Int)
With allow it to you use the String.init referred as Int -> String.
But in your case the NSURLQueryItem has not the desired initializer, so the more close you can do it is using map like in the conventional way and passing the parameters to the init of the class NSURLQueryItem like in this way:
let listMapped = params.map { NSURLQueryItem(name: $0.0, value: $0.1 as? String) }
I hope this help you.
I looked at What's the cleanest way of applying map() to a dictionary in Swift? and came up with these two answers:
var params = ["lat": "40", "lon":"100"]
var p:[NSURLQueryItem] = []
var result1 = params.map { (key, value) in p.append(NSURLQueryItem(name:key, value:value)) } // version 1
var result2 = params.reduce([NSURLQueryItem]()) { $0 + [NSURLQueryItem(name:$1.0, value:$1.1)] } // version 2
In version 1, the parameter passed by map() is a (String, String) tuple. In version 2, the parameters passed by reduce() are [NSURLQueryItem] and a (String, String) tuple
Firstly, the the block or closure you're providing to the map function isn't quite right. Blocks are basically nameless functions, they take some number of parameters and return some type. If we were to be verbose, a solution would look something like this:
params.map { (a: (String, String)) -> NSURLQueryItem in
return NSURLQueryItem(name: a.0, value: a.1)
}
However we can simplify this bit of code. The dictionary is [String : String] so it can be inferred that the map function will take a (String, String) as a parameter, so we don't need to write that explicitly.
Swift also allows $n to refer to the nth element of a tuple parameter. Since we only have 1 parameter, $0 will refer to the first element of the first parameter.
The return type of the block can also be inferred, since we're creating a NSURLQueryItem, meaning we don't need to specify that either. This means we also don't need to write return in the block, we can just create the object.
Lastly, you should not call NSURLQueryItem.init() to create a new NSURLQueryItem, you should instead just say NSURLQueryItem().
Here's the minimal solution:
params.map { NSURLQueryItem(name: $0, value: $1) }

How to append associative array elements in Swift

How do I create and append to an associative array in Swift? I would think it should be something like the following (note that some values are strings and others are numbers):
var myArray = []
var make = "chevy"
var year = 2008
var color = "red"
myArray.append("trackMake":make,"trackYear":year,"trackColor":color)
The goal is to be able to have an array full of results where I can make a call such as:
println(myArray[0]["trackMake"]) //and get chevy
println(myArray[0]["trackColor"]) //and get red
Simply like this:
myArray.append(["trackMake":make,"trackYear":year,"trackColor":color])
Add the brackets. This will make it a hash and append that to the array.
In such cases make (extensive) use of let:
let dict = ["trackMake":make,"trackYear":year,"trackColor":color]
myArray.append(dict)
The above assumes that your myArray has been declared as
var myArray = [[String:AnyObject]]()
so the compiler knows that it will take dictionary elements.
I accept above answer.It is good.Even you have given correct answer,I like to give simplest way.The following steps are useful,if you guys follow that.Also if someone new in swift and if they go through this,they can easily understand the steps.
STEP 1 : Declare and initialize the variables
var array = Array<AnyObject>()
var dict = Dictionary<String, AnyObject>()
var make = "chevy"
var year = 2008
var color = "red"
STEP 2 : Set the Dictionary(adding keys and Values)
dict["trackMake"] = make
dict["trackYear"] = year
dict["trackColor"] = color
println("the dict is-\(dict)")
STEP 3 : Append the Dictionary to Array
array.append(dict)
println("the array is-\(array)")
STEP 4 : Get Array values to variable(create the variable for getting value)
let getMakeValue = array[0]["trackMake"]
let getYearValue = array[0]["trackYear"]
let getColorValue = array[0]["trackColor"]
println("the getMakeValue is - \(getMakeValue)")
println("the getYearValue is - \(getYearValue)")
println("the getColorVlaue is - \(getColorValue)")
STEP 5: If you want to get values to string, do the following steps
var stringMakeValue:String = getMakeValue as String
var stringYearValue:String = ("\(getYearValue as Int)")
var stringColorValue:String = getColorValue as String
println("the stringMakeValue is - \(stringMakeValue)")
println("the stringYearValue is - \(stringYearValue)")
println("the stringColorValue is - \(stringColorValue)")
STEP 6 : Finally the total output values are
the dict is-[trackMake: chevy, trackColor: red, trackYear: 2008]
the array is-[{
trackColor = red;
trackMake = chevy;
trackYear = 2008;
}]
the getMakeValue is - Optional(chevy)
the getYearValue is - Optional(2008)
the getColorVlaue is - Optional(red)
the stringMakeValue is - chevy
the stringYearValue is - 2008
the stringColorValue is - red
Thank You
This sounds like you are wanting an array of objects that represent vehicles. You can either have an array of dictionaries or an array of vehicle objects.
Likely you will want to go with an object as Swift arrays and dictionaries must be typed. So your dictionary with string keys to values of differing types would end up having the type [String : Any] and you would be stuck casting back and forth. This would make your array of type [[String : Any ]].
Using an object you would just have an array of that type. Say your vehicle object's type is named Vehicle, that would make your array of type [Vehicle] and each array access would return an instance of that type.
If I want to try it with my own statement. Which also I want to extend my array with the data in my dictionary and print just the key from dictionary:
var myArray = ["Abdurrahman","Yomna"]
var myDic: [String: Any] = [
"ahmed": 23,
"amal": 33,
"fahdad": 88]
for index in 1...3 {
let dict: [String: Any] = [
"key": "new value"
]
// get existing items, or create new array if doesn't exist
var existingItems = myDic[myArray] as? [[String: Any]] ?? [[String: Any]]()
// append the item
existingItems.append(myArray)
// replace back into `data`
myDic[myArray] = existingItems
}

Create Dictionary<String, [SomeStruct]> from [SomeStruct] source-array

var sourceEntries: [Entry] = [entry1, ..., entry14]
var myDict: Dictionary<String, [Entry]> = [:]
for entry in sourceEntries {
if var array = myDict[entry.attribute1] { theArray.append(entry) }
else { myDict[entry.attribute1] = [entry] }
}
I am intending to create a Dictionary, which matches all the objects of the struct "Eintrag" with the same attribute from the source-Array "alleEinträge" to a String containing the value of the shared attribute. For some reason my final Dictionary just matches Arrays of one element to the Strings, although some Arrays ought to contain up to four elements.
The problem is that the array is passed by value (i.e. "copied"), so the array you are writing to when you say array.append is not the array that is "inside" the dictionary. You have to write back into the dictionary explicitly if you want to change what's in it.
Try it in a simple situation:
var dict = ["entry":[0,1,2]]
// your code
if var array = dict["entry"] { array.append(4) }
// so what happened?
println(dict) // [entry: [0, 1, 2]]
As you can see, the "4" never got into the dictionary.
You have to write back into the dictionary explicitly:
if var array = dict["entry"] { array.append(4); dict["entry"] = array }
FURTHER THOUGHTS: You got me thinking about whether there might be a more elegant way to do what you're trying to do. I'm not sure whether you will think this is "more elegant", but perhaps it has some appeal.
I will start by setting up a struct (like your Entry) with a name attribute:
struct Thing : Printable {
var name : String
var age : Int
var description : String {
return "{\(self.name), \(self.age)}"
}
}
Now I will create an array like your sourceEntries array, where some of the structs share the same name (like your shared attribute attribute1):
let t1 = Thing(name: "Jack", age: 40)
let t2 = Thing(name: "Jill", age: 38)
let t3 = Thing(name: "Jill", age: 37)
let arr = [t1,t2,t3]
And of course I will prepare the empty dictionary, like your myDict, which I call d:
var d = [String : [Thing]]()
Now I will create the dictionary! The idea is to use map and filter together to do all the work of creating key-value pairs, and then we just build the dictionary from those pairs:
let pairs : [(String, [Thing])] = arr.map {
t in (t.name, arr.filter{$0.name == t.name})
}
for pair in pairs { d[pair.0] = pair.1 }

Resources