Swift: Remove keys and values added during array flatMap conversion - ios

Trying to make array of dictionaries with object, which adds keys and values during array flatMap
var parameters: [String: Any] {
var postParameters: [String: Any] = [:]
postParameters["topics"] = categories.flatMap({ toPostParameters($0) })
print(postParameters)
return postParameters
}
private func toPostParameters(_ topics: Topics) -> [String: Any] {
var params: [String: Any] = [:]
params["id"] = topics.id
params["isFound"] = topics.isFound
return params
}
when I print the postParameters value, all I get is like the following. How to remove the key and value strings appending over it.
["topicCategories": [(key: "isFound", value: true), (key: "id", value: "62ef2b63e49a"),
(key: "id", value: "632serer269"), (key: "isFound", value: true)]]
Expected
["topicCategories": [["isFound": true, "id": "62ef2b63e49a"],
["id": "632serer269","isFound": true]]]

You should use map, because flatMap makes everything "flat" and you get a flat array of tuples instead of array of dictionaries.
Example:
class Topics {
var id = 1
var isFound = true
}
var categories = [Topics(), Topics(), Topics()]
var parameters: [String: Any] {
var postParameters: [String: Any] = [:]
postParameters["topics"] = categories.map({ toPostParameters($0) })
return postParameters
}
private func toPostParameters(_ topics: Topics) -> [String: Any] {
var params: [String: Any] = [:]
params["id"] = topics.id
params["isFound"] = topics.isFound
return params
}
print(parameters)
Output:
["topics": [["id": 1, "isFound": true], ["id": 1, "isFound": true], ["isFound": true, "id": 1]]]
P.S:
Good article to understand the difference between map, compactMap and flatMap.

Related

Convert nested dictionary to create String Swift

I have a dictionary which contains key and items model for the values.
var selectedDic: [String: [Item]] = [:]
Actually I am trying to make a string with dictionary key and key has multiple value separated by comma.
And if I add more values to key it should add values to specific key.
price and publisher are keys and itemId is the value.
I need this string: price:10-25;publisher:576,925,1737
Dictionary Print:
[
"price": [
Babil.Item(name: Optional("10 - 25"),
itemId: Optional("10-25"))
],
"publisher": [
Babil.Item(name: Optional("ABCD"),
itemId: Optional("576")),
Babil.Item(name: Optional("DEFG"),
itemId: Optional("925")),
Babil.Item(name: Optional("HIJK"),
itemId: Optional("1737"),
)
]
]
My code:
var itemString: [String: String] = [:]
var str: [String] = []
for (key, value) in selectedDic {
value.forEach { a in
if str.firstIndex(of: key) == nil {
str.append(a.itemId!)
}
}
let final = str.joined(separator: ",")
itemString.updateValue(final, forKey: key)
}
With:
struct Item {
let name: String?
let itemId: String?
}
let dict: [String: [Item]] = ["price": [Item(name: "10-25", itemId: "10-25")],
"publisher": [Item(name: "ABCD", itemId: "576"),
Item(name: "DEFG", itemId: "925"),
Item(name: "HIJK", itemId: "1737")]]
You could use:
var keys = ["price", "publisher"]
let reduced = keys.reduce(into: [String]()) { result, current in
guard let items = dict[current] else { return }
let itemsStr = items.compactMap {$0.itemId }.joined(separator: ",")
result.append("\(current):\(itemsStr)")
}
let finalStr = reduced.joined(separator: ";")
print(finalStr)
The idea:
Iterate over the needed keys (and order guaranteed), construct for each keys, the itemsIds list joined by "," and then append that with the key.
Finally, joined all that.
Add-on questions:
Why is name and itemId optional? Is that normal?
Site note: giving the first part (easily reproducible input) can increase the change of answers, so we don't have to recreate ourselves fake data to check our answers.
Try this:
The order of the keys can be different since it is a dictionary and order isn't signficant in a dict
print(
yourDict.map { (key, values) -> String in
"\(key): \(values.compactMap { $0.itemId }.joined(separator: ","))"
}.joined(separator: ";")
)

Create Dictionary from Array in Swift 5

