How to change dictionary keys only? Without change values swift - ios

I have array like this.
var Array = [Int:[String:Any]]()
I just update the values
let updateValue = ["Name":tripName!,"date":firstDate,"year":year,"startData":startDateValue,"endDate":endDateValue,"countryName":countrname!,"totelBudjet":totelBudjet,"backgroundImage":arrImages[randomItem],"code":getAddtripeAttibutes.code,"countryIcon":countryIcon] as [String : Any]
Array.updateValue(updateValue, forKey: tripId)
tripId = tripId + 1
We have more Array data as mentioned below
trip keys like :
[0] [1] [2]
[0: ["year": "2019", "tripName": "Test", "backgroundImage": "Europe_Trip", "code": "DZD", "startData": 2019-05-30 10:14:11 +0000, "date": "May 30 - May 31", "endDate": 2019-05-31 10:14:11 +0000, "totelBudjet": "5000 DZD", "countryIcon": "dz", "countryName": "Algeria"], 1: ["date": "May 30 - May 31", "backgroundImage": "Europe_Trip", "endDate": 2019-05-31 10:14:43 +0000, "code": "DZD", "countryName": "Algeria", "tripName": "Gg", "countryIcon": "dz", "totelBudjet": "500 DZD", "startData": 2019-05-30 10:14:43 +0000, "year": "2019"], 2: ["year": "2019", "backgroundImage": "Asia_Trip", "endDate": 2019-05-31 10:15:00 +0000, "countryIcon": "al", "totelBudjet": "5800 ALL", "code": "ALL", "tripName": "Bb", "countryName": "Albania", "date": "May 30 - May 31", "startData": 2019-05-30 10:15:00 +0000]]
I have updated tableview with above data
after i delete one array [1] it look like:
this
[0] [2]
[0: ["year": "2019", "tripName": "Test", "backgroundImage": "Europe_Trip", "code": "DZD", "startData": 2019-05-30 10:14:11 +0000, "date": "May 30 - May 31", "endDate": 2019-05-31 10:14:11 +0000, "totelBudjet": "5000 DZD", "countryIcon": "dz", "countryName": "Algeria"], 2: ["year": "2019", "backgroundImage": "Asia_Trip", "endDate": 2019-05-31 10:15:00 +0000, "countryIcon": "al", "totelBudjet": "5800 ALL", "code": "ALL", "tripName": "Bb", "countryName": "Albania", "date": "May 30 - May 31", "startData": 2019-05-30 10:15:00 +0000]]
But I want to update the keys as [0][1] not as [0][2]
Can any one please help me with this?

Why are you trying to mimic an array using a dictionary?
If you have an array like this.
var array: [[String: Any]] = [["year": "2019"], ["year": "2020"], ["year": "2021"]] // Minimalistic example array
You can delete values at that particular location and it'll give you what you need currently.
array.remove(at: 1)
print(array[1])
["year": "2021"]
Note - Your data looks like JSON response. So, you could use #Kamran's suggestion in the comments and use a struct Trip conforming Decodable and directly get your data as an array of Trips. However, if your JSON is formatted with keys 0, 1, 2... then your JSON is really bad and you should get it changed if you can. If you can't, you'll have to change the dictionary of dictionaries to an array of dictionary or better yet array of structs.

