Most efficient way to parse through nested dictionaries [closed] - ios

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have the following dictionary:
var masterDict = [Double : [Date:[String:Any]]]()
I would like to retrieve the following information based on this data source:
The earliest Date object to exist in the dictionary (search all Double keys).
The latest/furthest Date object to exist in the dictionary (search all Double keys).
My thoughts are to loop through the .keys of the Dictionary and then enumerate that way, but I am asking if there is a Swiftier way to accomplish retrieving this information (possibly faster too)?

First of all get the all the values from masterDict and flatten them out using flatMap(_:) create a single Array,
let values = masterDict.values.flatMap { $0 }
Next, sort the values in ascending order using sorted(_:)
let sortedValues = values.sorted { $0.key.compare($1.key) == .orderedAscending }
Now use first and last on the sortedValues to get the latest and further Date instance,
let first = sortedValues.first?.key
let last = sortedValues.last?.key

You can use reduce to extract all dates values
let allDates = masterDict.reduce(into: []) { $0.append(contentsOf: $1.value.keys) }
and then you can simply get the min and max dates
print(allDates.max())
print(allDates.min())
or if you prefer first and last you can sort the array
let allDates = masterDict.reduce(into: []) { $0.append(contentsOf: $1.value.keys) }.sorted()
print(allDates.last)
print(allDates.first)

You can use sort function on dictionary, sort condition will be based on dates comparison.
var masterDict = [Double : [Date:[String:Any]]]()
You can access latest and furthest date based on it.
let sorted = masterDict.sorted { (dict1, dict2) -> Bool in
return dict1.value.keys.first! < dict2.value.keys.first!
}
Try first and last properties on sorted array of dictionary to get the latest date.
print(sorted.first!.value)
print(sorted.last!.value)

Related

Sort received dates from response in swift

I am working on code where I am receiving lots of data associated with dates
each object having one date parameter and there might many objects with the same date.
I need to show this all objects in UITableView. each object as one cell.
I succeed in that,
I need to get unique dates from the response array of objects.
Those unique dates will be stored in an array which will act as a number of sections of my table view with section header title will be the date from the unique date array.
somehow I am able to sort out that with what I want,
The only problem I am facing is I am not able to sort the unique date array
every time the sequence change.
I need the latest date as the first date and the oldest date as the end date.
How to achieve this in swift.
Following is a piece of code I have written
let sortedKeys = Array(dictValue.keys).sorted(by: {$0 > $1})
print(sortedKeys)
here dicValue.keys is my unique date array and I wanted to sort it.
Following is a sample response I am getting
["08/03/2021”, “10/02/2021”, "26/04/2021", "25/03/2021”, "09/12/2020”, , "27/04/2021”, "23/03/2021”, "11/01/2021”, "05/03/2021”, "09/03/2021”, "16/10/2020", "19/03/2021", "12/10/2020" ]
and after applying sort I am getting the following output
[“27/04/2021", "26/04/2021", "25/03/2021", "23/03/2021", "19/03/2021", "16/10/2020", "12/10/2020", "11/01/2021", "10/02/2021", "09/12/2020", "09/03/2021", "08/03/2021", "05/03/2021”]
where dates are not properly sorted out.
Can anyone please help me out with it.
Thanks in advance.
This string date format is inappropriate for sorting, because the most significant component is day. Only a date format like yyyy/MM/dd can be sorted properly by comparison operator >.
However this is Swift. The closure can contain anything as long as it returns a Bool. You could sort the array with a custom sort algorithm. It splits the strings into components and sorts first year then month then day
let sortedKeys = dictValue.keys.sorted { (date1, date2) -> Bool in
let comps1 = date1.components(separatedBy: "/")
let comps2 = date2.components(separatedBy: "/")
return (comps1[2], comps1[1], comps1[0]) > (comps2[2], comps2[1], comps2[0])
}
print(sortedKeys)
If you want to sort a date, just sort a Date. Date supports Hashable and can be used as a dictionary key, you could map your original dictionary and by using a DateFormatter to format your string keys into Dates then you can easily sort them.
let dictionary = ["08/03/2021": 2, "10/02/2021": 5, "26/04/2021" : 6]
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy" // You should probably adjust other properties of the formatter
let newDict = Dictionary(uniqueKeysWithValues:
dictionary.map { (key, value) -> (Date, Int) in
print("Key: \(key)")
return (formatter.date(from: key)!, value)
})
let sortedDates = newDict.keys.sorted { $0 > $1 }
let value = newDict[sortedDates[0]]