I need to create a dictionary from array with custom type for first index of the array.
Sample array : ["ABC","ZYZ","123"]
Required result : [{"name" : "ABC", "type:"A"},{"name" : "ZYZ", "type:"B"},{"name" : "123", "type:"B"}]
Note type A for first index.
My code
for url in urlArray {
urlDict["name"] = url
}
You can do a map, and then individually change the type of the first dictionary:
var dicts = urlArray.map { ["name": $0, "type": "B"] }
dicts[0]["type"] = "A"
Seeing how all your dictionary keys are all the same, and that you are sending this to a server, a Codable struct might be a better choice.
struct NameThisProperly : Codable {
var name: String
var type: String
}
var result = urlArray.map { NameThisProperly(name: $0, type: "B") }
result[0].type = "A"
do {
let data = try JSONDecoder().encode(result)
// you can now send this data to server
} catch let error {
...
}
I suppose you can use a high order function such as map or reduce
Here is an example using reduce
var array = ["ABC","ZYZ","123"]
var result = array.reduce([[String: String]](), { (previous, current) -> [[String: String]] in
let type = previous.count == 0 ? "A" : "B"
let dictForCurrent = [
"name": current,
"type": type
]
return previous + [dictForCurrent]
})
print(result)
The result:
[["type": "A", "name": "ABC"], ["type": "B", "name": "ZYZ"], ["name":
"123", "type": "B"]]
Use reduce to convert array to dictionary:
let resultDict: [String: String]
= array.reduce(into: [:]) { dict, url in
dict["name"] = url
}
The result will look like:
[
"name": URL1,
"name": URL2
]
Use map(_:) to convert each element of the array to dictionary like so,
let arr = ["ABC","ZYZ","123"]
let result = arr.map { (element) -> [String:String] in
var dict = [String:String]()
dict["name"] = element
if let char = element.first {
dict["type"] = String(char)
}
return dict
}
print(result)
since you are concern about the index, my approach will be using enumerated() which gives out the index
let array = ["ABC","ZYZ","123"]
var results: [[String: String]] = []
for (i, content) in array.enumerated() {
let type: String = i == 0 ? "A" : "B"
results.append(["name": content, "type": type])
}
print(result)
// [["type": "A", "name": "ABC"], ["name": "ZYZ", "type": "B"], ["type": "B", "name": "123"]]

Create specific Json in Swift 4

I have to deliver some specific JSON to a webserver, but I'm very confused about how to use the correct arrays and dictionaries, so I can't get to my final result. I'm starting from this:
["entries": [
"member" : [
"id" : key,
"name" : value
],
"timestamp" : "\(dateString)"
]
]
in the JSON file I have to send to the server I can only have 1 "entries" and several "member" with specific "id" and "name" and timestamp. I'm aware that I have to use dictionaries but can't get to this result. This is what I tried so fare:
var leden = [Int: String]()
var jsonDict = [String: Any]()
var dateString = Date()
for (key, value) in leden{
jsonDict["\(key)"] = ["member" : [
"id" : key,
"naam" : value
],
"timestamp" : "\(dateString)"
]
}
jsonFinaal = ["entries": jsonDict]
this is as close as I could get, but good enough because the key in jsonDict should not be there, I added the "entries" in a second dictionary to make this a unique entry.
Anyone who can do beter to help me to the solution?
The value for members is an array of dictionaries, not a dictionary
This creates the structure sorted by the id keys
let leden = [1 : "Foo", 2: "Bar"]
let sortedKeys = leden.keys.sorted()
let dateString = Date()
var members = [[String:Any]]()
for key in sortedKeys {
let naam = leden[key]!
members.append(["member" : ["id" : key, "naam" : naam], "timestamp" : "\(dateString)"])
}
let entries = ["entries": members]
print(entries)
And the JSON string with
do {
let jsonData = try JSONSerialization.data(withJSONObject: entries)
let json = String(data: jsonData, encoding: .utf8)
print(json)
} catch { print(error) }

Swift 2.2 - the code getting crashed unless I turn off optimization (-Onone)

This is on XCode 7.3.
If I build the code with optimization flag -O, it will be crashed. It is working properly without optimization (flag -Onone). However I want to have my code built with optimization.
Is it Swift compiler bug?
This is the code.
import Foundation
extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
func multipartFormParams(preFix preFix: String = "") -> [String: AnyObject] {
var params: [String: AnyObject] = [:]
for (key, value) in self {
let subKey = preFix != "" ? "\(preFix)[\(key)]" : String(key)
if let value = value as? [String: AnyObject] {
// params += value.multipartFormParams(preFix: subKey) // this is my original code, however remove the operation extension += to focus on the bug.
value.multipartFormParams(preFix: subKey) // It crash here.
} else {
params[subKey] = value
}
}
return params
}
}
let parameters: [String: AnyObject] = [
"user": [
"first_name": "first",
"last_name": "last"
]
]
parameters.multipartFormParams()
After a while, I found that this is related to this bug: https://bugs.swift.org/browse/SR-1114, change for (key, value) in self { to forEach { key, value in solved the problem.
Full working version:
import Foundation
extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
func multipartFormParams(preFix preFix: String = "") -> [String: AnyObject] {
var params: [String: AnyObject] = [:]
forEach { key, value in
let subKey = preFix != "" ? "\(preFix)[\(key)]" : String(key)
if let value = value as? [String: AnyObject] {
// params += value.multipartFormParams(preFix: subKey) // this is my original code, however remove the operation extension += to focus on the bug.
value.multipartFormParams(preFix: subKey) // It crash here.
} else {
params[subKey] = value
}
}
return params
}
}
let parameters: [String: AnyObject] = [
"user": [
"first_name": "first",
"last_name": "last"
]
]
parameters.multipartFormParams()

