Swift Dictionary: join key and value into string - swift

I'm looking for a best syntax for this:
let responseParameters = ["keyA" : "valueA", "keyB" : "valueB"]
var responseString = ""
for (key, value) in responseParameters {
responseString += "\(key):\(value)"
if Array(responseParameters.keys).last != key {
responseString += "+"
}
}
//responseString: keyA:valueA+keyB:valueB
Something like an array joinWithSeparator, using a flatMap or something like that. (study purpose)

You can map over key/value pairs in dictionaries to convert them to an Array of Strings, then you can join those with +. But remember, dictionaries are unordered, so this will not preserve the input ordering.
let responseParameters = ["keyA" : "valueA", "keyB" : "valueB"]
let responseString = responseParameters.map{ "\($0):\($1)" }
.joined(separator: "+")

A dictionary is not an ordered collection, so you'll have to sort the keys prior to accessing the "ordered version" of the key-value pairs. E.g.
let responseParameters = ["keyA" : "valueA", "keyB" : "valueB", "keyC" : "valueC"]
let responseString = responseParameters
.sort { $0.0 < $1.0 }
.map { $0 + ":" + $1 }
.joinWithSeparator("+")
print(responseString) // keyA:valueA+keyB:valueB+keyC:valueC

Updated answer for swift 5 :
let responseParameters = ["keyA": "valueA", "keyB": "valueB"]
let responseString = responseParameters.map { "\($0):\($1)" }
.joined(separator: "+")

Actually, you can use reduce like: 🙌
let responseParameters = ["keyA": "valueA", "keyB": "valueB"]
let responseString = responseParameters.reduce("") { $0.isEmpty ? "\($1.key):\($1.value)" : "\($0)+\($1.key):\($1.value)" }

Related

how to check if a key equal a value in swift dictionary?

let say I have this dictionary
var myDic = ["Item" : " Item1", "Item2" : " Item3"]
how can I write an if statement to check if Item and Item1 are a key-value paire?
According to the https://developer.apple.com/documentation/swift/dictionary
Every dictionary is an unordered collection of key-value pairs. You
can iterate over a dictionary using a for-in loop, decomposing each
key-value pair into the elements of a tuple.
This means you can write the following code:
let myDict = ["Item" : " Item1", "Item2" : " Item3"]
for (key, val) in myDict {
if (key == "Item2" && val == " Item3") {
print("match!") //prints match!
}
}
This is obviously kind of a "naive" approach where the values are hardcoded in the if-statement. A one-line alternative:
print(myDict.contains(where: { $0.key == "Item2" && $0.value == " Item3" })) //true
Let's say you need to check for two pairs. Then you can use KeyValuePairs which preserves the order and simply iterate:
let toFind: KeyValuePairs = [ "Item2" : " Item3", "Itemx" : " Item4"]
for tup in toFind {
print(myDict.contains(where: { $0 == tup })) //true, false
}
If you want to follow this approach, I recommend reading about the important differences between Dictionary and KeyValuePairs https://developer.apple.com/documentation/swift/keyvaluepairs

Swift: Convert Array of Dictionaries to Array of String based on a key

