Swift: how to change a integer variable name within a method and reassign its value - swift

I am a complete beginner at Swift/programming and am making a noughts and crosses app as part of an online course. I wanted to do this on my own before I saw the solution so my logic may be a bit weird.
I have a function which is called each time a button is pressed (there are 9, one for each square). The function acts to:
i) update the number of turns (which allows me to see who's go it is)
ii) change the picture to X or O
iii) deactivate the button after each turn
iv) calculate is anyone has won by changing the value of that squares potential lines. The numbers I have chosen to do this are 3 and 4 (for noughts and crosses), hence the c = 3 or 4 below. This means that if any winning line adds up to 9 or 12, the game stops as someone has won.
I have 9 variables (Int) that hold the score for each square - they are a1o, a2o, a3o, b1o... i.e all end in "o". In the function I want to add the string of "a1" in front of the "o", meaning each button is only relevant to itself with only one line of code above the function (within the button parentheses).
The function looks like this at the moment; calling it by: button(a1, buttonValue: "a1")
func button(buttonName: UIButton, buttonValue: NSString) -> String {
let a = buttonName
var b = buttonValue
b = (b as String) + "o"
print(b)
// the above prints "a1o", the name of the variable, but it is a string...
index += 1
print("Go number \(index)")
if index % 2 == 0 {
// show a nought
a.setImage(UIImage(named: "nought.png"), forState: UIControlState.Normal)
c = 3
// the above line doesn't work as its a string, but I am attempting to set the squares value to 3 (i.e. a1o = 3)
winner()
a.userInteractionEnabled = false
} else {
// show a cross
a.setImage(UIImage(named: "cross.png"), forState: UIControlState.Normal)
c = 4
// the above line doesn't work as its a string
winner()
a.userInteractionEnabled = false
}
return "done"
}
What I can't figure out is how to change the common stem of the variable *a1*o by not making it a string, or if I do, how I convert it back to a variable.
I am struggling with definitions and as a result looking for an answer has been hard. The variable a1o is an integer, but how do I refer to a1o itself?
Thank you in advance,
Sam

It is not possible to access variables dynamically by name like you want to do. However, there are better ways to accomplish the same goal. How about a two-dimensional array?
var scores: [[Int]] = [[0,0,0], [0,0,0], [0,0,0]]
scores[0][1] = 4 //this is the new version of "a2o = 4"
Unlike with variable names, the indices to the array ("0" and "1" in this case) can be dynamically chosen. Or, if you really want to keep your buttons' values exactly the same, you could use a dictionary:
var scores: [String: Int] = [:]
scores[buttonValue as String] = 4

Related

how can I assign the same random number to 2 variables in swift?

I am building an app that the user selects a multiplication table. Then it gives random numbers to multiplicate with the number they select. for example, if the user selects "1". the questions shown would be "1 x 1", or "1 x 8".
The problem is that I need to assign the same random number to 2 variables. The one that will be shown on the question and the one used to calculate the result.
I thought of something like this, but the random number is different on each variable. What can I do to use the same random number generated on 2 variables?
func game() -> (String, Int) {
let randomNumber = multiplicate.randomElement()
switch selectedQuestion {
case 0:
return ("1 × \(randomNumber!)", 1 * randomNumber!)
default:
return ("", 0)
}
}
Not exactly sure what you're asking but you could do something like this:
let number = Int.random(in: 0..<10)
let number2 = number
but I don't think there is a need to create a new variable for this. The whole idea of variables is that you can save some value and then use it later so there isn't really a need to create number2 here.

Left side of mutating operator isn't mutable: 'gpa' is a 'let' constant

