Trouble using width for String in for-in cycle - swift

I've write a simple code:
extension String {
func trailingSpaces (width: Int) -> String {
var s = "\(self)"
for i in count(s)..<width {
s = s + " "
}
return s
}
func leadingSpaces (width: Int) -> String {
var s = "\(self)"
for i in count(s)..<width {
s = " " + s
}
return s
}
}
class ViewController: UIViewController {
var users = ["Marco", "Gianni", "Antonio", "Giulio", "Franco"]
var ages = [29, 45, 17, 33, 37]
override func viewDidLoad() {
super.viewDidLoad()
var merged = [String: Int] ()
var totalAge = 0.0
for var i = 0; i < ages.count; i++ {
merged[users[i]] = ages[i]
}
for user in sorted(merged.keys) {
let age = merged[user]
totalAge += Double(age!)
let paddedUser = user.trailingSpaces(10)
let paddedAge = "\(age)".leadingSpaces(3)
println("\(paddedUser) \(age!)")
}
println("\n\(merged.count) users")
println("average age: \(totalAge / Double(merged.count))")
}
}
but I can't make it work the leadingSpaces function and I can't understand the reason, it's quite identical to the other extension func that works.
It give the error
fatal error: Can't form Range with end < start
on runtime

in case you run into this kind of problem, always do a println() of the variable you are using
println("\(age)") right before let paddedAge = "\(age!)".leadingSpaces(3)
reveals the problem
age is an optional, meaning that you are trying to do the padding on a String which has this value "Optional(17)"
Thus, your count(s) is higher than 3, and you have an invalid range

Your variable age is not an Int - it's an optional - Int?. You know this already as you are unwrapping it in the lines totalAge += Double(age!) and println("\(paddedUser) \(age!)") - but you are not unwrapping it in the failing line let paddedAge = "\(age)".leadingSpaces(3). The string being passed to leadingSpaces is not "17", it's "Optional(17)", which is why your padding function is failing, as the length is greater than the requested width.
Having said that, as the commentator #milo256 points out, Swift can only iterate upwards, and so unless you put a check on width >= .count in your padding functions they will crash at some point.

Related

Swift function not working inside another function