How to save dictionary or array like String data when in Core data? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
Goal:
Store the uid's of users that have been followed, in core data
How can I store these uid's given my use case?
UIDS = Strings
Ideas:
create an array or dict
create a new attribute for each uid
As I understand you want to store an array of some values (uids) in device's memory. From my view there are several easy workarounds here.
If you are not going to store a massive array of data, you can try a chance with UserDefaults. Then your code will look something like this:
let array = ["array", "of", "some", "values"]
// Write to User Defaults
UserDefaults.standard.setValue(array, forKey: "followedIds")
// Read from it
let readArray = UserDefaults.standard.value(forKey: "followedIds") as! [String]
If you're going to store a lot of values then it makes sense to use core data. You can store your values as a String or Data.
In first case you can transform your array into a String like so:
let array = ["array", "of", "some", "values"]
// Specify any separator
let separator = "%^&"
// Join an array into string with the specified separator
let string = array.joined(separator: separator)
// Store it in core data
// Split it later
let readArray = string.split(separator: separator)
In second case you can encode it in a JSON representation and use it later:
let array = ["array", "of", "some", "values"]
// Encode array into Data
var data: Data
do {
data = try JSONEncoder().encode(data)
} catch {
print(error)
}
// Store it in core data
// Decode it later
var readArray: [String]
do {
readArray = try JSONDecoder().decode([String].self, from: data)
} catch {
print(error)
}

sort an array by comparing a string value swift [duplicate]

This question already has answers here:
Sorting array alphabetically with number
(8 answers)
Closed 4 years ago.
I'm trying to sort an array by comparing a string value from two items, the values of the property are a number but of type String. How can I convert them to Int and check which is greater. Current code looks like this.
libraryAlbumTracks = tracks.sorted {
$0.position!.compare($1.position!) == .orderedAscending
}
but values like "13" come before "2" because it's a string. I tried to cast the values to Int but because they are optional, I get the error that operand ">" cannot be applied to type Int?
Please how can I go around this in the sorted function?
Provide the numeric option when using compare. This will properly sort strings containing numbers and it also works if some of the string don't actually have numbers or the strings have a combination of numbers and non-numbers.
libraryAlbumTracks = tracks.sorted {
$0.position!.compare($1.position!, options: [ .numeric ]) == .orderedAscending
}
This avoids the need to convert the strings to Int.
Note: You should also avoid force-unwrapping position. Either don't make them optional if it's safe to force-unwrap them, or safely unwrap then or use ?? to provide an appropriate default when comparing them.
libraryAlbumTracks = tracks.sorted {
guard let leftPosition = $0.position,
let leftInt = Int(leftPosition),
let rightPosition = $1.position,
let rightInt = Int(rightPosition) else {
return false
}
return leftInt > rightInt
}

Int is not convertible to DictionaryIndex<String, String>

