Cave007 Cave007 - 4 months ago 20
Swift Question

Textfield as input in average function

I recently wanted to code an Average-Calculator.
My plan was to build a

UITextField
in which you can type Numbers separated by commas... By pressing the 'Calculate' button the App should calculate the Average of the Numbers above and give them out by setting a labeltext to the average.
So I wrote my average function and received this error message:


Can not convert value of type 'UITextField' to expected element type 'Double'.


This is my Code:

@IBOutlet var Input: UITextField!
@IBOutlet var Output: UILabel!
@IBAction func Calculate(sender: AnyObject) {

var grades:[Double] = [Input]
func average(nums: [Double]) -> Double {

var total = 0.0
for grade in nums{

total += Double(grade)
}

let gradesTotal = Double(nums.count)
let average = total/gradesTotal
return average
}

let Average = average(grades)

Output.text = "Average: \(Average)"
}


Can you help me with my idea?
Is there a better way to get an input?

Answer

Please use lower camel case for variables...

In this line:

var grades:[Double] = [Input]

Input is an instance of UITextField, so you are trying to assign a single-element Array<UITextField> to Array<Double>. You see you cannot do such sort of things.

If you want to accept a text which contains Numbers separated by commas, you need to explicitly convert the text to [Double].

To simplify, let's just ignore the nil or non-numeric values. Then you need to change your code as:

@IBOutlet var input: UITextField!
@IBOutlet var output: UILabel!
@IBAction func calculate(sender: AnyObject) {

    var grades: [Double] = (input.text ?? "").componentsSeparatedByString(",").flatMap{Double($0)}
    func average(nums: [Double]) -> Double {

        var total = 0.0
        for grade in nums{

            total += Double(grade)
        }

        let gradesTotal = Double(nums.count)
        let average = total/gradesTotal
        return average
    }

    let averageValue = average(grades)

    output.text = "Average: \(averageValue)"
}

The basic idea of this line:

var grades: [Double] = (input.text ?? "").componentsSeparatedByString(",").flatMap{Double($0)}

is well-described in Lu_'s answer. Mine is just a little safer version.


(Addition) Some explanation about safety:

UITextFields property text is of type String?, so you should think it can be nil. Giving a default value for nil with ?? operator.

And using Double($0)! may crash your app, as Double($0) will return nil for non-numeric strings.

Writing these reminded me one more crash case.

When gradesTotal == 0, the code above will crash with division by zero.

(The default value does not work well for "safety" in the code above...)

So, one more step ahead to safety:

@IBAction func calculate(sender: AnyObject) {

    var grades: [Double] = (input.text ?? "").componentsSeparatedByString(",").flatMap{
        Double($0.stringByTrimmingCharactersInSet(.whitespaceCharacterSet()))
    }
    func average(nums: [Double]) -> Double? {

        var total = 0.0
        for grade in nums{

            total += Double(grade)
        }

        let gradesTotal = Double(nums.count)
        if gradesTotal > 0 {
        let average = total/gradesTotal
            return average
        } else {
            return nil
        }
    }

    if let averageValue = average(grades) {
        output.text = "Average: \(averageValue)"
    } else {
        output.text = "Average not available"
    }
}
Comments