I'm new on this site but I've been struggling for several days about this issue I found. I wrote this code in order to solve a challenge of the site Codewars; the challenge consists in calculate the mean and the variance from some data about some fictional rainfalls (I attach the complete page on the bottom). In order to end this challenge I created a function to convert the data from this useless string into an array of Doubles. The weird thing is that the function if called outside the main one works properly but inside returns an empty array. I have no idea why is happening this. Thank you very much for every effort you'll put trying to explain me this.
This is the first part of the Codewars page that explain the callenge
This is the second one
//
// main.swift
// Prova
//
// Created by Lorenzo Santini on 13/06/18.
// Copyright © 2018 Lorenzo Santini. All rights reserved.
//
import Foundation
func mean(_ d: String,_ town: String) -> Double {
let arrayOfValues = obtainArrayOfMeasures(d, town)
var sum: Double = 0
for element in arrayOfValues {
sum += element
}
return sum / Double(arrayOfValues.count)
}
func variance(_ d: String,_ town: String) -> Double {
let meanValue: Double = mean(d, town)
//Here is the problem: when this function is called instead of returning the array containg all the measures for the selected city it returns an empty array
var arrayOfValues = obtainArrayOfMeasures(d, town)
var sum: Double = 0
for element in arrayOfValues {
sum += pow((element - meanValue), 2)
}
return sum / Double(arrayOfValues.count)
}
func isInt(_ char: Character) -> Bool {
switch char {
case "1","2","3","4","5","6","7","8","9":
return true
default:
return false
}
}
func obtainArrayOfMeasures(_ d: String,_ town: String) -> [Double]{
//The first array stores the Data string divided for city
var arrayOfString: [String] = []
//The second array stores the measures of rainfall of the town passed as argument for the function
var arrayOfMeasures: [Double] = []
//Split the d variable containg the data string in separated strings for each town and add it to the arrayOfString array
repeat {
let finalIndex = (data.index(of:"\n")) ?? data.endIndex
arrayOfString.append(String(data[data.startIndex..<finalIndex]))
let finalIndexToRemove = (data.endIndex == finalIndex) ? finalIndex : data.index(finalIndex, offsetBy: 1)
data.removeSubrange(data.startIndex..<finalIndexToRemove)
} while data.count != 0
//Find the string of the town passed as argument
var stringContainingTown: String? = nil
for string in arrayOfString {
if string.contains(town) {stringContainingTown = string; print("true")}
}
if stringContainingTown != nil {
var stringNumber = ""
var index = 0
//Add to arrayOfMeasures the measures of the selected town
for char in stringContainingTown! {
index += 1
if isInt(char) || char == "." {
stringNumber += String(char)
print(stringNumber)
}
if char == "," || index == stringContainingTown!.count {
arrayOfMeasures.append((stringNumber as NSString).doubleValue)
stringNumber = ""
}
}
}
return arrayOfMeasures
}
var data = "Rome:Jan 81.2,Feb 63.2,Mar 70.3,Apr 55.7,May 53.0,Jun 36.4,Jul 17.5,Aug 27.5,Sep 60.9,Oct 117.7,Nov 111.0,Dec 97.9" + "\n" +
"London:Jan 48.0,Feb 38.9,Mar 39.9,Apr 42.2,May 47.3,Jun 52.1,Jul 59.5,Aug 57.2,Sep 55.4,Oct 62.0,Nov 59.0,Dec 52.9" + "\n" +
"Paris:Jan 182.3,Feb 120.6,Mar 158.1,Apr 204.9,May 323.1,Jun 300.5,Jul 236.8,Aug 192.9,Sep 66.3,Oct 63.3,Nov 83.2,Dec 154.7" + "\n" +
"NY:Jan 108.7,Feb 101.8,Mar 131.9,Apr 93.5,May 98.8,Jun 93.6,Jul 102.2,Aug 131.8,Sep 92.0,Oct 82.3,Nov 107.8,Dec 94.2" + "\n" +
"Vancouver:Jan 145.7,Feb 121.4,Mar 102.3,Apr 69.2,May 55.8,Jun 47.1,Jul 31.3,Aug 37.0,Sep 59.6,Oct 116.3,Nov 154.6,Dec 171.5" + "\n" +
"Sydney:Jan 103.4,Feb 111.0,Mar 131.3,Apr 129.7,May 123.0,Jun 129.2,Jul 102.8,Aug 80.3,Sep 69.3,Oct 82.6,Nov 81.4,Dec 78.2" + "\n" +
"Bangkok:Jan 10.6,Feb 28.2,Mar 30.7,Apr 71.8,May 189.4,Jun 151.7,Jul 158.2,Aug 187.0,Sep 319.9,Oct 230.8,Nov 57.3,Dec 9.4" + "\n" +
"Tokyo:Jan 49.9,Feb 71.5,Mar 106.4,Apr 129.2,May 144.0,Jun 176.0,Jul 135.6,Aug 148.5,Sep 216.4,Oct 194.1,Nov 95.6,Dec 54.4" + "\n" +
"Beijing:Jan 3.9,Feb 4.7,Mar 8.2,Apr 18.4,May 33.0,Jun 78.1,Jul 224.3,Aug 170.0,Sep 58.4,Oct 18.0,Nov 9.3,Dec 2.7" + "\n" +
"Lima:Jan 1.2,Feb 0.9,Mar 0.7,Apr 0.4,May 0.6,Jun 1.8,Jul 4.4,Aug 3.1,Sep 3.3,Oct 1.7,Nov 0.5,Dec 0.7"
var prova = variance(data, "London")
The problem is that func obtainArrayOfMeasures modifies the global data
variable. When called the second time, data is an empty string.
An indicator for this problem is also that making the global data variable constant
let data = "Rome:..."
causes a compiler error at
data.removeSubrange(data.startIndex..<finalIndexToRemove)
// Cannot use mutating member on immutable value: 'data' is a 'let' constant
An immediate fix would be to operate on a local mutable copy:
func obtainArrayOfMeasures(_ d: String,_ town: String) -> [Double]{
var data = d
// ...
}
Note however that the function can be simplified to
func obtainArrayOfMeasures(_ d: String,_ town: String) -> [Double] {
let lines = d.components(separatedBy: .newlines)
guard let line = lines.first(where: { $0.hasPrefix(town)}) else {
return [] // No matching line found.
}
let entries = line.components(separatedBy: ",")
let numbers = entries.compactMap { Double($0.filter {".0123456789".contains($0) })}
return numbers
}
without mutating any values. You might also consider to return nil
or abort with fatalError() if no matching entry is found.

