Dani Turc
Dani Turc

Reputation: 39

Lose some if/else if statments! What should I use to replace them ? - SWIFT -

I'm making a unit convertor, I'm using an array like this :

private var subContentArray = [["Millimeter", "Centimeter", "Meter",    "Kilometer", "Foot", "Yard", "Mile"],
["Milliliter", "Centiliter", "Liter", "Gallon", "Quart", "Pint", "Fluid ounce"],
["Milligram", "Centigram", "Gram", "Kilogram", "Stone", "Pound", "Ounce"]]

And I'm using a picker that is set with 3 piker fields :

func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)  {

    if pickerView.tag == 0  {
        currentSelection = row
        self.pickerGeneral.text = self.pickerGeneral1[row]
        self.view.endEditing(true)
    } else if pickerView.tag == 1 {
        self.pickerTextField.text = self.pickerSubContent[currentSelection][row]
        self.view.endEditing(true)
    } else if pickerView.tag == 2 {
        self.pickedTextField2.text = self.pickerSubContent[currentSelection][row]
        self.view.endEditing(true)
    }
}

I wanna change the 2nd and 3rd picker by the General picker. So far everything is good, but then I wanna make the calculations and I was trying to do something like this :

func updateOutput(){
    //var res: Double
    var result: Double
    if input.text!.isEmpty {
        return

    } else if pickerTextField.text! == "Centimeter" && pickedTextField2.text == "Mile" {
            result = (Double(self.input.text!)! / 160934.4)
            self.output.text! = String("\(result)")

    } else if pickerTextField.text! == "Mile" && pickedTextField2.text! == "Centimeter" {
            result = (Double(self.input.text!)! * 160934.4)
            self.output.text! = String("\(result)")

    } else if pickerTextField.text! == "Centimeter" && pickedTextField2.text == "Millimeter" {
            result = (Double(self.input.text!)! * 10)
            self.output.text! = String("\(result)")

    } else if pickerTextField.text! == "Millimeter" && pickedTextField2.text! == "Centimeter" {
            result = (Double(self.input.text!)! / 10)
            self.output.text! = String("\(result)")

    }

...until I realize that if I'm gonna keep writing if/else if...i won't end soon and it will look really messy!

I'm still a noob in to OOP and this branch but willing to learn...so, any help, with example and explains will be very well appreciated!!!!

Upvotes: 0

Views: 64

Answers (3)

bjornruffians
bjornruffians

Reputation: 701

I understand why your array is broken into rows: row 0 is length, row 1 is volume, row 2 is weight/mass.

You could create a corresponding variable with the conversions between each pair:

private var conversions = [[10, 100, 1000, 0.0003048, 3, 1760] .....

Then, once a start and end unit are selected within a row, iterate over conversions and apply each one until the desired unit is reached. (Be sure to multiply if going in one direction, and divide if going the other way.)

For example, going from Millimeters to Feet, you would perform input / 10 / 100 / 1000 / 0.0003048. If going from Feet to Millimeters, you would perform input * 0.0003048 * 1000 * 100 * 10

This would be simple enough if your unit list isn't going to change often. Your if-statement could be a for-loop iterating from the start unit index to the end unit index and applying the conversion factor at each step.

Although this isn't very OO, I think it's straightforward for people familiar with unit analysis.

Upvotes: 0

molbdnilo
molbdnilo

Reputation: 66371

You can use a dictionary:

let factors : [(String, String): Double] = [("Centimeter", "Mile"): 1.0/160934.4, 
                                            ("Mile", "Centimeter"): 160934.4, 
                                            <and more...>]

and your function becomes

func updateOutput(){
    if input.text!.isEmpty {
        return
    }
    let result = (Double(self.input.text!)! * factors[(pickerTextField.text, pickerTextField2.text)]  
    self.output.text! = String("\(result)")
}

(Warning: completely untested.)

Using a common base unit would be even better.

let in_meters: [String: Double] = ["Centimeter": 0.01,
                                   "Meter": 1.0,
                                   "Mile": 1609.344,
                                   <and so on...>]

func updateOutput(){
    if input.text!.isEmpty {
        return
    }
    let meters = (Double(self.input.text!)! * in_meters[pickerTextField.text]
    let result =  meters * 1.0 / in_meters[pickerTextField2.text]  
    self.output.text! = String("\(result)")
}

Upvotes: 1

Alexander
Alexander

Reputation: 63271

First of all, it doesn't make sense for Centimeter and Mile to exist within the same array. Arrays aren't an appropriate data structure choice for that, anyway.

I suggest you use a separate enumeration for each type of unit (mass, volume, length, etc.). Enumerations are a way to store a finite set of related values. In this case, you might have a LengthUnit enumeration:

enum LengthUnit {
    case Millimeter
    case Cenitmeter
    case Meter
    case Kilometer
    //...
}

Now, rather than defining the explicit relationship between every possible pair of units, you would define (in the enumeration) the conversion factor between each unit and a standard base unit (such as the meter). You would then use this standard base unit as a way to convert from the given unit to the desired unit.

For example, if I wanted to convert 1.5km to miles, I would first convert 1.5km to meters using the conversion from km to meters (1km = 1000 meters), to get 1500. Then I would use the reciprocal of the conversion from miles to meters (1 mile = 1609.34 meters). In all, it would be: 1.5km * (1000m/km) * (1 mile/1609.34m) = 0.93 miles

Upvotes: 1

Related Questions