it is not quite clear to me what's the use-case here, so I'm trying to cover the most possible.
using enumerated() on the array
if you need only to add the index, call the enumerated() method on an array, like e.g.
var array: [[String: Any]] = [["year": "2019", "tripName": "Trip#1"], ["year": "2020", "tripName": "Trip#2"], ["year": "2021", "tripName": "Trip#3"]]
array.enumerated().forEach { (arg0) in
let (offset, element) = arg0
debugPrint("\(offset) : \(element)")
}
that would assign an offset to each element which you can print out or use otherwise, in my example the output on the console would look like this:
0 : ["tripName": "Trip#1", "year": "2019"]
1 : ["tripName": "Trip#2", "year": "2020"]
2 : ["tripName": "Trip#3", "year": "2021"]
if you need to actually remove an item, like e.g.
array.remove(at: 1)
and call the same snippet (above) again, it assigns the offset like this:
0 : ["tripName": "Trip#1", "year": "2019"]
1 : ["tripName": "Trip#3", "year": "2021"]
that is quite similar to what you might looking for.
mapping an array from dictionary
if you need to actually map the dictionary first into an array (like e.g. during post-processing a JSON response), you may go for that as well like e.g.:
var dictionary: [Int: [String: Any]] = [0: ["year": "2019", "tripName": "Trip#1"], 1: ["year": "2020", "tripName": "Trip#2"], 2: ["year": "2021", "tripName": "Trip#3"]]
var array = dictionary.map { $0.value }
then you can start dealing with the enumerated() method just like I shown it to you above.
NOTE: obviously if you do the mapping like I did, you will lose the information what the original key holds, and that will be replaced coincidently with the same number in my example, which may be misleading but if you are aware of that the offset and the key are two completely different values here, you may not have a huge surprise after you do the mapping.

Related

How can I combine the date into a dictionary?

I'm having trouble with Dictionary.
For example,
My current data
["date": "2018-01-01", "name": "New Year"], ["date": "2018-01-31", "name": "Public holiday"],["date": "2018-02-14", "name": "Valentine's Day"], ["date": "2018-02-28", "name": "Public Holiday"]
How can I transform to
["month": ["January": ["date": "2018-01-01", "name": "New Year"], ["date": "2018-01-31", "name": "Public holiday"]],["February": ["date": "2018-02-14", "name": "Valentine's Day"], ["date": "2018-02-28", "name": "Public Holiday"]]]
You can use DateFormatter to read the String format dates (yyyy-MM-dd) into Date instances, and subsequently re-use the formatter to extract the full month names (MMMM) from the dates as your String key.
Combine this with reduce(into:) to process the data array:
let data = [
["date": "2018-01-01", "name": "New Year"],
["date": "2018-01-31", "name": "Public holiday"],
["date": "2018-02-14", "name": "Valentine's Day"],
["date": "2018-02-28", "name": "Public Holiday"]
]
let monthCategorizedData = data
.reduce(into: (result: [String: [[String: String]]](),
formatter: DateFormatter(),
calendar: Calendar(identifier: .gregorian))) {(args, data) in
var month = "Unknown"
args.formatter.dateFormat = "yyyy-MM-dd" // format: read date from string
if let date = data["date"].flatMap({ args.formatter.date(from: $0)}) {
args.formatter.dateFormat = "MMMM" // format: read month (as string) from date
month = args.formatter.string(from: date)
}
args.result[month] = args.result[month, default: []] + [data]
}.result
print(monthCategorizedData)
/* ["January": [["date": "2018-01-01", "name": "New Year"],
["date": "2018-01-31", "name": "Public holiday"]],
"February": [["date": "2018-02-14", "name": "Valentine\'s Day"],
["date": "2018-02-28", "name": "Public Holiday"]]] */
In case of data with invalid dates (String form to Date fails, or "date" key does not exist in the data), the above will categorize them into the key Unknown. If you'd rather just discard such data (as such a failure should be unexpected), you can modify the above to:
let monthCategorizedData = data
.reduce(into: (result: [String: [[String: String]]](),
formatter: DateFormatter(),
calendar: Calendar(identifier: .gregorian))) {(args, data) in
args.formatter.dateFormat = "yyyy-MM-dd" // format: read date from string
if let date = data["date"].flatMap({ args.formatter.date(from: $0)}) {
args.formatter.dateFormat = "MMMM" // format: read month (as string) from date
let month = args.formatter.string(from: date)
args.result[month] = args.result[month, default: []] + [data]
}
}.result
Further nesting the result one more level into the value for a "month" key in a [String: [String : [[String : String]]]] is straight-forward:
let categorizedData = ["month": monthCategorizedData]
print(categorizedData)
/* ["month": ["January": [["date": "2018-01-01", "name": "New Year"],
["date": "2018-01-31", "name": "Public holiday"]],
"February": [["date": "2018-02-14", "name": "Valentine\'s Day"],
["date": "2018-02-28", "name": "Public Holiday"]]]] */
Dictionary has a method init(grouping:by:) which can do that in a pretty smart way.
Then date parameter is converted to Date and then to the month name with a DateFormatter
let array = [["date": "2018-01-01", "name": "New Year"],
["date": "2018-01-31", "name": "Public holiday"],
["date": "2018-02-14", "name": "Valentine's Day"],
["date": "2018-02-28", "name": "Public Holiday"]]
let dictionary = Dictionary(grouping: array) { (item) -> String in
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
guard let dateString = item["date"],
let date = formatter.date(from: dateString) else { return "Unknown" }
formatter.dateFormat = "MMMM"
return formatter.string(from: date)
}
print(["months" : dictionary])
while im not expert but problem for me seem to be just how u get from
"date" = "year-month-day" the value of month
if u could be able to some how cut? or get just 5th and 6th character it will become a number of month u need
based on that u could use if or switch and make an array of dictionaries
or even just dictionaries which u would then add into array as u wish.
so after getting this month number it would be like this :
var dict: [String: [[String:String]]] = [:]
dict["janury"] = [["2012-01-12":"Event"],["2012-01-13":"event2"]]
if monthNumber == 1 {
dict["janury"]?.append(["2012-01-14":"event3"])
}
and then for each month u would need new if total of 12 eventually switch with 12 statements
as for how to get this value of string i don't know yet, but i'm almost sure google could help it or someone with better experience than my