How to build a URL by using Dictionary in Swift

So I am messing around with the iMessages Framework, and in order to send data to another user the data must be sent as a URLComponents URL. However I can't figure out how to send a dictionary to use as the messages.url value.
func createMessage(data: dictionary) {
/*
The Dictionary has 3 values, 1 string and two arrays.
let dictionary = ["title" : "Title Of Dictionary", "Array1" : [String](), "Array2" : [String]() ] as [String : Any]
*/
let components = URLComponents()
let layout = MSMessageTemplateLayout()
layout.image = UIImage(named: "messages-layout.png")!
layout.imageTitle = "\(dictionary["title"])"
let message = MSMessage()
message.url = components.url!
message.layout = layout
self.activeConversation?.insert(message, completionHandler: { (error: Error?) in
print("Insert Message")
})
}
Does anybody know how I can send the dictionary values as URLQueryItems in the URLComponents to save as the message.url ?
PS: I was wondering wether it would be possible to create an extension for the URL to store the dictionary in, that is what I am unsuccessfully trying atm.
Here is a code snippet to convert dictionary to URLQueryItems:
let dictionary = [
"name": "Alice",
"age": "13"
]
func queryItems(dictionary: [String:String]) -> [URLQueryItem] {
return dictionary.map {
// Swift 3
// URLQueryItem(name: $0, value: $1)
// Swift 4
URLQueryItem(name: $0.0, value: $0.1)
}
}
var components = URLComponents()
components.queryItems = queryItems(dictionary: dictionary)
print(components.url!)
You can use the following URLComponents extension:
extension URLComponents{
var queryItemsDictionary : [String:Any]{
set (queryItemsDictionary){
self.queryItems = queryItemsDictionary.map {
URLQueryItem(name: $0, value: "\($1)")
}
}
get{
var params = [String: Any]()
return queryItems?.reduce([:], { (_, item) -> [String: Any] in
params[item.name] = item.value
return params
}) ?? [:]
}
}
}
And set the url components:
var url = URLComponents()
url.queryItemsDictionary = ["firstName" : "John",
"lastName" : "Appleseed"]
You can use iMessageDataKit library. It's a tiny, useful MSMessage extension for setting/getting Int, Bool, Float, Double, String and Array values for keys.
It makes setting and getting data really easy like:
let message: MSMessage = MSMessage()
message.md.set(value: 7, forKey: "user_id")
message.md.set(value: "john", forKey: "username")
message.md.set(values: ["joy", "smile"], forKey: "tags")
print(message.md.integer(forKey: "user_id")!)
print(message.md.string(forKey: "username")!)
print(message.md.values(forKey: "tags")!)
(Disclaimer: I'm the author of iMessageDataKit)

Resources