Convert String.CharacterView.Index to int [duplicate]

I want to convert the index of a letter contained within a string to an integer value. Attempted to read the header files but I cannot find the type for Index, although it appears to conform to protocol ForwardIndexType with methods (e.g. distanceTo).
var letters = "abcdefg"
let index = letters.characters.indexOf("c")!
// ERROR: Cannot invoke initializer for type 'Int' with an argument list of type '(String.CharacterView.Index)'
let intValue = Int(index) // I want the integer value of the index (e.g. 2)
Any help is appreciated.
edit/update:
Xcode 11 • Swift 5.1 or later
extension StringProtocol {
func distance(of element: Element) -> Int? { firstIndex(of: element)?.distance(in: self) }
func distance<S: StringProtocol>(of string: S) -> Int? { range(of: string)?.lowerBound.distance(in: self) }
}
extension Collection {
func distance(to index: Index) -> Int { distance(from: startIndex, to: index) }
}
extension String.Index {
func distance<S: StringProtocol>(in string: S) -> Int { string.distance(to: self) }
}
Playground testing
let letters = "abcdefg"
let char: Character = "c"
if let distance = letters.distance(of: char) {
print("character \(char) was found at position #\(distance)") // "character c was found at position #2\n"
} else {
print("character \(char) was not found")
}
let string = "cde"
if let distance = letters.distance(of: string) {
print("string \(string) was found at position #\(distance)") // "string cde was found at position #2\n"
} else {
print("string \(string) was not found")
}
Works for Xcode 13 and Swift 5
let myString = "Hello World"
if let i = myString.firstIndex(of: "o") {
let index: Int = myString.distance(from: myString.startIndex, to: i)
print(index) // Prints 4
}
The function func distance(from start: String.Index, to end: String.Index) -> String.IndexDistance returns an IndexDistance which is just a typealias for Int
Swift 4
var str = "abcdefg"
let index = str.index(of: "c")?.encodedOffset // Result: 2
Note: If String contains same multiple characters, it will just get the nearest one from left
var str = "abcdefgc"
let index = str.index(of: "c")?.encodedOffset // Result: 2
encodedOffset has deprecated from Swift 4.2.
Deprecation message:
encodedOffset has been deprecated as most common usage is incorrect. Use utf16Offset(in:) to achieve the same behavior.
So we can use utf16Offset(in:) like this:
var str = "abcdefgc"
let index = str.index(of: "c")?.utf16Offset(in: str) // Result: 2
When searching for index like this
⛔️ guard let index = (positions.firstIndex { position <= $0 }) else {
it is treated as Array.Index. You have to give compiler a clue you want an integer
✅ guard let index: Int = (positions.firstIndex { position <= $0 }) else {
Swift 5
You can do convert to array of characters and then use advanced(by:) to convert to integer.
let myString = "Hello World"
if let i = Array(myString).firstIndex(of: "o") {
let index: Int = i.advanced(by: 0)
print(index) // Prints 4
}
To perform string operation based on index , you can not do it with traditional index numeric approach. because swift.index is retrieved by the indices function and it is not in the Int type. Even though String is an array of characters, still we can't read element by index.
This is frustrating.
So ,to create new substring of every even character of string , check below code.
let mystr = "abcdefghijklmnopqrstuvwxyz"
let mystrArray = Array(mystr)
let strLength = mystrArray.count
var resultStrArray : [Character] = []
var i = 0
while i < strLength {
if i % 2 == 0 {
resultStrArray.append(mystrArray[i])
}
i += 1
}
let resultString = String(resultStrArray)
print(resultString)
Output : acegikmoqsuwy
Thanks In advance
Here is an extension that will let you access the bounds of a substring as Ints instead of String.Index values:
import Foundation
/// This extension is available at
/// https://gist.github.com/zackdotcomputer/9d83f4d48af7127cd0bea427b4d6d61b
extension StringProtocol {
/// Access the range of the search string as integer indices
/// in the rendered string.
/// - NOTE: This is "unsafe" because it may not return what you expect if
/// your string contains single symbols formed from multiple scalars.
/// - Returns: A `CountableRange<Int>` that will align with the Swift String.Index
/// from the result of the standard function range(of:).
func countableRange<SearchType: StringProtocol>(
of search: SearchType,
options: String.CompareOptions = [],
range: Range<String.Index>? = nil,
locale: Locale? = nil
) -> CountableRange<Int>? {
guard let trueRange = self.range(of: search, options: options, range: range, locale: locale) else {
return nil
}
let intStart = self.distance(from: startIndex, to: trueRange.lowerBound)
let intEnd = self.distance(from: trueRange.lowerBound, to: trueRange.upperBound) + intStart
return Range(uncheckedBounds: (lower: intStart, upper: intEnd))
}
}
Just be aware that this can lead to weirdness, which is why Apple has chosen to make it hard. (Though that's a debatable design decision - hiding a dangerous thing by just making it hard...)
You can read more in the String documentation from Apple, but the tldr is that it stems from the fact that these "indices" are actually implementation-specific. They represent the indices into the string after it has been rendered by the OS, and so can shift from OS-to-OS depending on what version of the Unicode spec is being used. This means that accessing values by index is no longer a constant-time operation, because the UTF spec has to be run over the data to determine the right place in the string. These indices will also not line up with the values generated by NSString, if you bridge to it, or with the indices into the underlying UTF scalars. Caveat developer.
In case you got an "index is out of bounds" error. You may try this approach. Working in Swift 5
extension String{
func countIndex(_ char:Character) -> Int{
var count = 0
var temp = self
for c in self{
if c == char {
//temp.remove(at: temp.index(temp.startIndex,offsetBy:count))
//temp.insert(".", at: temp.index(temp.startIndex,offsetBy: count))
return count
}
count += 1
}
return -1
}
}

Swift 2.1 Binary operator * cannot be applied to two String operands

I have a calculator, but I can´t resolve this code:
#IBAction func calcular(sender: AnyObject) {
resultado.text = String(format: "", Sliderdosis)
let peso = pesoLabel.text
let dosis = dosisLabel.text
let total = (peso * dosis) * 5 / 250
/* in this point, the program write:
Binary operator * cannot be applied to two String operands** */
resultado.text = total
}
Some body help me please?
I´m a beginner, sorry!
The infix binary operator * does not exist for type String. You are attempting to multiply String objects (that are, inherently, not numerical) hence the error.
I'd suggest you make use of Leo Dabus excellent UITextField extension, however, in your case, extending UILabel (assuming pesoLabel and dosisLabel are UILabel instances)
extension UILabel {
var stringValue : String { return text ?? "" }
var integerValue: Int { return Int(stringValue) ?? 0 }
var doubleValue : Double { return Double(stringValue) ?? 0 }
var floatValue : Float { return Float(stringValue) ?? 0 }
}
Add this to the header of your .swift file. Thereafter you can update you button action method according to:
#IBAction func calcular(sender: AnyObject) {
resultado.text = String(format: "", Sliderdosis)
let peso = pesoLabel.doubleValue
let dosis = dosisLabel.doubleValue
let total = (peso * dosis) * 5 / 250
resultado.text = String(total)
}
Finally note that this subject is well-covered here on SO, so there exists existing threads that can help you convert string to numerical values. E.g.
Swift - Converting String to Int
Swift - How to convert String to Double
Also have a look at the Asking section here on SO, it contains lots of valuable information of how to ask, when to ask, and so on.

Create Simple Class with multiple calculating function( )

Trying to run a simple calculation via a function in my class. I simply want to add bill1 + bill2 and print the total amount spent on bills. So (bill1 + bill2 = total). And then print the total amount.
Current error states - "Code after 'return' will never be executed." Now, is my location for my print in the wrong location or did I declare my variables incorrectly? Should I be using vars instead of lets?
What do you recommend for my function in order to calculate and print the result?
class BillsCalculator
{
let nameOfBill1: String = "Medical"
let nameOfBill1: String = "Hulu"
let monthlyBillAmount1: Double = 34.25
let monthlyBillAmount2: Double = 7.99
let calculateTotalsPerMonth: Double = 0.0
//calculateTotalPerMonth ( = monthlyBillAmount_1 + monthlyBillAmount_2 + 3)
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double
{
//totalBillsPerMonth = add(monthlyBillAmount1 + monthlyBillAmount2)
return totalBillsPerMonth(monthlyBillAmount1 + monthlyBillAmount2)
*Error println("You spend \(totalBillsPerMonth)")
}
}
First: "Code after 'return' will never be executed."
Yes it will not, after you call return you exit the function and return to the function that call it, you probably have an warning in XCode warning you about telling you that
Second: "Should I be using vars instead of lets"
If the value changes you MUST use var, if it does not you SHOULD use let.
Some problems I can see in your code:
class BillsCalculator
{
//use _ in the beginning of the name for class variables
//eg. _nameOfBill instead nameOfBill1
//It is not wrong use nameOfBill1 is just not recommended
//if nameOfBill1 change use var
let nameOfBill1: String = "Medical"
//Why is this declare twice
let nameOfBill1: String = "Hulu"
//Those values look like change should be var
var monthlyBillAmount1: Double = 34.25
var monthlyBillAmount2: Double = 7.99
var calculateTotalsPerMonth: Double = 0.0
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double
{
totalBillsPerMonth = add(monthlyBillAmount1 + monthlyBillAmount2)
//print before return
println("You spend \(totalBillsPerMonth)")
return totalBillsPerMonth(monthlyBillAmount1 + monthlyBillAmount2)
}
}
Here you should either print the value of your total bill or return that value. As you just want to print the total bill amount so I would recommend you to just print, not to return anything. You can refer the below code.
class BillsCalculator
{
let nameOfBill1: String = "Medical"
let nameOfBill1: String = "Hulu"
let monthlyBillAmount1: Double = 34.25
let monthlyBillAmount2: Double = 7.99
let calculateTotalsPerMonth: Double = 0.0
//calculateTotalPerMonth ( = monthlyBillAmount_1 + monthlyBillAmount_2 + 3)
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double
{
calculateTotalsPerMonth= add(monthlyBillAmount1 + monthlyBillAmount2)
println("You spend : "+totalBillsPerMonth);
}
}
One tiny error in your code
let nameOfBill1: String = "Medical"
let nameOfBill1: String = "Hulu"
These two variables have the same name, perhaps one should be:
let nameOfBill2: String = "Hulu"
And yes return is always the last line in the function, so any codes after return will never be executed. If you only want to get the total of two bills, you can simply do this:
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double {
//println("You spend \(totalBillsPerMonth)")
return monthlyBillAmount1 + monthlyBillAmount2
}
and call this function with your bill variables, like:
let bill1 = 34.25
let bill2 = 7.99
let totalBill = calculateTotalsPerMonth(bill1, bill2)
println("You spent \(totalBill)")
Swift is a very smart language, and it is type safe. You can remove the type if you want, more like a personal programming style thing.
let bill1: Double = 34.25
let bill1 = 34.25
They both will be type "Double"
As others have said, you need to put your println statement before return since returns ends the execution of the method; thus println will never be run.
However, I would suggest a few changes to your current approach:
// A bill is an object - why not encapsulate it in a struct.
struct Bill {
let name: String
let amount: Double
}
// Using structs is generally preferred, unless you need inheritance and/or
// references to your BillsCalculator objects.
struct BillsCalculator {
let bill1: Bill
let bill2: Bill
// Using a read-only computed property means you don't need to set
// the total to have an initial value of zero.
var totalBilled: Double {
return bill1.amount + bill2.amount
}
}
// Since you're probably going to want to reuse BillsCalculator,
// don't have each bill set already. Instead, use BillsCalculator's
// initialiser and pass in bills.
let bill1 = Bill(name: "Medical", amount: 34.25)
let bill2 = Bill(name: "Hulu", amount: 7.99)
let cal = BillsCalculator(bill1: bill1, bill2: bill2)
print("You've spend \(cal.totalBilled) this month")

(swift) Error: can not invoke '>' with an argument list of type '(UInt32, #lvalue UInt32)'

class ViewController: UIViewController {
#IBOutlet weak var inputField: UITextField!
#IBOutlet weak var output: UITextView!
var guesses : UInt = 0
var number : UInt32 = 0
var gameOver = false
let MAX_GUESSES : UInt = 8
#IBAction func guess(sender: UIButton) {
var possibleGuess : Int? = inputField.text.toInt()
if let guess = possibleGuess {
// possibleGuess exists!
} else {
consoleOut("Please input a valid number!\n")
clearInput()
}
if UInt32(guess) > Int(number) {
consoleOut("\(guess): You guessed too high!\n")
++guesses
} else if UInt32(guess) < number {
consoleOut("\(guess): You guessed too low!\n")
++guesses
} else {
consoleOut("\n\(guess): You win!\n")
consoleOut("Go again? (Y)")
guesses = 0
gameOver = true
}
clearInput()
if (guesses == MAX_GUESSES) {
consoleOut("\nYou lose :(\n")
consoleOut("Go again? (Y)")
guesses = 0
gameOver = true
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
number = generateNewNumber()
consoleOut("Gondolkodom egy számot...\n")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func consoleOut(text : String) {
output.text = output.text + text
}
func generateNewNumber () -> UInt32 {
return arc4random_uniform(100)
}
func clearInput() {
inputField.text = ""
}
}
This is the code that I use and I get the error message at if UInt32(guess) > Int(number) {. I really can't get through this.
(swift) Error: can not invoke '>' with an argument list of type '(UInt32, #lvalue UInt32)'
* This is not exactly your problem, but it may show you a way to get around it :) *
This must be a Swift bug like many others ObjectiveC had.
I'm having the same problem trying to compare an arc4random() number (which is an UInt32 type) with a UInt32() casted string, and I get the same error, which is more outrageous in my case because the two numbers ARE the same type. Which leads me to think that the casting must not be producing the desired result.
I though of creating an auxiliary UIint32 variable and assign it UInt32(theString), butSwift doesn't let you convert a String into UInt32 when defining a variable, so I had to create an auxiliary variable to be converted to Int, and then convert the Int to UInt32 to be able to compare the two numbers:
var theString = "5"
var randomNumber = arc4random() % 10
var UInt32Number = UInt32(theString)
// => ERROR: "Cannot invoke 'init' with an argument of type '#lvalue String!'
// (this is where I realized the comparison line could be suffering from the same problem)
if randomNumber == UInt32(theString) { ... }
// No error here 'cos Swift is supposed to have casted theString into a UInt32
// ...but surprisingly it prompts an ERROR saying it can't compare a UInt32 with a UInt32 (WTF!)
// And here's where I go crazy, because watch what happens in the next lines:
var intValue = theString.toInt()
var UInt32Value = UInt32(intValue!)
if randomNumber == UInt32Value { ... } // => NOW IT WORKS!!
CONCLUSION: Swift is not making the conversion type in the comparison even if it's supposed to. Sometimes it seems to f*** up. Using auxiliary variables with set types can get around the problem.