ios: Json in Socket.io

I need to send data over socketio from ios using swift. Data looks like this:
{
birthday: {
"date": 24,
"month": 12,
"year": 1991
}
}
I tried this code, but it doesn't work:
let myJSON = [
"birthday" : [
"date": 24,
"month": 12,
"year": 1991
]
]
socket.emit("updateUserInfo", myJSON)
Please, tell me what i'm doing wrong
January 2020
Asumming that you are using Socket.IO-Client-Swift Library
Here is the sample source on how to effectively pass your JSON data.
// Adopt to SocketData protocol
struct CustomData : SocketData {
let name: String
let age: Int
func socketRepresentation() -> SocketData {
return ["name": name, "age": age]
}
}
// Usage
socket.emit("myEvent", CustomData(name: "Erik", age: 24))
You are right that if you have a dictionary to send over socket.io then you don't have to convert it to JSON because the method socket.emit does that for you.
So the syntax you show seems to be correct : socket.emit("event-as-string", dictionary).
You should tell us the exact error you are encountering such as someone can help you.
With socket.io you can send a normal object from javascript and it will be converted to JSON automatically.
I tested that code and everything is ok
let myJSON = { birthday: { "date": 24, "month": 12, "year": 1991}};
socket.emit("updateUserInfo", myJSON);
I believe you'll need to convert your Swift object to a JSON string. As Eric D. said there's a built in way to convert dictionary to JSON. If you already have a class representation of the data, you can take a look at a Swift class to JSON converter here:
https://gist.github.com/peheje/cc3618253d4f38ea4885
Have you tried just writing it as a string?
let myJSON = "[\"birthday\" : [\"date\": 24, \"month\": 12, \"year\": 1991]]"
Try this may work , internally it's getting object from zero index
[{
birthday: {
"date": 24,
"month": 12,
"year": 1991
}
}]

POST multiple json objects in Alamofire POST method - Swift/IOS