Hi guys I am in big trouble. Here is my code:
let listOfQuestionsAndAnswers = ["Who’s Paul?": "An American", "Who’s Joao?": "A Bresilian", "Who’s Riccardo?": "An Italian"]
#IBAction func answerButtonTapped(sender: AnyObject){
for (Question, rightAnswer) in listOfQuestionsAndAnswers {
questionField.text = listOfQuestionsAndAnswers[currentQuestionIndex]
if currentQuestionIndex <= listOfQuestionsAndAnswers.count
{
currentQuestionIndex = (++currentQuestionIndex) % listOfQuestionsAndAnswers.count
answerBut.setTitle("ANSWER", forState: UIControlState.Normal)
}
else
{
(sender as UIButton).userInteractionEnabled = false
}
I am getting the error Int is not convertible to DictionaryIndex and I don't understand what that means. Shouldn't I be able to access my dictionary by index.
hard to say, what are you trying to do. here is an example, how to access dictionary (unordered collection of key, value pairs) by different ways
let dict = ["a":"A","b":"B"]
for (k,v) in dict {
print(k,v)
}
/*
b B
a A
*/
dict.forEach { (d) -> () in
print(d.0,d.1)
}
/*
b B
a A
*/
dict.enumerate().forEach { (e) -> () in
print(e.index,e.element,e.element.0,e.element.1)
}
/*
0 ("b", "B") b B
1 ("a", "A") a A
*/
dict.indices.forEach { (di) -> () in
print(dict[di],dict[di].0,dict[di].1)
}
/*
("b", "B") b B
("a", "A") a A
*/
dict.keys.forEach { (k) -> () in
print(k,dict[k])
}
/*
b Optional("B")
a Optional("A")
*/
The other answers here are correct, and I particularly like Daniel Leonard's answer, as it provides a good way to organize your questions and answers.
First of all, I would like say that listOfQuestionsAndAnswers is not a list - it's actually a Dictionary. In particular, it is a Dictionary<String, String>, i.e. it's key must be a string, and it's value must be a string.
But not to worry! The Dictionary type conforms to the protocol CollectionType, which means that we can use 'traditional' means to index it. It does not mean we can access it with an Int. But we can access it with an index of type Dictionary.Index.
How is that done?
Grab the index from the dictionary.
Iterate over the contents, by using the index to grab the values.
Get the next index by calling index.successor()
Check that the index is not invalid, by checking that it is not equal to the end index.
Code
// Not a list of questions, it's a dictionary.
let questionsAndAnswers = ["Who’s Paul?": "An American",
"Who’s Joao?": "A Bresilian",
"Who’s Riccardo?": "An Italian"]
var index = questionsAndAnswers.startIndex
while index != questionsAndAnswers.endIndex {
let question = questionsAndAnswers[index].0
let answer = questionsAndAnswers[index].1
print("Question: \(question); Answer: \(answer)")
index = index.successor()
}
You can see that when we access the contents of the dictionary using an index, we retrieve a tuple. .0 is the key, and .1 is the value, in this case, corresponding to the question and answer respectively.
Note: Indexes from a dictionary are not guaranteed to be ordered - they could come out in a different order every time! If you want an ordered collection, then you should use an array.
So there are a couple of things that are going on here that my not be in the way you want them. First your main question you are trying to iterate this dictionary like it is a list ie let list = [apple, banana, orange] this list has an index that you could iterate through similarly to what you are doing.
for fruit in list {
print(fruit)
}
This would print:
apple
banana
orange
Where as dictionarys are more key:value based.
struct food {
catigory:String()
type:String()
}
What I would suggest is that you make a list of dictionary but structure you data a little differently more like
let listOfQuestionAnswers =
[["question":"Who’s Paul?","answer":"An American"],
["question":"Who’s Joao?","answer":"A Bresilian"],
["question":"Who’s Riccardo?","answer": "An Italian"]]
so this lets you have a list of dictionary each dictionary has two keys (question and answer) and now you can iterate through all of them and you questions and answers will be paired together.
or alternatively you could make a struct to represent your question answer combo then have a list of those structs. This makes things nice because you can use dotsyntax to access the items within the struct
struct dictionaryStruct {
var question:String
var answer:String
}
var listOfQuestionAnswers = [dictionaryStruct]()
func makeList(quest:String,answer:String){
let dict = dictionaryStruct.init(question: quest, answer: answer)
listOfQuestionAnswers.append(dict)
}
makeList("Who’s Paul?", answer: "An American")
makeList("Who’s Joao?", answer: "A Bresilian")
makeList("Who’s Riccardo?", answer: "An Italian")
for entry in listOfQuestionAnswers {
print("\(entry.question), \(entry.answer)")
}
---------- Console Output:
Who’s Paul?, An American
Who’s Joao?, A Bresilian
Who’s Riccardo?, An Italian
let me know if you have any other questions? 🤓
To solve your score logic you have two lists. The user selects an answer and you already know the index of the question so you just need to check that their answer is the same as the answer for the question at the same index. so look kinda like this
if answer == listOfAnswers[currentQuestionIndex]{
score ++
}

Get the object with the most recent date

I have an array of objects of type Thing:
class Thing: NSObject {
var data: String
var type: String
var created: NSDate
}
These things have an NSDate property called created. My aim is to write a function that reads the created property of every thing in the array and returns the thing that has the most recent date. The function looks like this:
public func getLastSwipe(list: Array<Thing>) -> Thing {
return someThing
}
Another approach is using Swift's .max, like this:
dates.max(by: <)
The following is my old answer. The above is updated in feb 2023.
let mostRecentDate = dates.max(by: {
$0.timeIntervalSinceReferenceDate < $1.timeIntervalSinceReferenceDate
})
This is the most performant solution I've found.
Returns the sequence’s most recent date if the sequence is not empty; otherwise, nil.
You could use reduce if you wanted. This will find the object with the highest timestamp.
var mostRecent = list.reduce(list[0], { $0.created.timeIntervalSince1970 > $1.created.timeIntervalSince1970 ? $0 : $1 } )
If your dates are not all in the past, you'll have to also compare against the current date to determine a cutoff. If your dates are all in the future, you'll want to switch the > to < to find the next future date (lowest timestamp).
You can sort the array, then find the first/last element. For example...
let objects: [Thing] = ... //Set the array
let mostResent = array.sorted { (firstThing, secondThing) -> Bool in
firstThing.created.timeIntervalSince1970 > secondThing.created.timeIntervalSince1970
}.first
This will return the most resent Thing as an Optional (because there is no guarantee that the array is not empty. If you know that the array is not empty, then you can end that line with .first!

Resources