I am currently struggling with an error for a homework assignment in my coding class. We are creating a loop that loops through an array of gpa values and then adds it to a variable named totalGradePoints. The problem is that I am coming across an error when the loop runs:
Left side of mutating operator isn't mutable: 'gpa' is a 'let' constant
The error is on this line:
var totalGradePoints = Double()
for gpa in gpaValues {
let averageGPA: Double = gpa += totalGradePoints
}
Here is my full code:
//: Playground - noun: a place where people can play
import UIKit
// You are the university registrar processing a transfer student's transcripts that contains grades that are a mix of letters and numbers. You need to add them to our system, but first you need to convert the letters into grade points.
// Here's an array of the student's grades.
var transferGrades: [Any] = ["C", 95.2, 85, "D", "A", 93.23, "P", 90, 100]
// To prepare for converting the letters to numerical grades, create a function that returns a double, inside which you create a switch that will convert an A to a 95, B to 85, C to 75, D to 65, , P (for passing) to 75. Everything else will be a zero.
func gradeConverter(letterGrade: String) -> Double {
switch letterGrade {
case "A":
return 95
case "B":
return 85
case "C":
return 75
case "D":
return 65
case "P":
return 75
default: // Is this where everything else is zero?
return 0
}
}
// Create a new array called convertedGrades that stores doubles.
var convertedGrades: [Double] = [98.75, 75.5, 60.0, 100.0, 82.25, 87.5]
// Loop through the transferGrades array, inspecing each item for type and sending strings (your letter grades) to the function you just made and storing the returned double in your convertedGrades array. If your loop encounters a double, you can place it directly into the new array without converting it. It it encounters an int, you will need to convert it to a double before storing it. Print the array. (You may notice that some of your doulbes are stored with many zeros in the decimal places. It's not an error, so you can ignore that for now)
for grade in transferGrades {
if let gradeAsString = grade as? String {
gradeConverter(letterGrade: gradeAsString)
} else if let gradeAsDouble = grade as? Double {
transferGrades.append(gradeAsDouble)
} else if let gradeAsInt = grade as? Int {
Double(gradeAsInt)
transferGrades.append(gradeAsInt)
}
}
print(transferGrades)
// Now that we have an array of numerical grades, we need to calculate the student's GPA. Create a new array called GPAValues that stores doubles.
var gpaValues: [Double] = [2.5, 3.0, 4.0, 3.12, 2.97, 2.27]
// Like with the letter conversion function and switch you created before, make a new function called calculateGPA that takes a double and returns a double. Inside your function, create another switch that does the following conversion. Grades below 60 earn zero grade points, grades in the 60s earn 1, 70s earn 2, 80s earn 3, and 90s and above earn 4.
func calculateGPA(gpaValue: Double) -> Double {
switch gpaValue {
case 0..<59:
return 0
case 60...69:
return 1
case 70...79:
return 2
case 80...89:
return 3
case 90..<100:
return 4
default:
return 0
}
}
// Loop through your convertedGrades array and append the grade point value to the GPAValues array. Because your calculateGPA function returns a value, you can use it just like a varialbe, so rather than calculate the grade points and then put that varialbe in your append statement, append the actual function. i.e. myArray.append(myFunction(rawValueToBeConverted))
for gpa in gpaValues {
gpaValues.append(calculateGPA(gpaValue: gpa))
}
// Finally, calculate the average GPA by looping through the GPA and using the += operator to add it to a variable called totalGradePoints. You may need to initialize the variable before using it in the loop. i.e. var initialized = Double()
var totalGradePoints = Double()
for gpa in gpaValues {
let averageGPA: Double = gpa += totalGradePoints
}
// Count the number of elements in the array (by using the count method, not your fingers) and store that number in a variable called numberOfGrades. Pay attention to creating your variables with the right types. Swift will tell you if you're doing it wrong.
var numberOfGrades: Int = gpaValues.count
// Divide the totalGradePoints by numberOfGrades to store in a variable called transferGPA.
var transferGPA: Double = Double(totalGradePoints) / Double(numberOfGrades)
// Using code, add one numerical grade and one letter grade to the transferGrades array that we started with (i.e. append the values rather than manualy writing them into the line at the beginning of this file) and check that your transferGPA value updates. You'll need to append the new grades on the line below the existing transferGrades array so that your changes ripple through the playground.
transferGrades.append(97.56)
transferGrades.append("B")
averageGPA must be define using the var keyword to make it mutable later on when summing up the values.
var averageGPA: Double = 0
for gpa in gpaValues {
averageGPA += gpa
}
averageGPA = averageGPA / Double(gpaValues.count)
Recall the average is calculated by summing up the score and dividing the number of scores.
Defining something with let means that the following will be a constant.
let answer: Int = 42
answer = 43 /* Illegal operation. Cannot mutate a constant */
Left side of mutating operator isn't mutable: 'gpa' is a 'let' constant
The problem is that gpa is a constant, you can't modify its value. And the "+=" operator means "increase gpa's value by totalGradePoints", it is trying to increase the value of gpa. What you probably mean to do is make averageGPA equal the sum of gpa and totalGradePoints. For that you would do this:
let averageGPA: Double = gpa + totalGradePoints

Can this be more Swift3-like?