Sorry if my question is not clear, I'll try to make myself clear with an explanation. So here is exactly what I'm trying to do, I'm trying to use Alamofire to post more than one comment (Something that my app implements and will be stored as a JSON object whenever user writes a new comment). I'm passing these JSON comments to my post routine, where I can use SwiftyJSON to extract each value. Noe the thing is I know how to set the parameters if I'm trying to authorize the user as follows,
var parameters = [
"userName": userName,
"password": passwordSalt,
"somethingElse": somethingElse
]
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(parameters , options: nil, error: &err)
it's quite straightforward until here, now comes my problem. I'm trying to use alamofire post to post multiple json objects, which should look like this
[
{
"comment": "my First Comment",
"commentDate": "2014-05-13 14:30 PM",
"isSigned": 1,
"patientId": 2,
"documentId": 3
},
{
"comment": "my SecondComment",
"commentDate": "2014-05-14 14:30 PM",
"isSigned": 2,
"patientId": 3,
"documentId": 4
},
{
"comment": "my third Comment",
"commentDate": "2014-05-15 14:30 PM",
"isSigned": 3,
"patientId": 4,
"documentId": 5
}
]
How do I create above array/json (I'm not exactly sure on what to call this) by iterating JSON object? I know how to get the JSON values from the JSON object all I'm asking is how to create this parameters variable to hold the data like above example. Is it even possible to do this using Alamofire? (POST multiple objects at once)
I tried a couple of ways to but they didn't work out
var dictArray = [Dictionary<String, Any>]
var dict = Dictionary<String, Any>
While iterating over JSON object inserted each value in dict and appended dict to dictArray, now when I'm trying to use dictArray as parameters in .dataWithJSONObject it doesn't like the object.
var dict = Dictionary<String, AnyObject>
var array = NSArray()
extracted each value by iterating over the JSON object and inserted them into dict and tried inserting dict into array. But this gives a different problem. The way it builds the objects is different from what is required, as follows.
[
{
comment: my First Comment,
commentDate: 2015-05-13 13:30 PM"",
isSigned: 1,
patientId: 2,
documentId: 3
},
{
comment: my Second Comment,
commentDate: 2015-05-13 13:30 PM"",
isSigned: 2,
patientId: 5,
documentId: 4
},
{
comment: my third Comment,
commentDate: 2015-06-13 13:30 PM"",
isSigned: 5,
patientId: 1,
documentId: 9
}
]
Here the Keys doesn't get wrapped inside quotes (Correct way: "comment", wrong way: comment).
Did anyone try posting multiple objects, is alamofire capable of doing so? I hope I made the question clear. Sorry if this is too simple of a question to answer, I spent my whole day figuring this out but didn't work out.
The correct representation in Swift for the array of comment objects you have posted would be like this:
let comments: Array<[String:AnyObject]> = [
[
"comment": "my First Comment",
"commentDate": "2014-05-13 14:30 PM",
"isSigned": 1,
"patientId": 2,
"documentId": 3
],
[
"comment": "my SecondComment",
"commentDate": "2014-05-14 14:30 PM",
"isSigned": 2,
"patientId": 3,
"documentId": 4
],
[
"comment": "my third Comment",
"commentDate": "2014-05-15 14:30 PM",
"isSigned": 3,
"patientId": 4,
"documentId": 5
]
]
Sending a single comment would be fairly simple:
let comment: [String:AnyObject] = [
"comment": "my First Comment",
"commentDate": "2014-05-13 14:30 PM",
"isSigned": 1,
"patientId": 2,
"documentId": 3
]
Alamofire.request(.POST, "http://httpbin.org/post", parameters: comment).responseJSON { (req, res, json, error) in
println(req)
println(res)
println(json)
println(error)
}
However, in order to send an array of comments, seems like you would have to generate the URLRequest your self and then pass it to Alamofire as follows:
let mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: "http://httpbin.org/post")!)
mutableURLRequest.HTTPMethod = "POST"
var error: NSError? = nil
let options = NSJSONWritingOptions.allZeros
if let data = NSJSONSerialization.dataWithJSONObject(comments, options: options, error: &error) {
mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
mutableURLRequest.HTTPBody = data
}
Alamofire.request(mutableURLRequest).responseJSON { (req, res, json, error) in
println(req)
println(res)
println(json)
println(error)
}
If you could modify the API backend to accept an object with multiple comments, you could also send them this way:
Alamofire.request(.POST, "http://httpbin.org/post", parameters: ["comments": comments]).responseJSON { (req, res, json, error) in
println(req)
println(res)
println(json)
println(error)
}
Regards.
This would be better
Create dictionary and array of dictionary var
Then loop how many parameters you need to send from data source either an array or whatever.
Here my scenario
Need to answer to all questions (will be a random number/size)
var ansParams = [[String: String]]()
var paramz = [String: String]()
for question in sectionQuestions{
paramz = [
AppConstants.PARAMETER.KEY_1 : "Value",
AppConstants.PARAMETER.KEY_2 : "Value",
AppConstants.PARAMETER.KEY_3 : "Value",
AppConstants.PARAMETER.KEY_4 : "Value",
AppConstants.PARAMETER.KEY_5 : "Value"
]
ansParams.append(paramz)
}
print(ansParams)
//Check All Paramz and its values then send ansParams as Parameter to POST request
I had a similar issue in my project while working with an API that did now allow posting multiple objects at once. The formatting of the array as noted above is fine.
let comments: Array<[String:AnyObject]> = [
[
"comment": "my First Comment",
"commentDate": "2014-05-13 14:30 PM",
"isSigned": 1,
"patientId": 2,
"documentId": 3
],
[
"comment": "my SecondComment",
"commentDate": "2014-05-14 14:30 PM",
"isSigned": 2,
"patientId": 3,
"documentId": 4
],
[
"comment": "my third Comment",
"commentDate": "2014-05-15 14:30 PM",
"isSigned": 3,
"patientId": 4,
"documentId": 5
]
]
Then I used a for loop to post each object of the array to post the API.
var index = comments.count
var i = 0
for i = 0; i < index; i++ {
let urlString = "\(.baseURL)...etc"
let parameters = comments[i]
Alamofire.request(.POST, urlString, parameters: parameters)
.responseJSON { (req, res, data, error) -> Void in
// println(req)
// println(res)
// println(data)
// println(error)
println("\(i) of \(index) posted")
}
}
More efficient ways if the API allows, but otherwise this flow works great.

