I am using Alamofire and SwiftyJSON.
Code:
var array = Array<Array<String>>()
Alamofire.request(.GET, url, parameters: ["postType": "live"], encoding: ParameterEncoding.URL).responseJSON { (_, _, result) in
switch result {
case .Success(let data):
let json = JSON(data)
for(_,subJSON):(String, JSON) in json["Info"] {
let Tag = subJSON["Tag"].string!
let Location = subJSON["Location"].string!
array.append(Array(count: 4, repeatedValue: concertTag))
}
case .Failure(_, let error):
print("Request failed with error: \(error)")
}
}
I want to create an array multidimensional. And add JSON variables to it for after pass the array to TableView.
So basically i need "array" to be like:
[[Tag1; Location1],[Tag2, Location2], .... ]
How can I do this? any ideas?
Thank you
You just would have to replace
array.append(Array(count: 4, repeatedValue: concertTag))
with
array.append([Tag, Location])
And you would get the data back like this for example:
for arr in array {
print("tag: " + arr[0])
print("loc: " + arr[1])
}
But I would suggest another approach: using tuples.
First let's create a type result because it's convenient:
typealias TagAndLocation = (tag: String, location: String)
Then prepare an empty array of results:
var resultTuples = [TagAndLocation]()
Fill the array of tuples in the loop:
for (_, subJSON) in json {
let Tag = subJSON["Tag"].string!
let Location = subJSON["Location"].string!
let tagloc = (tag: Tag, location: Location)
resultTuples.append(tagloc)
}
Then you can access your data in two ways:
for (tag, loc) in resultTuples {
print("tag: " + tag)
print("loc: " + loc)
}
for result in resultTuples {
print("tag: " + result.tag)
print("loc: " + result.location)
}
Note: your variables names should begin with a lowercase letter. Beginning with uppercase is usually for classes, types, protocols and such.
Related
I have an array:
let arr = ["Ivan Ivanov", "Bogdan Bogdanov", "Georgi Milchev", "Bogdan Petkov", "Vladimir Zahariev"]
let name = "Bogdan"
Search if array contains(name) and append the result to the new array without loop.
So new array have to be ["Bogdan Bogdanov", "Bogdan Petkov"]
Trying with: if arr.contains(where: {$0 == name}) { newArray.append($0) }
but it's not working. Error: Anonymous closure argument not contained in a closure
You need
let res = arr.compactMap { $0.contains(name) ? $0.components(separatedBy: " ").last! : nil }
I would like to retrieve the values from keys named "termKey" from all dictionaries in an array of dictionaries (as I want to display the values in a UITableView). Any suggestions?
Here's the array of dictionaries:
{
"questionData": [
{
"termKey": "respiration"
},
{
"termKey": "mammals"
}
]
}
This is the flattened array:
[(key: "termKey", value: "respiration"), (key: "termKey", value: "mammals")]
The output I want would be something like: ["respiration", "mammals"]
let array = [(key: "termKey", value: "respiration"), (key: "termKey", value: "mammals")]
array.map({ $0.value })
And you will get an array of the values that looks like:
["respiration", "mammals"]
Use compactMap on the array and lookup the dictionary key in the closure:
let questionData = [["termKey": "respiration"], ["termKey": "mammals"], ["badKey": "foo"]]
let values = questionData.compactMap { $0["termKey"] }
print(values)
["respiration", "mammals"]
compactMap runs its closure for each element in the array to create a new array. Here, we look up the value for the key "termKey". Dictionary lookups return an optional value. If the key is not present, the result will be nil. compactMap skips the nil values and unwraps the values that are present.
Decode the JSON into structs and map the result to the termKey values of questionData.
struct Response: Decodable {
let questionData : [Question]
}
struct Question: Decodable {
let termKey : String
}
let jsonString = """
{"questionData": [{"termKey": "respiration"},{"termKey": "mammals"}]}
"""
let data = Data(jsonString.utf8)
do {
let result = try JSONDecoder().decode(Response.self, from: data)
let termKeys = result.questionData.map{$0.termKey}
} catch { print(error) }
I have some objects in parse and I am getting the data successfully as [PFObjects]. The issue is that I am trying to add the array elements [PFObjects] to a dictionary as values. But I keep getting an empty dictionary, so the values are not added to the dictionary. The dictionary count is also 0.
This is what I tried so far:
var postDictionary = [String:[AnyObject]]()
query.findObjectsInBackground(block: { (posts: [PFObject]?, error:Error?) in
if let unwrappedPosts = posts {
for posts in unwrappedPosts {
if let postText = posts.object(forKey: "title") as?String {
self.titleArray.append(postText)
print("count", self.titleArray.count) // count 10
self.postDictionary["title"]?.append(self.titleArray as AnyObject)
**try to force unwrap **
self.postDictionary["title"]!.append(self.titleArray as AnyObject), and the app crashed
for (title, text) in self.postDictionary {
print("\(title) = \(text)")
}
print("Dictionay text count",self.postDictionary.count) // count is 0
}
}
}
})
This syntax is very confusing
self.titleArray.append(postText)
self.postDictionary["title"]?.append(self.titleArray as AnyObject)
You append a string to an array and then you are going to append the array to the array in the dictionary. I guess this is not intended.
I recommend to map the title strings and set the array for key title once
var postDictionary = [String:[String]]()
query.findObjectsInBackground(block: { (posts: [PFObject]?, error:Error?) in
if let unwrappedPosts = posts {
self.titleArray = unwrappedPosts.compactMap { $0.object(forKey: "title") as? String }
self.postDictionary["title"] = self.titleArray
for (title, text) in self.postDictionary {
print("\(title) = \(text)")
}
print("Dictionay text count",self.postDictionary.count) // count is 0
}
})
Never use AnyObject if the type is more specific.
The proper way of adding to a dictionary is using updateValue because as far as i can see that you don't have the key "title" in your dictionary and you are appending values to unknown key i guess.
This should help:
titleArray.append(postText)
postDictionary.updateValue(titleArray as [AnyObject], forKey: "title")
for (key,value) in postDictionary {
print("\(key) \(value)")
}
Finally this should print:
title [posts1, posts2, posts3]
I'm Using Swift2.0+, SwiftyJSON, Alamofire
I Got the Value let stringJSON:JSON = ["a1","a2","a3"] from Server
But If I Check stringJSON[0],Then it's null
When I debugPrint(stringJSON), Then it's ["a1","a2","a3"]
How Can I got the value stringJSON[0] //"a1" ?
Do I Have To Convert From JSON To Another?
The correct syntax is
let stringJSON:JSON = ["a1","a2","a3"]
if let firstValue = stringJSON.array?.first {
print(firstValue) // a1
}
Update
Since the value actually contains this string "[\"a1\/a1\",\"a2\/a2\",\"a3\"]" and you cannot fix this on the server side here it is a workaround.
if let words = stringJSON.string?.characters.dropFirst().dropLast().split(",").map(String.init) {
let word = String(words[0].characters.dropFirst().dropLast())
print(word) // a1
}
if let x = stringJSON[0].string{
print(x)
}
Since the server is not returning an Array, but a String, you need to convert that into an Array of Strings like this:
let string = stringJSON.string
let array = string.stringByReplacingOccurrencesOfString("[", withString: "")
.stringByReplacingOccurrencesOfString("]", withString: "")
.stringByReplacingOccurrencesOfString("\"", withString: "")
.componentsSeparatedByString(",")
stringJSONIf it is a string type, you can like this to solve:
extension String {
var parseJSONString: AnyObject? {
var any: AnyObject?
let data = self.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
do{
any = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers)
}catch let error as NSError{
print("error: \(error)")
}
return any
}
and use it like this:
let strArr = stringJSON.stringvalue.parseJSONString as! Array<String>
What woudl be a simple way to reduce a string like AAA:111;BBB:222;333;444;CCC:555 to a dictionary in Swift. I have the following code:
var str = "AAA:111;BBB:222;333;444;CCC:555"
var astr = str.componentsSeparatedByString(";").map { (element) -> [String:String] in
var elements = element.componentsSeparatedByString(":")
if elements.count < 2 {
elements.insert("N/A", atIndex: 0)
}
return [elements[0]:elements[1]]
}
The code above produces an Array of Dictionaries:
[["A": "111"], ["BBB": "222"], ["UKW": "333"], ["UKW": "444"], ["CCC": "555"]]
I want it to produce
["A": "111", "BBB": "222", "UKW": "333", "UKW": "444", "CCC": "555"]
but no mater what I try, since i call the map function on an Array it seems impossible to convert the nature of the map function's result.
NOTE: The dictionary in string format is described as either having KEY:VALUE; format or VALUE; format, in which case the mapping function will add the "N/A" as being the key of the unnamed value.
Any help on this matter is greatly appreciated.
Your map produces an array of dictionaries. When you want to combine them into 1, that's a perfect job for reduce:
func + <K,V>(lhs: Dictionary<K,V>, rhs: Dictionary<K,V>) -> Dictionary<K,V> {
var result = Dictionary<K,V>()
for (key, value) in lhs {
result[key] = value
}
for (key, value) in rhs {
result[key] = value
}
return result
}
var str = "AAA:111;BBB:222;333;444;CCC:555"
var astr = str
.componentsSeparatedByString(";")
.reduce([String: String]()) {
aggregate, element in
var elements = element.componentsSeparatedByString(":")
if elements.count < 2 {
elements.insert("N/A", atIndex: 0)
}
return aggregate + [elements[0]:elements[1]]
}
print(astr)
Swift has no default operator to "combine" two Dictionaries so you have to define one. Note that the + here is not commutative: dictA + dictB != dictB + dictA. If a key exist in both dictionaries, the value from the second dictionary will be used.
This is a work for reduce:
let str = "AAA:111;BBB:222;333;444;CCC:555"
let keyValueStrings = str.componentsSeparatedByString(";")
let dictionary = keyValueStrings.reduce([String: String]()) {
aggregate, element in
var newAggregate = aggregate
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
newAggregate[key] = value
return newAggregate
}
print(dictionary)
You can also make aggregate mutable directly:
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.
The reason this is happening is because map can only return arrays. If you are using this method to parse your string, then you need to convert it to a dictionary after.
var newDict = [String:String]()
for x in astr {
for (i, j) in x {
newDict[i] = j
}
}
The current issue with your code is that map function iterates over array containing [["key:value"],["key:value"]..] and you separate it again. But it returns ["key":"value"] which you then add to your array.
Instead you can add elements[0]:elements[1] directly to a locally kept variable which will fix your problem. Something like
finalVariable[elements[0]] = elements[1]