patternswiftMinor
Creating an array of objects for dictionaries
Viewed 0 times
objectscreatingarrayfordictionaries
Problem
I wrote a piece of code that creates an array of objects based on the array of dictionaries. The problem is that my solution uses multiple loops imperatively.
Let's consider the following case. There is a class called
I obtain the data as an array of dictionaries:
Now is the tricky part. What is the best way to turn this data into an array of
The purpose is to create objects without repeating their names and provide each object with an dosage array which consist of (dose, time) tuples):
I believe there is a better way to do that, probably in a few lines of code. I tried to figure out something with basic FP functions such as map or filter but I end up with nothing.
How can I perform the operation in an elegant way (probably using more advanced functional programming)?
Let's consider the following case. There is a class called
Drug:class Drug {
var name:String
var dosage:[(dose:String,time:String)]
init(name:String){
self.name = name
self.dosage = []
}
}I obtain the data as an array of dictionaries:
var array = [["drugName":"Amotaks", "time":"17.00", "dose":"5"], ["drugName":"Amotaks", "time":"18.00", "dose":"5"], ["drugName":"Amotaks", "time":"18.00", "dose":"5"], ["drugName":"Amotaks", "time":"17.00", "dose":"5"], ["drugName":"Claritin", "time":"17.00", "dose":"5"], ["drugName":"Claritin", "time":"18.00", "dose":"5"]]Now is the tricky part. What is the best way to turn this data into an array of
Drug objects? I am not happy with my solution because it uses nested loops and additional helper arrays thus it's very inefficient.The purpose is to create objects without repeating their names and provide each object with an dosage array which consist of (dose, time) tuples):
var DrugArray:[Drug] = []
var drugs:[String] = []
for i in array
{
if contains(drugs, i["drugName"]! as String){
continue
}
else{
drugs.append(i["drugName"]!)
DrugArray.append(Drug(name: i["drugName"]!))
}
}
for a in DrugArray{
for i in array {
if i["drugName"]! == a.name
{
a.dosage.append(dose: i["dose"]!, time: i["time"]!)
}
}}I believe there is a better way to do that, probably in a few lines of code. I tried to figure out something with basic FP functions such as map or filter but I end up with nothing.
How can I perform the operation in an elegant way (probably using more advanced functional programming)?
Solution
var DrugArray:[Drug] = []
var drugs:[String] = []
for i in array {
if contains(drugs, i["drugName"]! as String) {
continue
} else {
drugs.append(i["drugName"]!)
DrugArray.append(Drug(name: i["drugName"]!))
}
}
for a in DrugArray {
for i in array {
if i["drugName"]! == a.name {
a.dosage.append(dose: i["dose"]!, time: i["time"]!)
}
}
}So, now that we've cleaned up the code and made it more readable, let's talk about some refactoring we can do.
The first thing I notice is that your nested loop at the bottom is entirely unnecessary. We can do all of that logic in the top loop.
We're also doing a lot of forced unwrapped... which you really don't want to do. All this is going to do is result in a crash in your app eventually.
We're also using single letter variable names which invokes no meaning to the reader. These loops are short... for now. If & when the contents of the loop grows (the top loop is already close to being at the point), you'll start to see the value in having meaningful variable names. It helps you remember exactly what the variable represents. Better variable names is just one part of making your code self-documenting.
And ultimately, as Mario Zahone's answer points out, what we clearly need is a dictionary rather than an array (and a separate array to keep track of what's already been tracked).
Finally, repeatedly
appending to a collection almost always results in poor performance. If there's not enough space in the collection's current memory location for one more item, the entire collection has to be copied into a new memory location, which is an expensive task. In a loop with lots of iterations, this is likely to happen multiple times. So, when we can guess at a good minimum size, we want to go ahead and make sure our collection is allocated in a large enough memory space.So, taking all of these things into account, the final implementation of our code probably looks something like this:
var drugs = [String:Drug](minimumCapacity: array.count)
for drugDictionary in array {
if let
drugName = drugDictionary["drugName"],
drugDose = drugDictionary["dose"],
drugTime = drugDictionary["time"] {
drugs[drugName] = drugs[drugName] ?? Drug(name: drugName)
drugs[drugName]?.dosage.append(dose: drugDose, time: drugTime)
}
}If we really want an array, we can always fetch one from the dictionary:
let drugArray = drugs.valuesCode Snippets
var DrugArray:[Drug] = []
var drugs:[String] = []
for i in array {
if contains(drugs, i["drugName"]! as String) {
continue
} else {
drugs.append(i["drugName"]!)
DrugArray.append(Drug(name: i["drugName"]!))
}
}
for a in DrugArray {
for i in array {
if i["drugName"]! == a.name {
a.dosage.append(dose: i["dose"]!, time: i["time"]!)
}
}
}var drugs = [String:Drug](minimumCapacity: array.count)
for drugDictionary in array {
if let
drugName = drugDictionary["drugName"],
drugDose = drugDictionary["dose"],
drugTime = drugDictionary["time"] {
drugs[drugName] = drugs[drugName] ?? Drug(name: drugName)
drugs[drugName]?.dosage.append(dose: drugDose, time: drugTime)
}
}let drugArray = drugs.valuesContext
StackExchange Code Review Q#97707, answer score: 5
Revisions (0)
No revisions yet.