Swift:如何使用更优雅的方法重构我的代码?

时间:2015-07-22 15:50:59

标签: ios arrays swift functional-programming

我编写了一段代码,根据字典数组创建了一个对象数组。问题是我的解决方案,它是以非常无条件的方式使用多个循环强制写的。

让我们考虑以下情况。有一个名为" Drug"

的课程
class Drug {

var name:String
var dosage:[(dose:String,time:String)]

init(name:String){

    self.name = name
    self.dosage = []
    }
}

我将数据作为字典数组获取,以下是示例:

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"]]

现在是棘手的部分,将这些数据转换为Drug对象数组的最佳方法是什么?我对我的解决方案不满意,因为它使用嵌套循环,其他辅助数组,因此效率非常低。 目的是创建对象而不重复其名称,并为每个对象提供由(剂量,时间)元组组成的剂量阵列

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"]!)

            }

        }}

我相信有更好的方法可以做到这一点,可能只需几行代码。我试图找出具有基本FP功能的东西,例如地图或过滤器,但我最终没有任何东西。

我的问题是:如何以优雅的方式执行上面编码的操作(可能使用更高级的功能编程)?

2 个答案:

答案 0 :(得分:2)

首先,你应该更好地重构你的对象,因此它更容易和更易读,可能是这样的:

struct Dosage {

    // Declare variables
    let dose : String
    let time : String

    // Get dosage from drug data
    init(drugData : DrugData) {
        self.dose = drugData["dose"]!
        self.time = drugData["time"]!
    }
}


class Drug {

    let name : String
    var dosage = [Dosage]()

    init(drugData : DrugData) {

        // Assign name
        self.name = drugData["dose"]!

        // Append dosage
        self.appendDosage(drugData)
    }


    func appendDosage(drugData : DrugData) {

        self.dosage.append(Dosage(drugData: drugData))
    }
}

请注意,我正在使用TypeAlias来掩盖字典的定义,如下所示:

typealias DrugData = [String : String]

定义药物储存:

var drugs = [String : Drug]()

现在,当您对所有定义进行整理时,您可以改进算法:

func createDrugs() {

    // Your drug definitions
    var array = [["drugName":"Amotaks", "time":"17.00", "dose":"5"]]

    // Iterate through them
    for drugDefinition in array {

        // Check if there is a name, if yes, get it
        if let name = drugDefinition["drugName"] {

            // If drug exists, just update it, otherwise create it
            if let drug = drugs[name] {
                drug.appendDosage(drugDefinition)
            } else {
                drugs[name] = Drug(drugData: drugDefinition)
            }
        }
    }
}

如您所见,我使用了使用药物名称作为关键词的词典。使用密钥搜索速度非常快,比遍历整个数组要快得多。我相信还有更多的改进可能性,但我认为现在适合你了:)希望它有所帮助!

答案 1 :(得分:2)

首先,让我们通过让您的药物类可打印来了解我们的工作方式:

class Drug : CustomStringConvertible {

    var name:String
    var dosage:[(dose:String,time:String)]

    init(name:String){
        self.name = name
        self.dosage = []
    }

    var description:String {
        return "\(name):\(dosage)"
    }
}

然后我的第一次尝试:

let 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"]]

var d = [String:Drug]()
for drug in array.map ({
    d -> Drug in
    let drug = Drug(name:d["drugName"]!)
    drug.dosage = [(dose:d["dose"]!, time:d["time"]!)]
    return drug
}) {
    let name = drug.name
    if d[name] == nil {
        d[name] = drug
    } else {
        d[name]!.dosage += drug.dosage
    }
}

let result = Array(d.values)
print(result) 
// [Claritin:[("5", "17.00"), ("5", "18.00")], Amotaks:[("5", "17.00"), ("5", "18.00"), ("5", "18.00"), ("5", "17.00")]]