Following is my Array of Dictionaries and I want to get an Array of only strings based on particular key (contentURL key in my case).
How can I achieve it? I have came across Reduce & Filter but no one fits into my requirement.
(
{
contentURL = "https://d1shcqlf263trc.cloudfront.net/1510232473240ab.mp4";
},
{
contentURL = "https://d1shcqlf263trc.cloudfront.net/151021804847312.mp4";
},
{
contentURL = "https://d1shcqlf263trc.cloudfront.net/151021536556612.mp4";
},
{
contentURL = "https://d1shcqlf263trc.cloudfront.net/151021528690312.mp4";
}
)
Expected Output
[
"https://d1shcqlf263trc.cloudfront.net/1510232473240ab.mp4",
"https://d1shcqlf263trc.cloudfront.net/151021804847312.mp4",
"https://d1shcqlf263trc.cloudfront.net/151021536556612.mp4",
"https://d1shcqlf263trc.cloudfront.net/151021528690312.mp4"
]
Just use compactMap
let array = arrayOfDicts.compactMap {$0["contentURL"] }
var myDict: [[String : String]] = [["contentURL" : "https://d1shcqlf263trc.cloudfront.net/1510232473240ab.mp4"],["contentURL" : "https://d1shcqlf263trc.cloudfront.net/1510232473240ab.mp4"],["contentURL" : "https://d1shcqlf263trc.cloudfront.net/1510232473240ab.mp4"]]
let arr = myDict.map { $0["contentURL"] }
var stringArray:[String] = []
for (key, value) in yourArrayOfDictionary {
stringArray.append(value)
}
var arrayDict = [["contentURL":"fd"],["contentURL":"fda"],["contentURL":"fdb"],["contentURL":"fdc"]]
let arraywithOptionstring = arrayDict.map{$0["contentURL"]}
if let arr = arraywithOptionstring as? [String]{
print(arr)
}
Expected Output : ["fd", "fda", "fdb", "fdc"]
If you want to use reduce:
let arr = [
["contentURL" : "https://d1shcqlf263trc.cloudfront.net/"],
["contentURL" : "https://d1shcqlf263trc.cloudfront.net/.mp4"],
["contentURL" : "https://d1shcqlf263trc.cloudfront.net/1510232473240ab.mp4"]
]
let only = arr.reduce([String]()) { (partialRes, dictionary) -> [String] in
return partialRes + [dictionary["contentURL"]!]
}
More compact version:
let compact = arr.reduce([String]()) { $0 + [$1["contentURL"]!] }
Probably you weren't able to use reduce since you need to remember that subscripting a dictionary returns an Optional that is a different type than String
Also you can use just .map in this case.
let array = arrayOfDicts.map {$0["contentURL"]! }

Check for nil values in Dictionary - Swift 3

