Creating an array of objects for dictionaries 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 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)?
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 &amp; 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.values