user3043098 user3043098 - 1 month ago 10
Swift Question

Check and get only Int's out of an Array of Strings

Im currently working on a small app for chemical calculations. Im trying to program a Mol Calculator in which you just have to enter the sum formula and the amount of it. Without any numbers it works perfectly, for example if i enter

NaCl
with an amount of
100
grams (you enter the gram in a different text field) you get about 1.73 mol, which is the correct solution.

What i want to do now is that i can add numbers, for example
Na2Cl4
. In my method, the string is splitted up properly, if you print it you get
["Na", "2", "Cl", "4"]
but i don't know how i can get the Int's out and remember to which element they were assigned to (2 to Na and 4 to Cl, because i need to multiply the mol/g with the 2).

I tried the following:
The number will always be on a odd index (Na at 0, 2 at 1, Cl at 2, 4 at 3), so i tried to put every element at a odd index into a new array :

var i = 0
var testarray = [String]()

for content in splitted {
if i % 2 == 0 {

}else {
testarray.append(content)
}
print(content)
i += 1
}


splitted is the [String] array which contains the splitted sum formula. The result i get if i print out content is 2 and 4 which is fine but if i enter for example Na10 i get 1 and 0 which is not good, also it does not know to which element it was assigned to.

Does anybody know how to solve this problem that i can get the Int values out of the string array and remember to which element they were assigned to ?

Thanks in advance!

EDIT:

Heres the code how i split the string:

let splitted = summenformel.characters.splitBefore(separator: { $0.isUpperCase }).map{String($0)}


and the extensions:

extension Sequence {
func splitBefore(
separator isSeparator: (Iterator.Element) throws -> Bool
) rethrows -> [AnySequence<Iterator.Element>] {
var result: [AnySequence<Iterator.Element>] = []
var subSequence: [Iterator.Element] = []

var iterator = self.makeIterator()
while let element = iterator.next() {
if try isSeparator(element) {
if !subSequence.isEmpty {
result.append(AnySequence(subSequence))
}
subSequence = [element]
}
else {
subSequence.append(element)
}
}
result.append(AnySequence(subSequence))
return result
}
}

extension Character {
var isUpperCase: Bool { return String(self) == String(self).uppercased() }
}

Answer

Alternative approach with regular expression

The regex searches for:

a capital letter followed by optional lowercase letters followed by optional numbers.

let string = "Na10Cl4"

let pattern = "([A-Z]([a-z]+)?)(\\d+)?"
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let matches = regex.matches(in: string, options: [], range: NSRange(location: 0, length: string.characters.count))
for match in matches {
  let elementRange = match.rangeAt(1)
  let element = (string as NSString).substring(with: elementRange)
  print("element", element)
  let quantityRange = match.rangeAt(3)
  let quantity : Int
  if quantityRange.location != NSNotFound {
    quantity = Int((string as NSString).substring(with: quantityRange))!
  } else {
    quantity = 1
  }
  print("quantity", quantity)
}