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
Related
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.
I need create a json for send from a API REST:
{
"ownId": "seu_identificador_proprio",
"amount": {
"currency": "BRL",
"subtotals": {
"shipping": 1000
}
},
"items": [
{
"product": "Descrição do pedido",
"quantity": 1,
"detail": "Mais info...",
"price": 1000
}
],
"customer": {
"ownId": "seu_identificador_proprio_de_cliente",
"fullname": "Jose Silva",
"email": "nome#email.com",
"birthDate": "1988-12-30",
"taxDocument": {
"type": "CPF",
"number": "22222222222"
},
"phone": {
"countryCode": "55",
"areaCode": "11",
"number": "66778899"
},
"shippingAddress": {
"street": "Avenida Faria Lima",
"streetNumber": 2927,
"complement": 8,
"district": "Itaim",
"city": "Sao Paulo",
"state": "SP",
"country": "BRA",
"zipCode": "01234000"
}
}
}
I am confused with the creation..
I try begin with [NSObject:AnyObject]
var d1 : [NSObject:AnyObject] = ["ownId":"seu_identificador_proprio", "customer":""]
let dd1 = ["currency":"BRL"]
let dd2 = ["shipping":"1000"]
let arr = [d1]
let d = try! NSJSONSerialization.dataWithJSONObject(arr, options: NSJSONWritingOptions.PrettyPrinted)
let s = NSString(data: d, encoding: NSUTF8StringEncoding)! as String
print(s)
But I need help!
I have updated your code and added some hints, how can you build the above listed structure. Happy coding!
// Do not use NSObject as key's type
// Keys in a dictionary are usually Strig in every language
var d1: [String: AnyObject] = ["ownId":"seu_identificador_proprio", "customer":""]
// Define the type of your dictionaries, if you dont, in this case it will create a [String:String] dictionary, but you need to insert an array into it
// Make it a var, so you can mutate the container
var dd1: [String: AnyObject] = ["currency":"BRL"]
// Here the type is undefined. Try inserting anything else than String, and see the results
let dd2 = ["shipping":"1000"]
dd1["subtotals"] = dd2
d1["amount"] = dd1
// Build all your dictionaries like i did above, and at the end add all of them into arr
let arr = [d1]
// Do not force try any throwing function in swift - if there is an error, your application will crash
// Use proper error handling - https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html
do {
let d = try NSJSONSerialization.dataWithJSONObject(arr, options: NSJSONWritingOptions.PrettyPrinted)
let s = NSString(data: d, encoding: NSUTF8StringEncoding)! as String
print(s)
} catch {
// Do your error handling here
}
I have 3 lists:
a = ["John", "Archie", "Levi"]
b = ["13", "20"]
c = ["m", "m", "m", "m"]
I want to merge this into one list with dictionaries:
result = [
{"name": "John", "age": "13", "gender": "m"},
{"name": "Archie", "age": "20", "gender": "m"},
{"name": "Levi", "age": "", "gender": "m"},
{"name": "", "age": "", "gender": "m"},
]
Ok, this is pretty normal computer science problem. Here is an outline of how to go about solving it:
First, identify the inputs and the desired outputs. You've done that.
Note any edge cases you need to handle
Next, map out the logic flow using pseudo-code.
Convert from pseudo-code to real code.
Test and debug.
For step 2, your data suggests that you want to handle cases where you have different numbers of elements in each source array. it looks like you want to create a dictionary for all the 0 elements in each array, then a dictionary for the 1 elements, etc. When you run out of elements for a given array, it looks like you want to skip that key in your resulting dictionary entry.
Now to pseudo-code:
Find the array with the maximum number of elements. Make this your output_count (the number of items in your output array.)
Create an output array large enough for output_count entries.
Loop from 0 to output_count - 1.
create a dictionary variable
for each input array, if there are enough elements, add a key/value pair
for that array's key and the value at the current index. Otherwise skip
that key.
add the new dictionary to the end of your output array.
That should be enough to get you started. Now see if you can convert that pseudo-code to actual code, test it, and debug it. Report back here with your actual code, and feel free to ask for help if you get stuck getting your code working.
Try like this:
let a = ["John", "Archie", "Levi"]
let b = ["13", "20"]
let c = ["m", "m", "m", "m"]
var dicArray:[[String:String]] = []
for index in 0..<max(a.count,b.count,c.count) {
dicArray.append([:])
dicArray[index]["name"] = index < a.count ? a[index] : ""
dicArray[index]["age"] = index < b.count ? b[index] : ""
dicArray[index]["gender"] = index < c.count ? c[index] : ""
}
dicArray // [["gender": "m", "age": "13", "name": "John"], ["gender": "m", "age": "20", "name": "Archie"], ["gender": "m", "age": "", "name": "Levi"], ["gender": "m", "age": "", "name": ""]]
Here is my take. I first create the array, and then use enumerate() and forEach to process each array and add them to the array of dictionaries:
let a = ["John", "Archie", "Levi"]
let b = ["13", "20"]
let c = ["m", "m", "m", "m"]
let count = max(a.count, b.count, c.count)
var result = Array(count: count, repeatedValue: ["name":"", "age":"", "gender":""])
a.enumerate().forEach { idx, val in result[idx]["name"] = val }
b.enumerate().forEach { idx, val in result[idx]["age"] = val }
c.enumerate().forEach { idx, val in result[idx]["gender"] = val }
print(result)
[["gender": "m", "age": "13", "name": "John"], ["gender": "m", "age":
"20", "name": "Archie"], ["gender": "m", "age": "", "name": "Levi"],
["gender": "m", "age": "", "name": ""]]
This should do the job
let maxLength = max(a.count, b.count, c.count)
let paddedA = a + [String](count: maxLength-a.count, repeatedValue: "")
let paddedB = b + [String](count: maxLength-b.count, repeatedValue: "")
let paddedC = c + [String](count: maxLength-c.count, repeatedValue: "")
let res = zip(paddedA, zip(paddedB, paddedC)).map {
["name": $0.0, "age": $0.1.0, "gender": $0.1.1]
}
Output
[["gender": "m", "age": "13", "name": "John"], ["gender": "m", "age": "20", "name": "Archie"], ["gender": "m", "age": "", "name": "Levi"], ["gender": "m", "age": "", "name": ""]]
I have two Dictionaries that look like:
var dict1 = [Int: [String: AnyObject]]
var dict2 = [Int: [String: AnyObject]]
for example:
dict1: [0: ["sender": "user1", "time": NSDate(), "mId": "as2f2ASf"], [1: ["sender": "user1", "time": NSDate(), "mId": "Sjf82FsJ"]
dict2: [0: ["sender": "user2", "time": NSDate(), "mId": "J2fAS92D"], [1: ["sender": "user2", "time": NSDate(), "mId": "Sjf82FsJ"]
I want to find out if the dictionaries have data with the same mId and if, I want to do some action.
I wrote this code for that and got these errors.
for (_, value) in dict1! {
for content in value {
if content.0 == "mId" {
var mID = content.1
for (_, 2ndValue) in dict2 {
for 2ndContent in 2ndValue {
if 2ndContent.0 == "mId" {
var 2ndMID = 2ndContent.1
if mID == 2ndMID {
//Do Some Action
}
}
}
}
}
}
}
Here are the errors:
https://www.dropbox.com/s/e28ple6640hkrzo/Bildschirmfoto%202015-08-28%20um%2021.21.54.png?dl=0
Firstly, as Eric says in his comment, you are getting errors because you can't start a variable name with a number.
Secondly what you are calling a 'dictionary' here is in fact an array of tuples - if you define your dict1 variable in the Swift REPL by running var dict1 = [Int,[String,AnyObject]()) you get:
dict1: [(Int, [String : AnyObject])] = 0 values
A dictionary with the equivalent structure would be defined thus:
var dict1 = [Int:[String:AnyObject]]() - this returns
dict2: [Int : [String : AnyObject]] = 0 key/value pairs
in the REPL.
Therefore the dict1 and dict2 declarations you show will not work. They need to be declared as follows to match the type you've specified:
var dict1 = [(0, ["sender": "user1", "time": NSDate(), "mId": "as2f2ASf"]), (1, ["sender": "user1", "time": NSDate(), "mId": "Sjf82FsJ"])]
var dict2 = [(0, ["sender": "user2", "time": NSDate(), "mId": "J2fAS92D"]), (1, ["sender": "user2", "time": NSDate(), "mId": "Sjf82FsJ"])]
If you want to use an actual dictionary, use :
var dict3 = [[0: ["sender": "user1", "time": NSDate(), "mId": "as2f2ASf"]], [1: ["sender": "user1", "time": NSDate(), "mId": "Sjf82FsJ"]]]
While I can see what you're trying to achieve in the code you've posted, I don't think the approach you're taking is the right one. Any time you have 4 levels of nested for loops you should consider whether there's a better way of solving the problem.
You are iterating over all the keys in the dictionary when you are only interested in the value of "mId". Just looking for the value of that key gets rid of 2 loops and a lot of pointless iterating over things you aren't interested in:
for (_,value1) in dict1 {
if let mld1 = value1["mId"] {
for(_,value2) in dict2 {
if let mld2 = value2["mId"] {
if mld1 == mld2 {
println("Found diff")
}
}
}
}
}
The if let is necessary to ensure you are making a valid comparison (i.e. to avoid a false positive if the value of 'mld' is nil in both dicts). No idea whether this is even possible in your data model but always worth being thorough. Note that this solution uses arrays of tuples, as in your examples - you'd need something slightly different if you use actual dictionaries.
Well im not sure i understood your problem, but this is my idea for that:
for (key, value) in dicOne {
for (keyTwo, valueTwo) in dicTwo {
for (keyItem,valueItem) in value {
for (keyItemTwo, valueItemTwo) in valueTwo {
if keyItem == keyItemTwo && valueItem == valueItemTwo{
println("do some action")
}
}
}
}
}
The first loop is for the first dictionary and the second, for the other dictionary. After this you'r in the second layer ["a": "A"] of both dictionaries. So the next step is to get this dictionary. For this are the other two loops.
Just a question: Did u tried tuple? Like this:
var dictionaryOne:[Int: (String, String)] = [1: ("a", "A")]
var dictionaryTwo:[Int: (String, String)] = [1: ("a", "A")]
for (key, value) in dictionaryOne {
for (keyTwo, valueTwo) in dictionaryTwo {
if value.0 == valueTwo.0 && value.1 == valueTwo.1 {
println("nice action")
}
}
}
I have got a json result, that looks like this:
[{
"EventDate": "2015-02-19",
"PubEvents": [{
"Title": "Ladies Night",
"Description": "Every thursday is Ladies Night at the Irish House.\nLadies: 2 cocktails for the price of 1 - 1 pint of Crowmoor Cider - 30 kr",
"EventType": "LadiesNight",
"Start": "2015-02-19",
"End": "2015-02-20",
"StartTime": "19:00",
"EndTime": "02:00",
"Image": "ladies.jpg"
}, {
"Title": "Pat Kelly",
"Description": "Pat Kelly from Ireland has extensive experience in entertaining all nationalities, as he has travelled around the world from New York to Amsterdam. He has a very wide repertoire maintaining a traditional approach, and is well received and appreciated for his ability to get his audience excited every night he hits the stage. A “sure thing” evening you will get with the talented Pat.",
"EventType": "Music",
"Start": "2015-02-19",
"End": "2015-02-20",
"StartTime": "21:30",
"EndTime": "01:00",
"Image": "http://domain.dk/Images/Musicians/kelly"
}],
"Matches": [{
"EventType": "Sports",
"Start": "2015-02-19",
"End": "2015-02-19",
"StartTime": "18:00",
"EndTime": "19:00",
"HomeTeam": {
"Id": 0,
"TeamName": "Hold",
"HomeImageUrl": "defaultHome.png",
"AwayImageUrl": "defaultAway.png",
"Badge": "defaultBadge.png"
},
"AwayTeam": {
"Id": 0,
"TeamName": "AndetHold",
"HomeImageUrl": "defaultHome.png",
"AwayImageUrl": "defaultAway.png",
"Badge": "couldn't get an away team"
}
}]
}, {
"EventDate": "2015-02-20",
"PubEvents": [{
"Title": "Pat Kelly",
"Description": "Pat Kelly from Ireland has extensive experience in entertaining all nationalities, as he has travelled around the world from New York to Amsterdam. He has a very wide repertoire maintaining a traditional approach, and is well received and appreciated for his ability to get his audience excited every night he hits the stage. A “sure thing” evening you will get with the talented Pat.",
"EventType": "Music",
"Start": "2015-02-20",
"End": "2015-02-21",
"StartTime": "22:30",
"EndTime": "02:00",
"Image": "http://domain.dk/Images/Musicians/kelly"
}],
"Matches": []
},
An Array of Events in the format:
var eventDate : String?
var pubEvents : Array<PubEvent>?
var matches : Array<Match>?
numberOfSectionsInTableView is eventarray.count, as every date is distinct
numberOfRowsInSection:
let event = self.eventarray[section]
let matchesCount = event.matches?.count ?? 0
let pubEventsCount = event.pubEvents?.count ?? 0
return matchesCount + pubEventsCount
Since every section has x number of matches and x number of pubEvents i need a combined count of the amount of each.
Here is my problem:
I would need to insert the pubevents (if any) and the matches (if any) in the tableview, but I can't figure it out with Swift.
Edit
Philip Mills helped: A very quick implementation:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
let event = self.eventarray[indexPath.section] as SortedEvent
let matchesCount = event.matches?.count ?? 0
let pubEventsCount = event.pubEvents?.count ?? 0
if indexPath.row < matchesCount{
cell.textLabel?.text = "\(event.matches![indexPath.row].homeTeam!.teamName!) v \(event.matches![indexPath.row].awayTeam!.teamName!)"
cell.detailTextLabel?.text = "\(event.matches![indexPath.row].startTime!) - \(event.matches![indexPath.row].endTime!)"
}
else{
cell.textLabel?.text = event.pubEvents![indexPath.row - matchesCount].title!
cell.detailTextLabel?.text = "\(event.pubEvents![indexPath.row - matchesCount].startTime!) - \(event.pubEvents![indexPath.row - matchesCount].endTime!)"
}
return cell
}
When you're given an index path to provide a cell, see if the row is less than the count of matches. If it is, use that match. If it's not less, subtract the count of matches from the row and use that as the index of the pub event you want.