How to convert json into dictionary for POST api call Swift
info contains an array of object that have User and Address keys You need
let dict1 : [String:Any] = ["ID" : "User123", "Name" : "Virat", "UserID" : 0]
let dict2 : [String:Any] = ["city" : "Delhi", "pin" : 123]
let addressDict : [String:Any] = ["User" : dict1,"Address" : dict2]
let infoDict : [String:Any] = ["info" :[addressDict]]
To better understand look to
// MARK: - Empty
struct Empty: Codable {
let info: [Info]
}
// MARK: - Info
struct Info: Codable {
let user: User
let address: Address
enum CodingKeys: String, CodingKey {
case user = "User"
case address = "Address"
}
}
// MARK: - Address
struct Address: Codable {
let city: String
let pin: Int
}
// MARK: - User
struct User: Codable {
let id, name: String
let userID: Int
enum CodingKeys: String, CodingKey {
case id = "ID"
case name = "Name"
case userID = "UserID"
}
}
You can also use the models above and convert the model to Data with JSONEncodable
The issue is that you're adding user and address as separate dictionaries on the array.
The JSON you posted has an array for info key but its ONE dictionary with two keys.
You need to combine the address and user dicts into one declaration and then wrap that in an array.
For ex:
let dict1 : [String:Any] = ["ID" : "User123", "Name" : "Virat", "UserID" : 0]
let dict2 : [String:Any] = ["city" : "Delhi", "pin" : 123]
let dicts : [String:Any] = ["User": dict1, "Address" : dict2]
let arr = [dicts]
let infoDict : [String:Any] = ["info" : arr]
EDIT: I would agree with SH_Khan that a much better way of doing this would be to create a Codable model
The issue is you are make dictionary per object.
try this code:
let userDict : [String:Any] = ["ID" : "User123", "Name" : "Virat", "UserID" : 0]
let addressDict : [String:Any] = ["city" : "Delhi", "pin" : 123]
let infoDict : [String:Any] = ["info" : ["User": addressDict, "Address": addressDict]]
Try this:
func getDictionaryFromString(_ json: String) -> [String: Any]? {
if let data = json.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
override func viewDidLoad() {
super.viewDidLoad()
let json = """
{"info": [{
"User": {"ID": "user123",
"Name": "Virat",
"UserID": 0
},
"Address": {"city": "Delhi",
"pin": 123
}
}
]
}
"""
if let dict = getDictionaryFromString(json) {
print(dict["info"] as! NSArray)
}
}
//Printed:
> (
> {
> Address = {
> city = Delhi;
> pin = 123;
> };
> User = {
> ID = user123;
> Name = Virat;
> UserID = 0;
> };
> } )
Related
I have array of dictionaries.
>[{"name": "John",
"address":
{"home": "addr1",
"work": "add2"}
},
{"name": "Anu",
"address": {"home": "addr1",
"work": "add2"}
}]
I am saving it to user default like this -
let personsData1 = ["name": "John", "address": {"home": "addr1", "work": "add2"}] as [String : Any]
let personsData2 = ["name": "Anu", "address": {"home": "addr1", "work": "add2"}] as [String : Any]
var persons = [personsData, personsData1]
UserDefaults.standard.set(forKey: "persons")
Retrieving it in another method and filter them on the basis of name.
let name = "John"
Getting below error
Cannot invoke 'filter' with an argument list of type '((Any?) -> Bool)'
Here is the code :-
func test () {
let personData1 = ["name": "John", "addresses": ["home":"addr1", "work": "addr2"]] as [String : Any]
let personData2 = ["name": "And", "addresses": ["home":"addr1", "work": "addr2"]] as [String : Any]
let persons = [personData1, personData2]
(UserDefaults.standard.set(persons, forKey: "persons")
print("Saved ----\(UserDefaults.standard.value(forKey: "persons"))")
if let savedPersons = UserDefaults.standard.value(forKey: "persons") {
let namePredicate = NSPredicate(format: "name like %#", name);
var filteredArray: [[String:Any]] = savedPersons.filter { namePredicate.evaluate(with: $0) }
print("names = \(filteredArray)")
}
}
If I try to filter like this -
let filteredArray = savedBrs.filter { $0["name"] == name }
getting different error -
Value of type 'Any' has no member 'filter'
With NSPredicate
let arr = [["name":"Rego","address":["one":"peek","two":"geelo"]],["name":"pppp","address":["one":"peek","two":"geelo"]]]
let neededName = "Rego"
let pre = NSPredicate(format: "name == %#",neededName)
let result = arr.filter { pre.evaluate(with:$0) }
print(result)
Without NSPredicate
let result = arr.filter { $0["name"] as? String == neededName }
i am trying to parse JSON into an array. Sort it according the highest appointment made. And then use that new array to populate it on leaderboard tableview.
I am using SwiftJson
Stuck on sorting into array
Would need to populate Name and the rest of the values in tableview in descending order.
Here are my snippets.
let jsonUrl = URL(string: url)
URLSession.shared.dataTask(with: jsonUrl!) { (data, response, error) in
guard let data = data else { return }
let jsonResult : JSON = JSON(data)
print(jsonResult)
} .resume()
Here are the output
{
"Activities" : {
"AHiHr9bzGXcN7pxvR68wulD9zqE3" : {
"Case Closed" : "2",
"Name" : "Tim Mac",
"Appointment Made" : "2",
"Prospects Met" : "2",
"Policy Servicing" : "2"
},
"gDKBlbeMsiUUFaASOLn6eOdCIrJ3" : {
"Case Closed" : "1",
"Name" : "Jane Simpson",
"Appointment Made" : "1",
"Prospects Met" : "1",
"Policy Servicing" : "1"
},
"W8uWoLf9qRX4a9BgXjLw5VZXjFu1" : {
"Case Closed" : "3",
"Name" : "John Doe",
"Appointment Made" : "4",
"Prospects Met" : "3",
"Policy Servicing" : "2"
}
}
}
you can get all values as Array of Dictionary and Sort it Like :
guard let Activities = jsonResult?["Activities"] as? [String:AnyObject] else {
return
}
var values = [Dictionary<String, AnyObject>]()
for (_, value) in Activities {
values.append(value as! Dictionary<String, AnyObject>)
}
let sorted = values.sorted { (dic1, dic2) -> Bool in
Int(dic1["Appointment Made"] as? String ?? "") ?? 0 > Int(dic2["Appointment Made"] as? String ?? "") ?? 0
}
print(sorted)
// model your data
for item in sorted {
let model = Model.init(jsonData: item)
// use model
}
// your model
class Model: NSObject {
var caseClosed :Int?
var name :String?
var appointmentMade :Int?
var prospectsMet :Int?
var policyServicing :Int?
override init() {
super.init()
}
init(jsonData : [String:AnyObject]) {
// map data to object
}
}
Use JsonSerialisation jsonObjectWithData to convert jSon response to NSArray object. And run a for loop, access every element in array as NSDictionary and compare their values for highest appointment.
To know more about accessing values from NSDictionary, apple docs reference
To know more about working with json in swift, apple docs reference.
I have tried this sorting:
let activitiesDict = jsonData?["Activities"] as? [String:Any]
let activitiesArray = activitiesDict.map({ [$0.0 : $0.1] })
let sortedActivitiesArray = self.activitiesArray.sorted {
(Int((($0 as! Dictionary<String, Any>)["Appointment Made"] as? String)!))! > (Int((($1 as! Dictionary<String, Any>)["Appointment Made"] as? String)!))!
}
print(sortedActivitiesArray)
Hope it helps.
I am using Swift with Firebase and I am a little bit confused with this error : Could not cast value of type '__NSDictionaryM' (0x122ab6130) to 'Repeat.Expression' (0x1100004c0).
Here is a sample of the JSON file I use :
{
"levels" : {
"level1" : {
"coverImage" : "lvl1",
"expressions" : [ {
"expression" : "Yes",
"id" : 0
}, {
"expression" : "Yes",
"id" : 1
}, {
"expression" : "Yes",
"id" : 2
}, {
"expression" : "Yes",
"id" : 3
} ],
"id" : 0,
"title" : "Essentiel"
},
"level2" : {
...
},
}
}
Here are the two models I use :
struct Level {
let id : Int
let coverImage : String
let title : String
let expressions : [Expression]
}
struct Expression {
let id : Int
let expression : String
}
Finally, here is the function I use to fetch the levels :
var levels = [Level]()
func fetchLevels() {
FIRDatabase.database().reference().child("levels").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
if let levelId = dictionary["id"], let levelCoverImage = dictionary["coverImage"], let levelTitle = dictionary["title"], let levelExpressions = dictionary["expressions"] {
let level = Level(
id: levelId as! Int,
coverImage: levelCoverImage as! String,
title: levelTitle as! String,
expressions: levelExpressions as! [Expression]
)
self.levels.append(level)
}
DispatchQueue.main.async{
self.collectionView?.reloadData()
}
}
}, withCancel: nil)
}
It appears that the problem is at the line expressions: levelExpressions as! [Expression]
Thank you very much for your help.
Have a good day.
let levelExpressions = dictionary["expressions"]
The above line returns array of dictionaries i.e.[[String:Any]] and needs to require that value to be mapped into your struct Expression.This can be done in 2 ways:-
1) You can use ObjectMapper to typecast the return value of above code.
2) Manually allocate the Expression object by parsing the return values of
let levelExpressions = dictionary["expressions"] as! [[String:Any]] and assigning them to properties id and expression
I'm pulling an array of dictionaries straight from Parse and displaying them in a table. So I'd really like to work with the data structure I'm handed (the oddly structured dictionaries below).
A PFObject is [String : AnyObject?] and I want to be able to sort by any key so I don't know the object type AND the key might be missing from some of the dictionaries. Because in Parse, if you don't give a property a value, it is simply nonexistent. For example:
[
{
"ObjectId" : "1",
"Name" : "Frank",
"Age" : 32
},
{
"ObjectId" : "2",
"Name" : "Bill"
},
{
"ObjectId" : "3",
"Age" : 18
}
{
"ObjectId" : "4",
"Name" : "Susan",
"Age" : 47
}
]
I want the dictionaries with missing keys to always be ordered after the sorted dictionaries. An example:
Original Table:
ObjectId Name Age
1 Frank 32
2 Bill
3 18
4 Susan 47
Ordered By Name:
ObjectId Name Age
2 Bill
1 Frank 32
4 Susan 47
3 18
As I don't have a lot of control over the data model, and it's usage is limited throughout the application, I'd prefer to focus on an algorithmic solution rather than structural.
I came up with a way to do this but it just seems inefficient and slow, I'm certain there's someone who can do this better.
//dataModel is an array of dictionary objects used as my table source
//sort mode is NSComparisonResult ascending or descending
//propertyName is the dictionary key
//first filter out any objects that dont have this key
let filteredFirstHalf = dataModel.filter({ $0[propertyName] != nil })
let filteredSecondHalf = dataModel.filter({ $0[propertyName] == nil })
//sort the dictionaries that have the key
let sortedAndFiltered = filteredFirstHalf { some1, some2 in
if let one = some1[propertyName] as? NSDate, two = some2[propertyName] as? NSDate {
return one.compare(two) == sortMode
} else if let one = some1[propertyName] as? String, two = some2[propertyName] as? String {
return one.compare(two) == sortMode
} else if let one = some1[propertyName] as? NSNumber, two = some2[propertyName] as? NSNumber {
return one.compare(two) == sortMode
} else {
fatalError("filteredFirstHalf shouldn't be here")
}
}
//this will always put the blanks behind the sorted
dataModel = sortedAndFiltered + filteredSecondHalf
Thanks!
Swift can't compare any two objects. You have to cast them to a specific type first:
let arr: [[String: Any]] = [
["Name" : "Frank", "Age" : 32],
["Name" : "Bill"],
["Age" : 18],
["Name" : "Susan", "Age" : 47]
]
let key = "Name" // The key you want to sort by
let result = arr.sort {
switch ($0[key], $1[key]) {
case (nil, nil), (_, nil):
return true
case (nil, _):
return false
case let (lhs as String, rhs as String):
return lhs < rhs
case let (lhs as Int, rhs as Int):
return lhs < rhs
// Add more for Double, Date, etc.
default:
return true
}
}
print(result)
If there are multiple dictionaries that have no value for the specified key, they will be placed at the end of the result array but their relative orders are uncertain.
Requirements
So you have an array of dictionaries.
let dictionaries: [[String:AnyObject?]] = [
["Name" : "Frank", "Age" : 32],
["Name" : "Bill"],
["Age" : 18],
["Name" : "Susan", "Age" : 47]
]
You want to sort the array:
with the Name value ascending
dictionaries without a Name String should be at the end
Solution
Here's the code (in functional programming style)
let sorted = dictionaries.sort { left, right -> Bool in
guard let rightKey = right["Name"] as? String else { return true }
guard let leftKey = left["Name"] as? String else { return false }
return leftKey < rightKey
}
Output
print(sorted)
[
["Name": Optional(Bill)],
["Name": Optional(Frank), "Age": Optional(32)],
["Name": Optional(Susan), "Age": Optional(47)],
["Age": Optional(18)]
]
Make a datatype to represent your data:
struct Person
{
let identifier: String
let name: String?
let age: Int?
}
Make an extraction routine:
func unpack(objects: [[String : Any]]) -> [Person]
{
return objects.flatMap { object in
guard let identifier = object["ObjectID"] as? String else {
// Invalid object
return nil
}
let name = object["Name"] as? String
let age = object["Age"] as? Int
return Person(identifier: identifier, name: name, age: age)
}
}
Your datatype can be sorted by its fields, because they have real types.
let objects: [[String : Any]] =
[["ObjectID" : "1", "Name" : "Frank", "Age" : 32],
["ObjectID" : "2", "Name" : "Bill"],
["ObjectID" : "3", "Age" : 18],
["ObjectID" : "4", "Name" : "Susan", "Age" : 47]]
let persons = unpack(objects)
let byName = persons.sort { $0.name < $1.name }
nils compare as "before" any other value; you can write your own comparator if you'd like to change that.
Here is what I would do. If you are able to, I would make the struct more specific by giving it a Name and Age other than just key and value. This should give you an outline for how to achieve that though!
struct PersonInfo {
var key: String!
var value: AnyObject?
init(key key: String, value: AnyObject?) {
self.key = key
self.value = value
}
}
class ViewController: UIViewController {
var possibleKeys: [String] = ["Name", "Age", "ObjectId"]
var personInfos: [PersonInfo] = []
override func viewDidLoad() {
super.viewDidLoad()
for infos in json {
for key in possibleKeys {
if let value = infos[key] {
personInfos.append(PersonInfo(key: key, value: value))
}
}
}
personInfos.sortInPlace({$0.value as? Int > $1.value as? Int})
}
}
to make it easier, here:
struct PersonInfo {
var key: String!
var objectId: Int!
var name: String?
var age: Int?
init(key key: String, objectId: Int, name: String?, age: Int?) {
self.key = key
self.objectId = objectId
self.name = name
self.age = age
}
}
class ViewController: UIViewController {
var possibleKeys: [String] = ["Name", "Age", "ObjectId"]
var personInfos: [PersonInfo] = []
override func viewDidLoad() {
super.viewDidLoad()
for infos in json {
var objectId: String!
var name: String? = nil
var age: Int? = nil
for key in possibleKeys {
if let value = infos[key] {
if key == "ObjectId" {
objectId = value as? String
}
if key == "Name" {
name = value as? String
}
if key == "Age" {
age = value as? Int
}
}
}
personInfos.append(PersonInfo(key: key, objectId: objectId, name: String?, age: Int?))
}
//by objectId
personInfos.sortInPlace({$0.objectId? > $1.objectId?})
//by age
personInfos.sortInPlace({$0.age? > $1.age?})
//byName
personInfos.sortInPlace({$0.name?.compare($1.name?) == NSComparisonResult.OrderedAscending})
}
}
I have an object taken from Parse and I want to save its columns into a Dictionary or something else (if it's better).
I want to have a Dictionary like this: ["name" : "Mike", "lastname" : "vorisis", "id" : "advsas"]
Below is the code I use to take my results:
func queryEvents() {
let query = PFQuery(className: "eventController")
query.limit = 1000
query.includeKey("idEvent")
query.includeKey("eventType")
query.includeKey("idEvent.idMagazi")
query.findObjectsInBackgroundWithBlock { (objects, error)-> Void in
if let objects = objects {
for object in objects {
var post = object["idEvent"] as? PFObject
var post2 = post!["idMagazi"]
print("retrieved related post: \(post2["name"]!)")
}
}
}
}
Something else (if it's better) is a custom class.
Change the type of idMagazi to the real type.
class Event {
let post : PFObject
let name : String
let idMagazi : String
init(object : PFObject) {
self.post = object
self.name = object["name"] as! String
self.idMagazi = object["idMagazi"] as! String
}
}
And use it
...
if let objects = objects as? [PFObject] {
var events = [Event]()
for object in objects {
let post = Event(object: object)
events.append(post)
print("retrieved related post: \(post.name)")
}
}
...
In Swift Dictionary<T,V> is equivalent to [T: V]. Type is inferred if not explicitly declared.
Empty dictionary creation (all equivalent):
var dict1: Dictionary<String, String> = [:]
var dict2: [String: String] = [:]
var dict3 = Dictionary<String, String>()
var dict4 = [String: String]()
Dictionary with values (all equivalent):
var dict5 = ["Foo": "Bar", "Foo1": "Bar1", "Foo2": "Bar2"]
var dict6: Dictionary<String, String> = ["Foo": "Bar", "Foo1": "Bar1", "Foo2": "Bar2"]
var dict7: [String: String] = ["Foo": "Bar", "Foo1": "Bar1", "Foo2": "Bar2"]
Add values to an existing dictionary:
dict["Foo"] = "Bar"
In your specific scenario, you could use this:
let dict = ["name" : name, "lastname" : lastname , "id" : id]
where name, lastname and id are String variables.
Update based on your own answer:
Having this struct:
struct Event {
var nameEvent: String
var nameMagazi: String
}
You can use this approach, that avoid having an external index and uses an array instead of a dictionary for storing the results.
var events: [Event]?
guard let objects = objects else { return }
events = objects.map { object in
let post = object["idEvent"] as? PFObject
let post2 = post!["idMagazi"] as? PFObject
let nameEvent = post!["name"] as! String
let idEvent = post?.objectId
let nameMagazi = post2!["name"] as! String
return Event(nameEvent: nameEvent , nameMagazi: nameMagazi)
}
I finally found it out how can i do it.
I use a struct with what I want like this:
var userDictionary = [Int : Event]()
struct Event {
var nameEvent: String
var nameMagazi: String
}
And then i use this:
if let objects = objects {
for object in objects {
let post = object["idEvent"] as? PFObject
let post2 = post!["idMagazi"] as? PFObject
let nameEvent = post!["name"] as! String
let idEvent = post?.objectId
let nameMagazi = post2!["name"] as! String
self.events[self.i] = Event(nameEvent: nameEvent , nameMagazi: nameMagazi)
self.i += 1
}
print(self.events[1]!.nameEvent)
}
Thank you all for your answers!
Create dictionary like this:
var dict = ["name" : "Mike", "lastname" : "vorisis" , "id" : "advsas"]
OR
var dict = Dictionary<String,String>
dict.setValue("Mike", forKey: "name")
A dictionary can be made using this line:
var dictionaryArray: [Dictionary<String,String,Int>] = []
Then values can be added by appending them to the dictionary.
dictionaryArray.append(["name" : "Mike", "lastname" : "vorisis" , "id" : "advsas"])
Hope this helps.