What I want to do is populate an Array (sequence) by appending in the elements of another Array (availableExercises), one by one. I want to do it one by one because the sequence has to hold a given number of items. The available exercises list is in nature finite, and I want to use its elements as many times as I want, as opposed to a multiple number of the available list total.
The current code included does exactly that and works. It is possible to just paste that in a Playground to see it at work.
My question is: Is there a better Swift3 way to achieve the same result? Although the code works, I'd like to not need the variable i. Swift3 allows for structured code like closures and I'm failing to see how I could use them better. It seems to me there would be a better structure for this which is just out of reach at the moment.
Here's the code:
import UIKit
let repTime = 20 //seconds
let restTime = 10 //seconds
let woDuration = 3 //minutes
let totalWOTime = woDuration * 60
let sessionTime = repTime + restTime
let totalSessions = totalWOTime / sessionTime
let availableExercises = ["push up","deep squat","burpee","HHSA plank"]
var sequence = [String]()
var i = 0
while sequence.count < totalSessions {
if i < availableExercises.count {
sequence.append(availableExercises[i])
i += 1
}
else { i = 0 }
}
sequence
You can overcome from i using modulo of sequence.count % availableExercises.count like this way.
var sequence = [String]()
while(sequence.count < totalSessions) {
let currentIndex = sequence.count % availableExercises.count
sequence.append(availableExercises[currentIndex])
}
print(sequence)
//["push up", "deep squat", "burpee", "HHSA plank", "push up", "deep squat"]
You can condense your logic by using map(_:) and the remainder operator %:
let sequence = (0..<totalSessions).map {
availableExercises[$0 % availableExercises.count]
}
map(_:) will iterate from 0 up to (but not including) totalSessions, and for each index, the corresponding element in availableExercises will be used in the result, with the remainder operator allowing you to 'wrap around' once you reach the end of availableExercises.
This also has the advantage of preallocating the resultant array (which map(_:) will do for you), preventing it from being needlessly re-allocated upon appending.
Personally, Nirav's solution is probably the best, but I can't help offering this solution, particularly because it demonstrates (pseudo-)infinite lazy sequences in Swift:
Array(
repeatElement(availableExercises, count: .max)
.joined()
.prefix(totalSessions))
If you just want to iterate over this, you of course don't need the Array(), you can leave the whole thing lazy. Wrapping it up in Array() just forces it to evaluate immediately ("strictly") and avoids the crazy BidirectionalSlice<FlattenBidirectionalCollection<Repeated<Array<String>>>> type.

Swift 3 subscript range works for first cluster but not for middle

I'm trying to figure out why the following works on the first string cluster (character) but not on a second one. Perhaps the endIndex cannot be applied on another String?
let part = "A"
let full = "ABC"
print(full[part.startIndex ... part.startIndex]) // "A"
print(full[part.endIndex ... part.endIndex]) // "" <- ???
print(full[part.endIndex ... full.index(after: part.endIndex)]) // "B"
bSecond should hold "B", but instead is empty. But the proof that one string index works on another is that the last statement works.
EDIT:
Assuming full.hasPrefix(part) is true.
Swift puzzles.
You cannot use the indices of one string to subscript a different
string. That may work by chance (in your first example) or not
(in your second example), or crash at runtime.
In this particular case, part.endIndex (which is the "one past the end position" for the part string) returns
String.UnicodeScalarView.Index(_position: 1), _countUTF16: 0)
with _countUTF16: (which is the "count of this extended grapheme cluster in UTF-16 code units") being zero, i.e. it describes
a position (in the unicode scalar view) with no extent. Then
full[part.endIndex ... part.endIndex]
returns an empty string. But that is an implementation detail
(compare StringCharacterView.swift). The real answer is just "you can't do that".
A safe way to obtain the intended (?) result is
let part = "A"
let full = "ABC"
if let range = full.range(of: part) {
print(full[range]) // A
if range.upperBound != full.endIndex {
print(full[range.upperBound...range.upperBound]) // B
}
}

In Swift 3.0 How to make one character in a string move backward when you typing?

I am new in Swift.
I am trying to make a budget application. This app have a Calculator like keyboard. My idea is when users enter the money app will automatically add a decimal place for users.
For example, if you type 1230 it will give you 12.30 and type 123 it will display 1.23
I wrote a couple lines of code down below. The problem is it only can add decimal point after first digit it won't go backwards when you give more digits. It only can display as X.XXXXX
I tried solve this problem with String.index(maybe increase index?) and NSNumber/NSString format. But I don't know this is the right direction or not.
let number = sender.currentTitle!
let i: String = displayPayment.text!
if (displayPayment.text?.contains("."))!{
displayPayment.text = i == "0" ? number : displayPayment.text! + number
}
else {
displayPayment.text = i == "0" ? number : displayPayment.text! + "." + number
}
Indexing Strings in Swift is not as "straightforward" as many would like, simply due to how Strings are represented internally. If you just want to add a . at before the second to last position of the user input you could do it like this:
let amount = "1230"
var result = amount
if amount.characters.count >= 2 {
let index = amount.index(amount.endIndex, offsetBy: -2)
result = amount[amount.startIndex..<index] + "." + amount[index..<amount.endIndex]
} else {
result = "0.0\(amount)"
}
So for the input of 1230 result will be 12.30. Now You might want to adjust this depending on your specific needs. For example, if the user inputs 30 this code would result in .30 (this might or might not be what you want).