Making it to the promised land of O(n)
To reproduce your code in a playground, a Media
struct could be defined this way:
struct Media {
let mediaUrl: String
let postTimeStamp: String?
let timeStamp: String //A double would be more appropriate
init(mediaUrl: String, timeStamp: String, postTimeStamp: String? = nil) {
self.mediaUrl = mediaUrl
self.timeStamp = timeStamp
self.postTimeStamp = postTimeStamp
}
}
Let's suppose the value of imageUrlString
is this:
let imageUrlString: [String: Media] =
["media1": Media(mediaUrl: "URL", timeStamp: "573889179.6991431", postTimeStamp: "573889189.73954"),
"media4": Media(mediaUrl: "URL", timeStamp: "573889185.750419"),
"media2": Media(mediaUrl: "URL", timeStamp: "573889181.49576"),
"media3": Media(mediaUrl: "URL", timeStamp: "573889183.89598")]
var values = [Media]()
Your code works by relying on the chance of having the last character read from the imageUrlString
dictionary, equal the order of the element you want to append to the values
array.
Bear in mind that a dictionary is an unordered collection. And unless mutated, the order of elements stays the same. The worst case would be when, reading elements from the dictionary, yields elements in a reversed "order". In this case, you'll have to read from the dictionary n*(n+1)/2
times, in order to build your values
array. In other terms, this algorithm is has O(n²)
time complexity (worst case), O(n)
best case, and is not the proper Counting Sort Algorithm which is O(n)
.
Here is an attempt to make this O(n)
:
let tempo = Media(mediaUrl: "", timeStamp: "")
var values = Array(repeating: tempo, count: imageUrlString.count)
var keys = Array(repeating: "", count: imageUrlString.count)
for entry in imageUrlString {
let index = Int(String(entry.key.last!))! - 1 //Force-unwrapping for brevity
(keys[index], values[index]) = entry
}
Robustness
The code in question relies on external facts that are not checked in code. For example:
- If
imageUrlString
is empty, Done
will never be mutated and thus the outer loop will be infinite;
- The order of the elements in the result array relies on the last character in a string;
- The last character in all the keys has to exist and be numerical for
j
to be incremented. Otherwise, you're in for another infinite loop;
- Breaking the outer loop relies on the digits at the end of the keys go from 1 to at least
imageUrlString.count
.
Breaking an outer loop
Instead of mutating the variable Done
(which shouldn't be uppercased since it's an instance, not a class/type/struct/enum/etc), you can break from a nested loop this way:
OuterLoop: while true {
for i in imageUrlString {
...
if imageUrlString.count == j {
break OuterLoop
}
...
}
}
Straight forward sorting
In Swift 5, Timsort is the algorithm that is going to be used by the standard library while sorting. It has better time complexity than Introsort and is more versatile and less memory greedy than O(n) sorting algorithms.
So, why not just use it to sort the entries in imageUrlString
by timeStamp
or a some other more reliable criteria?
let values = imageUrlString.values
.sorted(by: { $0.timeStamp < $1.timeStamp })
(If you're sure that timeStamp
s represent real numbers, you could cast them to Double before comparing them)
imageUrlString
is empty, you'll never beDone
; Force-unwrappingi.key.last
calls for trouble; The logic that makes sure thatvalues
has at least 4 elements isn't clear enough. \$\endgroup\$