Parsing JSON with unknown root element in Swift with swiftlyJSON

I am working in iOS using Swift. The service I am using returns the following JSON (cut off as the node begins to repeat):
{
"Ambler Departures: April 15, 2015, 2:57 pm": [
{
"Northbound": [
{
"direction": "N",
"path": "R5N",
"train_id": "564",
"origin": "Frazer Yard",
"destination": "Colmar-Link Belt",
"status": "On Time",
"service_type": "LOCAL",
"next_station": "North Broad St",
"sched_time": "Apr 15 2015 03:27:00:000PM",
"depart_time": "Apr 15 2015 03:27:00:000PM",
"track": "2",
"track_change": null,
"platform": " ",
"platform_change": null
},
{
"direction": "N",
"path": "R5N",
"train_id": "6566",
"origin": null,
Notice that the root node is created dynamically by the service-- I don't have a way of knowing what it will be as the user can chose departures from one of fifty stations and the time is based on the time on the server, which is likely inconsistent with the time on the user's clock.
I want to extract the data from the Northbound Array, and then a Southbound Array lower in the JSON.
I am getting the data returned form the service but I can't parse correctly. I can't get past the root node (the dynamic one) to get to the structure inside.
Here's what I am trying:
let json = JSON(data:data)
var x = 0
while x < json.count
{
let track = json[0]["Northbound"][x]["track"].string
println(track)
x++
}
This results in "nil". I am stuck...
I know that json[0] is probably wrong since that level of the JSON is not an array. Thanks in advance
If json is a JSON object from SwiftyJSON but you know it's a dictionary with an unknown key, you can find this key with:
let theKey = json.dictionary!.keys.first
Then access the array:
let theArray = json[theKey] as! [[String:AnyObject]] // array of dictionaries
Then theArray[0] will be the dictionary with "Northbound" as a key, which contains an array of dictionaries containing the infos.
(Edited to reflect the correct answer specific to SwiftyJSON.)

iOS: Switching from JSON to CoreData for keeping local persistent data; is this practical? Dictionary within array within dictionary within array

I started last month with Swift and iOS. I'm working on an iPhone fitness app, specifically for weightlifting. Until now I've just been using a JSON file in the app's documents folder to store all the user's workouts, and using let workoutData = NSJSONSerialization.JSONObjectWithData( . . . ) as NSArray to load the data from the file (for display in a UITableView-like format) and then adding new workout entries with:
let newEntry: NSData = NSJSONSerialization.dataWithJSONObject(newData, options: .PrettyPrinted, error: nil)
newEntry.writeToFile(documentsPath, atomically: true)
This worked fine at first, but now I need to be able to edit workouts, add new ones at arbitrary dates, or delete specific workouts. This requires a fair amount of looping through arrays within dictionaries within arrays, and sorting the workouts properly has become difficult.
I feel like CoreData may be the best option going forward, considering the nature of my data. That said, here's an example of the structure of my JSON database:
[
{
"day": "Thursday",
"date": "June 5 2014",
"lbs": true,
"lifts": [
{
"name": "SQUAT",
"sets": 1,
"reps": 5,
"weight": 205,
"warmup": true
},
{
"name": "SQUAT",
"sets": 3,
"reps": 5,
"weight": 245,
},
{
"name": "BENCH PRESS",
"sets": 1,
"reps": 5,
"weight": 135,
"warmup": true
},
{
"name": "BENCH PRESS",
"sets": 3,
"reps": 5,
"weight": 185
},
{
"name": "YATES ROW",
"sets": 3,
"reps": 5,
"weight": 135,
}
]
},
{
"day": "Wednesday",
"date": "June 3 2014",
"lbs": true,
"lifts": [
{
"name": "DEADLIFT",
"sets": 1,
"reps": 3,
"weight": 255,
"warmup": true
},
{
"name": "DEADLIFT",
"sets": 1,
"reps": 5,
"weight": 305,
},
{
"name": "OVERHEAD PRESS",
"sets": 1,
"reps": 5,
"weight": 95,
"warmup": true
},
{
"name": "OVERHEAD PRESS",
"sets": 3,
"reps": 5,
"weight": 125
},
{
"name": "CHIN-UPS",
"sets": 3,
"reps": 5,
"weight": 180,
}
]
},
{
"day": "Monday",
"date": "June 1 2014",
"lbs": true,
"lifts": [
]
}
]
So the structure is:
Top level array, containing each workout day dictionary
Workout day dictionary, with a day string, date string, and an array of lifts
Array of lifts, each lift itself a dictionary
Lift dictionary, containing the lift name, sets, reps, weight, and a bool indicating whether this was a warmup set.
CoreData entities seem to only allow rather simple attributes. I suppose I could use a "Transformable" attribute, or use lots of To-Many relationships, but I'd rather not go to the trouble of learning those if it's not necessary.
So, should I suck it up and figure out how to make this same structure happen in CoreData? Alternatively, is this data structure too complex? Should I get rid of arrays/dictionaries altogether and just have a flat "workout" entity, and then after loading data from CoreData, collect all the workouts of the same date/type into groups manually in a ViewController?
Or, given the nature of my data, is there something better suited to use rather than CoreData or JSON? SQLite, perhaps?
If I were you I'd use CoreData or CloudKit (for online storage). The reason why is if I'm a user and I've got workouts spanning a year and a ton of data within each workout. That's going to take forever to load into memory. So you're going to have to figure out a way to segment your data anyways or else your users could be waiting a while for the file to be read into memory or possibly, the file could get bigger than is allotted and it wouldn't be possible to read in.
I don't know why you're saying it would be difficult to store these into database. It looks like to me you already have a structure that could be translated to a DBMs fairly easily. You're tables I would think should look something like this:
Workout : Table #1
wid (primary key)
date (timestamp)
lifts (array of: foreign keys)
Lifts : Table #2
lid (primary key)
name (string)
sets (number)
reps (number)
weight (number)
warmup (bool)
Don't be scared of Databases, they're really not that bad and it'll be 1000% more efficient than storing them in files.

Resources