What's the issue in sum += i
, as the error said, i
is a Character
, and sum
a Int
.
Can you make addition between bananas & apples? It's the same logic here.
So you might want to have its Int
equivalent with Int(i)
It's returning an optional value, because there is no guarantee that i
is valid. You check isNumber
before hand, but the line itself doesn't know that. So you can soft unwrap, or if you are sure force unwrap:
sum += Int(String(i))! //Char -> String -> Int
Because there is a String.init(someChar)
, and Int.init(someString)
, but not Int.init(someChar)
, that's why there is the double init()
.
BUT, keeping your logic, you are iterating characters per characters...
So, in the end you have:
1 + 2 + 3 + 0 + 6 + 7
(ie 19
), not 1 + 2 + 30 + 67
(ie 100
) as expected.
So if you want to iterate, you need to "group" the consecutive numbers...
With basic for loops, your can do this (it's a possible solution, might no be the better one, but a working one)
let numsInStr = "1abc2x30yz67"
var lastWasNumber = false
var intStrings: [String] = []
for aCharacter in numsInStr {
if aCharacter.isNumber {
if !lastWasNumber {
intStrings.append(String(aCharacter))
} else {
intStrings[intStrings.count - 1] = intStrings[intStrings.count - 1] + String(aCharacter)
}
lastWasNumber = true
} else {
lastWasNumber = false
}
print("After processing: \(aCharacter) - got: \(intStrings)")
}
print(intStrings)
var sum = 0
for anIntString in intStrings {
sum += Int(anIntString)!
}
print("Sum: \(sum)")
At your level, never hesitate to add print()
(but never just the variable, always add an additional text which will be context to know from where it's called).
The output being:
$>After processing: 1 - got: ["1"]
$>After processing: a - got: ["1"]
$>After processing: b - got: ["1"]
$>After processing: c - got: ["1"]
$>After processing: 2 - got: ["1", "2"]
$>After processing: x - got: ["1", "2"]
$>After processing: 3 - got: ["1", "2", "3"]
$>After processing: 0 - got: ["1", "2", "30"]
$>After processing: y - got: ["1", "2", "30"]
$>After processing: z - got: ["1", "2", "30"]
$>After processing: 6 - got: ["1", "2", "30", "6"]
$>After processing: 7 - got: ["1", "2", "30", "67"]
$>["1", "2", "30", "67"]
$>100
We rely on Int(someString)
(and force unwrapping), but sum += Int(anIntString) ?? 0
should be safer. Since for too big values, if you have "a1234567890123456789123456789123456789" for instance, I'm not sure that Int
will be big enough to handle that value. That some edges cases that you need to be aware of.
With high level methods, you can use componentsSeparated(by:)
to get an array of only string & only letters. Then, you can filter()
(if needed), or compactMap()
and transform to Int
if possible, then sum (with reduce(into:_:)
.
As suggested, another solution without keeping a list of String
could be:
var sum = 0
var lastWasNumber = false
var currentIntString = ""
for aCharacter in numsInStr {
if aCharacter.isNumber {
if !lastWasNumber {
sum += Int(currentIntString) ?? 0
currentIntString = "" // Reset, but in fact since we override with the next line, it's not necessary to write it
currentIntString = String(aCharacter)
} else {
currentIntString += String(aCharacter)
}
lastWasNumber = true
} else {
lastWasNumber = false
}
print("After processing: \(aCharacter) - got: \(currentIntString) - current sum: \(sum)")
}
sum += Int(currentIntString) ?? 0
print("Sum: \(sum)")
Here, we keep currentInString
as a "buffer".
This could be simplified too by removing lastWasNumber
and checking instead currentIntString
:
var sum = 0
var currentIntString = ""
for aCharacter in numsInStr {
if aCharacter.isNumber {
if currentIntString.isEmpty {
currentIntString = String(aCharacter)
} else {
currentIntString += String(aCharacter)
}
} else {
sum += Int(currentIntString) ?? 0
currentIntString = ""
}
print("After processing: \(aCharacter) - got: \(currentIntString) - current sum: \(sum)")
}
sum += Int(currentIntString) ?? 0
print("Sum: \(sum)")