Search code examples
arraysswiftstringinteger

How to get letters and ints from a single string, considering that 2 (or more) ints can be together and should be treated as a single int?


How do I get letters and integers from a single string, considering that 2 (or more) integers can be together and should be treated as a single integer?

So let's take an string "a2bc31d" as an input, I want the output to be an array either ["a","2","bc","31","d"] or ["a","2","b","c","31","d"]

So far I have tried this:

let input = "a2bc31d"
let intArr = input.components(separatedBy: CharacterSet.decimalDigits.inverted) // produces ["", "2", "", "31", ""]
var stringArr = input.components(separatedBy: CharacterSet.letters.inverted).filter({ $0 != "" }) // produces ["a", "bc", "d"]
var newArr = intArr

// Then merge both arrays
for (index,string) in intArr.enumerated() {
     if string == "" {
          newArr[index] = stringArr.first ?? ""
          stringArr.removeFirst()
     }
}

print(newArr) //prints ["a", "2", "bc", "31", "d"]

The problem is that a minimum change to the input to something like "ab2bc31d" then it crashes because the arrays are not created the same.

The stringArr is fine, it is now ["ab","bc","d"], but the intArr is now ["","","2","","31",""] instead of ["","2","","31",""]. So sometimes is creating 1 string for a block of letters, and sometimes is creation 1 string for each letter, which makes my logic to merge both arrays to crash.

  1. Is there a better way to get one of the expected outputs? or
  2. How to fix the issue that components(separatedBy: is giving different outputs for the same kind of strings?

Solution

  • Using a regular expression, this can be done in a one-liner.

    let input = "a2bc31d"
    let matches = input.matches(of: /\D+|\d+/).map(\.output)
    print(matches)
    

    The regex /\D+|\d+/ matches either a sequence of non-digits, or a sequence of digits. matches(of:) finds all such sequences.

    The result matches is a [Substring]. Feel free to convert that to a [String] if you wish:

    let strings = matches.map(String.init)