Related
I am getting a response in this format
[
"1234": {
"startDate": "1536278400",
"endDate": "1536796800",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 1
},
"adityaKumar": {
"startDate": "1536364800",
"endDate": "1540080000",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 6
},
"madhu60": {
"startDate": "1539388800",
"endDate": "1539475200",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 2
}
]
This response is of type [string:Any]. I have to sort the data using the playerRank property. I want the output to be of type [string:Any].
How is this possible in Swift?
A dictionary is unsorted
From swift documentation
A dictionary stores associations between keys of the same type and values of the same type in an collection with no defined ordering.
However, you can use the .sorted method on your dictionary which will give you an array of dictionaries (arrays are sortable).
let sorted = dictionary.sorted(by: { ($0.value["playerRank"]) < ($1.value["playerRank"]) })
From your original example sorted might look like this
[
(
"key": "madhu60",
"value": {
"startDate": "1539388800",
"endDate": "1539475200",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 2
}
)
....
]
Once you parse that JSON, you'll get a dictionary of type [String: [String:Any]], i.e.
let data = [
"1234": [
"startDate": "1536278400",
"endDate": "1536796800",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 1
],
"adityaKumar": [
"startDate": "1536364800",
"endDate": "1540080000",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 6
],
"madhu60": [
"startDate": "1539388800",
"endDate": "1539475200",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 2
]
]
You can sort this dictionary simply using sorted method, i.e.
let sortedData = data.sorted {
if let rank1 = $0.value["playerRank"] as? Int, let rank2 = $1.value["playerRank"] as? Int
{
return rank1 < rank2
}
return false
}
And you are good to go.
I show you a complete answer here.
First convert the response text to a qualified JSON Data.
Second build a customized structure to decode JSON Data to.
Third sort the structure
Last to print back to original response text (You may use a string to have the print result.)
First:
let json = """
[
"1234": {
"startDate": "1536278400",
"endDate": "1536796800",
"playerRank": 4,
"performance": 100,
"isProfit": false,
"members": 1
},
"adityaKumar": {
"startDate": "1536364800",
"endDate": "1540080000",
"playerRank": 2,
"performance": 100,
"isProfit": false,
"members": 6
},
"madhu60": {
"startDate": "1539388800",
"endDate": "1539475200",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 2
}
]
""".replacingOccurrences(of: "[", with: "{").replacingOccurrences(of: "]", with: "}").data(using: .utf8)
second:
struct Response {
struct ResponseData {
let name : String
let startDate : String
let endDate : String
let performance : Int
let playerRank : Int
let isProfit : Bool
let members : Int
}
var responseData: [ResponseData]
init (responseData: [ResponseData] = []){
self.responseData = responseData
}
}
extension Response: Decodable {
struct DataKey: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int? { return nil }
init?(intValue: Int) { return nil}
static let startDate = DataKey(stringValue: "startDate")!
static let endDate = DataKey(stringValue: "endDate")!
static let performance = DataKey(stringValue: "performance")!
static let playerRank = DataKey(stringValue: "playerRank")!
static let isProfit = DataKey(stringValue: "isProfit")!
static let members = DataKey(stringValue: "members")!
}
public init(from decoder: Decoder) throws {
var responseData = [ResponseData]()
let container = try decoder.container(keyedBy: DataKey.self)
for key in container.allKeys {
let dataContainer = try container.nestedContainer(keyedBy: DataKey.self, forKey: key)
let startDate = try dataContainer.decode(String.self, forKey: .startDate)
let endDate = try dataContainer.decode(String.self, forKey: .endDate)
let performance = try dataContainer.decode(Int.self, forKey: .performance)
let playerRank = try dataContainer.decode(Int.self, forKey: .playerRank)
let isProfit = try dataContainer.decode(Bool.self, forKey: .isProfit)
let members = try dataContainer.decode(Int.self, forKey: .members)
let each = ResponseData.init(name: key.stringValue, startDate: startDate, endDate: endDate, performance: performance, playerRank: playerRank, isProfit: isProfit, members: members)
responseData.append(each)
}
self.init(responseData: responseData)
}
}
let decoder = JSONDecoder()
var decodedStore = try decoder.decode(Response.self, from: json!)
third:
decodedStore.responseData.sort{$0.playerRank < $1.playerRank }
last:
print ("[")
for each in decodedStore.responseData {
print ("""
\(each.name): {
"startDate": \(each.startDate),
"endDate": \(each.endDate),
"playerRank": \(each.playerRank),
"performance": \(each.performance),
"isProfit": \(each.isProfit),
"members": \(each.members)
}
""")
}
print ("]\n")
I have 2 array of dictionaries. I want to write a function which compare these 2 arrays.
Function should return true only if main array contains sub array element.
Else it should return false.
Here is my logic-
let mainArry = [ ["id":"1","products":["pid": 1, "name": "A", "price": "$5"]], ["id":"3","products":["pid": 3, "name": "B", "price": "$1"]], ["id":"2","products":["pid": 14, "name": "C", "price": "$15"]]]
let array1 = [ ["id":"1","products":["pid": 1, "name": "A", "price": "$5"]], ["id":"3","products":["pid": 3, "name": "B", "price": "$1"]]]
let array2 = [ ["id":"1","products":["pid": 1, "name": "A", "price": "$5"]], ["id":"3","products":["pid": 4, "name": "B", "price": "$1"]]]
func compareDictionary(mainArry:[[String: Any]], arr2: [[String: Any]])-> Bool{
let itemsId = arr2.map { $0["id"]! } // 1, 3, 14
let filterPredicate = NSPredicate(format: "id IN %#", itemsId)
let filteredArray = mainArry.filter{filterPredicate.evaluate(with:$0) }
if filteredArray.count != arr2.count {
return false
}
for obj in filteredArray {
let prd = obj as Dictionary<String, Any>
let str = prd["id"] as! String
let searchPredicate = NSPredicate(format: "id == %#", str )
let filteredArr = arr2.filter{searchPredicate.evaluate(with:$0) }
if filteredArr.isEmpty {
return false
}
if !NSDictionary(dictionary: obj["products"] as! Dictionary<String, Any>).isEqual(to: filteredArr.last!["products"] as! [String : Any]) {
return false
}
}
return true
}
let result1 = compareDictionary(mainArry: mainArry, arr2: array1)
let result2 = compareDictionary(mainArry: mainArry, arr2: array2)
print("Result1 = \(result1)") // true
print("Result2 = \(result2)") //false
It is working. But I want to know the best way to achieve this.
Instead of using for-loop for comparision.
I want to use filter like this
let arrayC = filteredArray.filter{
let dict = $0
return !arr2.contains{ dict == $0 }
}
if arrayC is empty that means both arrays are equal.
I got it Finally!
We don't need to write big function.
let mainArry = [ ["id":"1","products":["pid": 1, "name": "A", "price": "$5"]], ["id":"3","products":["pid": 3, "name": "B", "price": "$1"]], ["id":"2","products":["pid": 14, "name": "C", "price": "$15"]]]
let array1 = [ ["id":"1","products":["pid": 1, "name": "A", "price": "$5"]], ["id":"3","products":["pid": 3, "name": "B", "price": "$1"]]]
let array2 = [ ["id":"1","products":["pid": 1, "name": "A", "price": "$5"]], ["id":"3","products":["pid": 4, "name": "B", "price": "$1"]]]
let result = array2.filter{
let dict = $0
return !mainArry.contains{
return NSDictionary(dictionary: dict).isEqual(to: $0)
}
}
if result.isEmpty {
print("Same key values")
} else {
print("Diff key values")
}
i will build a UICollectionView with sections.
The sections are based on the return value from json.category.
the json format is like:
[{"id":"1",
"name":"Apple",
"category":"Fruits"},
{"id":"2",
"name":"Pie",
"category":"Fruits"},
{"id":"3",
"name":"Tomato",
"category":"Vegetable"}]
I need a array filter hat the array is something like: (for sectionsItems and sectionNames)
CategorieNames[STRING] = ["Fruits","Vegetable"] // the section names from json.category
Fruits = [STRING] = ["Apple","Pie"]
Vegetables = [STRING] = ["Tomato"]
Categories.append[Fruits]
Categories.append[Vegetables]
Categories[[STRING]] = [[Fruits],[Vegetable]]
Try bellow code.
let arrData = [["id": "1",
"name": "Apple",
"category": "Fruit"],
["id": "2",
"name": "Pie",
"category": "Fruit"],
["id": "3",
"name": "Tomato",
"category": "Vegetable"]]
let categorieNames = Array(Set(arrData.map({$0["category"]!})))
var arrResult:[[String]] = []
for i in 0..<categorieNames.count {
let categories = arrData.filter({$0["category"] == categorieNames[i]}).map({$0["name"]!})
arrResult.append(categories)
}
print("result : \(arrResult)")
result : [["Apple", "Pie"], ["Tomato"]]
you can do it as follows:
let arrData = [["id": "1",
"name": "Apple",
"category": "Fruit"],
["id": "2",
"name": "Pie",
"category": "Fruit"],
["id": "3",
"name": "Tomato",
"category": "Vegetable"]]
var categorys = [[String]]()
var fruits = [String]()
var vegetable = [String]()
for data in arrData {
if let category = data["category"] {
if category == "Fruit"{
if let aFruit = data["name"] {
fruits.append(aFruit)
}
}
else if category == "Vegetable" {
if let aVeggeie = data["name"] {
vegetable.append(aVeggeie)
}
}
}
}
categorys.append(fruits)
categorys.append(vegetable)
I have the following JSON:
{
"_embedded": {
"modifier_groups": [
{
"_embedded": {
"options": [
{
"id": "8kT9KTX7",
"name": "Perfect",
"open": false,
"pos_id": "8kT9KTX7",
"price_per_unit": 0
},
{
"id": "zRcEkcj8",
"name": "Overcooked",
"open": false,
"pos_id": "zRcEkcj8",
"price_per_unit": 0
}
]
},
"id": "eMiy4iR4",
"maximum": 1,
"minimum": 1,
"name": "Temperature",
"required": false
},
{
"_embedded": {
"options": [
{
"id": "E5cpac84",
"name": "Tomato",
"open": false,
"pos_id": "E5cpac84",
"price_per_unit": 0
},
{
"id": "GkiREiyL",
"name": "Cheese",
"open": false,
"pos_id": "GkiREiyL",
"price_per_unit": 100
}
]
},
"id": "kMT85Tay",
"maximum": null,
"minimum": 1,
"name": "Toppings",
"required": false
}
]
},
"count": 2,
"limit": 20
}
So there are modifier group names (e.g. "Temperature" and "Toppings"), and group options (e.g. "Perfect" and "Overcooked" for "Temperature" group).
What I am trying to do is build a [String] such as:
["Temperature - Perfect", "Temperature - Overcooked", "Toppings - Tomato", "Toppings - Cheese"]
What would be the quickest way to go about that?
Currently, I first extract the groups into a [String] using valueForKeyPath:
Alamofire.request(.GET, url, headers: headers, encoding: .JSON)
.responseJSON { response in
switch response.result {
case .Success(let JSON):
let jsonData = JSON as? NSDictionary
let groupNames = jsonData?.valueForKeyPath("_embedded.modifier_groups.name")
But how would I get from there to drilling deeper into the group options so that I append them into the [String]?
UPDATE
I tried this but it's not returning anything:
var mods = [String]()
let modGroups = jsonData?.valueForKeyPath("_embedded.modifier_groups")
if let modGroups = modGroups {
for modGroup in modGroups as! [AnyObject] {
let groupOptions = modGroups.valueForKeyPath("_embedded.options")
if let groupOptions = groupOptions {
for groupOption in groupOptions as! [AnyObject] {
mods.append("\(modGroup) - \(groupOption)")
}
}
}
}
Got it:
var mods = [String]()
let modGroups = jsonData?.valueForKeyPath("_embedded.modifier_groups") as? [NSDictionary]
if let modGroups = modGroups {
for modGroup in modGroups {
let groupOptions = modGroup.valueForKeyPath("_embedded.options") as? [NSDictionary]
if let groupOptions = groupOptions {
for groupOption in groupOptions {
mods.append("\(modGroup.valueForKey("name")!) - \(groupOption.valueForKey("name")!)")
}
}
}
}
This is my json to parse (example):
[
{
"id": 1,
"name": "Team name",
"shower": {
"id": 1,
"status": 1,
"startLocation": {
"id": 1,
"name": "abc 16"
}
}
},
{
"id": 2,
"name": "Team name",
"shower": {
"id": 2,
"status": 1,
"startLocation": {
"id": 1,
"name": "efg 16"
}
}
}
]
paste it this json viewer to view it easily.
as you can see, it is an array (of teams).
I need to get each team and do something with it.
After many attempts, I tried using SwiftyJSON, because I thought it will be easier. But, it did not worked for me.
This is what I tried:
let array = JSON(response)
// print each subJSON in array
for team in array.arrayValue {
print(team)
}
But the loop does not work. It does not go in to the loop at all.
Maybe it does not understand that my json is an array.
I can see the array object in the debugger. It looks like this:
How can I get these sub-JSONs?
Thanks.
I think you should use
let array = JSON(parseJSON: response)
instead of
let array = JSON(response)
SwiftyJSON contains methods to parse JSON string into a JSON object, check documentation
/**
Parses the JSON string into a JSON object
- parameter json: the JSON string
- returns: the created JSON object
*/
public init(parseJSON jsonString: String) {
if let data = jsonString.data(using: .utf8) {
self.init(data)
} else {
self.init(NSNull())
}
}
/**
Creates a JSON from JSON string
- parameter string: Normal json string like '{"a":"b"}'
- returns: The created JSON
*/
#available(*, deprecated: 3.2, message: "Use instead `init(parseJSON: )`")
public static func parse(json: String) -> JSON {
return json.data(using: String.Encoding.utf8)
.flatMap{ JSON(data: $0) } ?? JSON(NSNull())
}
or alternatively you can convert son string into son object like
Swift 3:
let dataFromString = response.data(using: .utf8)
let jsonArray = JSON(data: dataFromString!)
In the following example, I save team names in an array. I've tested it.
var names = [String]()
if let array = json.array {
for i in 0..<array.count {
let name = array[i]["name"]
names.append(name.stringValue)
}
}
print(names) // ["Team name", "Team name"]
Here is the answer for Swift 5. In My case data response is something like below :
[
{
"Name": "Some Type",
"Data": [
{
"ParentId": 111,
"Code": "Personal",
"SortOrder": 1,
"Name": "Personal",
"Id": 323
},
{
"ParentId": null,
"Code": "Work",
"SortOrder": 2,
"Name": "Work",
"Id": 324
}
],
"KeyType": "Integer"
},
{
"Name": "Phone Type",
"Data": [
{
"ParentId": null,
"Code": "Phone",
"SortOrder": 1,
"Name": "Phone",
"Id": 785
},
{
"ParentId": null,
"Code": "Cell",
"SortOrder": 2,
"Name": "Cell",
"Id": 786
},
{
"ParentId": null,
"Code": "Fax",
"SortOrder": 3,
"Name": "Fax",
"Id": 787
},
{
"ParentId": null,
"Code": "Home",
"SortOrder": 4,
"Name": "Home",
"Id": 788
},
{
"ParentId": null,
"Code": "Office",
"SortOrder": 5,
"Name": "Office",
"Id": 789
}
],
"KeyType": "Integer"
}
]
I was handled it with following code.
struct responseObjectClass:BaseModel {
var responsearray: [arrayData]? = nil
init(json: JSON) {
responsearray = json.arrayValue.map { arrayData(json: $0) }
}
}
struct arrayData:BaseModel {
let Name: String?
var DataValue: [DataLookup]? = nil
let KeyType: String?
init(json: JSON) {
Name = json["Name"].stringValue
DataValue = json["Data"].arrayValue.map { DataLookup(json: $0) }
KeyType = json["KeyType"].stringValue
}
}
struct DataLookup:BaseModel {
let ParentId: Any?
let Code: String?
let SortOrder: Int?
let Name: String?
let Id: Int?
init(json: JSON) {
ParentId = json["ParentId"]
Code = json["Code"].stringValue
SortOrder = json["SortOrder"].intValue
Name = json["Name"].stringValue
Id = json["Id"].intValue
}
}
BaseModel is Optional it's just used for init Json.
protocol BaseModel {
init(json: JSON)
}
Without SwiftyJSON
Below is the valid JSON
data.json File
[{
"id": 1,
"name": "Team name",
"shower": {
"id": 1,
"status": 1,
"startLocation": {
"id": 1,
"name": "abc 16"
}
}
}, {
"id": 2,
"name": "Team name",
"shower": {
"id": 2,
"status": 1,
"startLocation": {
"id": 1,
"name": "efg 16"
}
}
}]
Below is the code to read your json.
if let path = Bundle.main.path(forResource: "data", ofType: "json") {
let url = URL(fileURLWithPath: path)
do {
let data = try Data(contentsOf: url)
if let jsonArray = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSArray {
for (_, item) in jsonArray.enumerated() {
let itemDict = item as! NSDictionary
let id = itemDict["id"] as! Int
let name = itemDict["name"] as! String
let shower = itemDict["shower"] as! NSDictionary
let showerId = shower["id"] as! Int
let showerStatus = shower["status"] as! Int
let startLocation = shower["startLocation"] as! NSDictionary
let startLocationId = startLocation["id"] as! Int
let startLocationName = startLocation["name"] as! String
}
}
} catch {
print("Error: \(error.localizedDescription)")
}
}
this is what worked for me:
// Convert JSON to Array
func JSONToArray(_ json: String) -> Array<Any>? {
if let data = json.data(using: String.Encoding.utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? Array
} catch let error as NSError {
print(error)
}
}
return nil
}
After using this function i could loop trough the sub JSONs.
Thanks.