I have converted a XML into a Dictionary and I would like to check first if any of the values is nil.
Is there any alternative to this ugly code (the Dictionary contains 30 key-value pairs):
if (xmlDict["key1" != nil && xmlDict["key2" != nil && .... && xmlDict["key30" != nil) {
//Do something with these values
} else {
//Do not do anything at all with this dictionary
}
[Update]
The dictionary has this format (mixed value types):
let futuramaDict = NSArray(contentsOf: xmlFile!) as? [NSDictionary]
futuramaDict {
"Cartoon" : "Futurama"
"Length" : 120
"MainCharacter" : "Philip J. Fry"
"ClosestFriends" : ["Bender","Leela","Zoidberg"]
}
I left the other key-value pairs so I don't fill the thread with irrelevant content
You can check with one line
xml.values.filter({ $0 == nil }).isEmpty
Here's the complete snippet
let xml: [String: String?] = [
"key1" : "Value1",
"key2" : "Value2",
"key3" : nil,
"key4" : "Value4"
]
if xml.values.filter({ $0 == nil }).isEmpty
{
print("Plenty of values")
}
else
{
print("nil :(")
}
A more "swifty" way:
for (key, val) in xmlDict where val == nil {
print("Found a nil value for this key: \(key)")
}
A quick and short solution:
if xmlDict.values.contains(where: {$0 == nil}) {
print("Dict contains at least one nil value")
}
In the case when you have a predefined set of keys you want to look for:
let keysToLookFor = ["k1", "k2", "k3", "k4"]
if keysToLookFor.contains(where: {xmlDict[$0] == nil}) {
print("Dict contains at least one nil value")
}
A sample dict for testing:
let xmlDict: [String: Any?] = ["k1": 22, "k2": Int(2), "k3": nil]
How about this -
let hasAnyKeyNil = dic.keys.contains(where: { dic[$0]! == nil })
This code should achieve what you want:
var values = [Any]()
for index in 1 ... 30 {
let key = "key\(index)" // Generates "key1" through "key30"
guard let value = xmlDict[key] else {
values.removeAll()
break // Abort loop
}
values.append(values)
}
// Next: Process contents of array 'values'
// (will be empty if any key was absent)

How to get last entered value in a Dictionary in Swift?

How do I get the last entered value in a Dictionary in Swift? For example how would I get the value "CCC" from below:
var dictionary = Dictionary<String, String>()
dictionary.updateValue("AAA", forKey: "111")
dictionary.updateValue("BBB", forKey: "222")
dictionary.updateValue("CCC", forKey: "333")
What you are after is generally known as a Ordered Dictionary, i.e. a dictionary that remembers the order that items have been added. A quick Google search turns up a GitHub project that may suit you.
https://github.com/lukaskubanek/OrderedDictionary
With this as the type of your dictionary, you could just use:
dictionary.last // returns Optional(("CCC", "333"))
The GitHub page has details on how you would go about adding this class to your project.
If your key is sorted you can try:
var dictionary = Dictionary<String, String>()
dictionary.updateValue("AAA", forKey: "111")
dictionary.updateValue("BBB", forKey: "222")
dictionary.updateValue("CCC", forKey: "333")
print(dictionary.keys.sorted().last.map({ ($0, dictionary[$0]!) }))
The whole point of dictionaries is they're NOT ordered. This is why their lookups are generally so fast is because they partition elements without regard for their order.
If you want items in a particular order, you should use an array. Yes, "sorted dictionaries" exist, but they're really just dictionary-like interfaces on top of arrays. They contain none of the benefits that traditional dictionaries bring.
If you think you need an ordered dictionary, you probably need to consider a new structure for your data.
//If you need to build a query string for http:
let httpBody:[String : String] = [
"Street" : "1234 W 1st Street",
"Building" : "Suite 500",
"City" : "Los Angeles",
"State" : "CA",
"Zip" : "92005"
]
var idx = 0
var queryStr = ""
for (k, v) in httpBody {
if idx == httpBody.count - 1 {
queryStr += k + "=" + v
} else {
queryStr += k + "=" + v + "&"
idx += 1
}
}
print(queryStr)
//Example: for some REST API
//let bodyData:Data = queryStr.data(using: .utf8)!
//request.httpBody = bodyData
//Or this option:
var newStr = ""
for (k, v) in httpBody {
let index = httpBody.index(httpBody.startIndex, offsetBy: httpBody.count - 1)
if k == httpBody.keys[index] {
newStr += k + "=" + v
} else {
newStr += k + "=" + v + "&"
}
}
print( "\n", newStr)

Concatenate two dictionaries in Swift

Swift gives us plenty new abilities like (at last!) concatenating strings and even arrays. But no support for dictionaries. Is the only way to concatenate dictionaries is to overload + operation for them?
let string = "Hello" + "World" // "HelloWorld"
let array = ["Hello"] + ["World"] // ["Hello", "World"]
let dict = ["1" : "Hello"] + ["2" : "World"] // error =(
Use it like this:
Put this anywhere, e.g. Dictionary+Extension.swift:
func +<Key, Value> (lhs: [Key: Value], rhs: [Key: Value]) -> [Key: Value] {
var result = lhs
rhs.forEach{ result[$0] = $1 }
return result
}
Now your code just work
let string = "Hello" + "World" // "HelloWorld"
let array = ["Hello"] + ["World"] // ["Hello", "World"]
let dict = ["1" : "Hello"] + ["2" : "World"] // okay =)
That is not possible because there can be matching keys in the second dictionary. But you can do it manually and the values in the dictionary will be replaced in that case.
var dict = ["1" : "Hello"]
let dict2 = ["2" : "World"]
for key in dict2.keys {
dict[key] = dict2[key]
}
You also can use Swift provided functions to do the merge:
public func +<K, V>(left: [K:V], right: [K:V]) -> [K:V] {
return left.merging(right) { $1 }
}
$1 will take common keys from right dictionary, you can use $0 if you want to give priority to left dictionay.
This can be done by using for each loop and inout keyword
func mergDict(firstDict:inout [String:Any], secondDict:[String:Any]){
secondDict.forEach { (key, value) in
firstDict[key] = value
}
}
than call like
mergDict(firstDict: &firstDictToMerge, secondDict: secondDictToMerge)
you will see that your firstDictToMerge will be merged with second one without using any other variable