Initialize a String from a range of Characters in Swift

Issue

In our code, we found a bug from not writing the alphabet correctly. Instead of "0123456789abcdefghijklmnopqrstuvwxyz", we had "0123456789abcdefghijklmnoqprstuvwxyz". So we are wondering if it’s possible to avoid similar typo by declaring Strings made from ranges of characters?

Using Swift 4.1+, we tried:

attempt 1

let 📚1: String = "0"..."9" + "a"..."z"

Adjacent operators are in non-associative precedence group ‘RangeFormationPrecedence’

attempt 2

let 📚2: String = ("0"..."9") + ("a"..."z")

Binary operator ‘+’ cannot be applied to two ‘ClosedRange<String>‘ operands

attempt 3

let 📚3: String = String("0"..."9") + String("a"..."z")

Cannot invoke initializer for type ‘String’ with an argument list of type ‘(ClosedRange<String>)

attempt 4

let 📚4: String = (Character("0")...Character("9")) + (Character("a")...Character("z"))

Binary operator ‘+’ cannot be applied to two ‘ClosedRange<Character>‘ operands

attempt 5

let 📚5: String = String(Character("0")...Character("9")) + String(Character("a")...Character("z"))

Cannot invoke initializer for type ‘String’ with an argument list of type ‘(ClosedRange<Character>)

Solution

"a"..."z" is a ClosedRange, but not a CountableClosedRange.
It represents all strings s for which "a" <= s <= "z"
according to the Unicode standard. That are not just the 26 lowercase letters from the english alphabet but many more, such as “ä”, “è”, “ô”.
(Compare also
ClosedInterval<String> to [String] in Swift.)

In particular, "a"..."z" is not a Sequence, and that is why
String("a"..."z") does not work.

What you can do is to create ranges of Unicode scalar values
which are (UInt32) numbers (using the UInt32(_ v: Unicode.Scalar) initializer):

let letters = UInt32("a") ... UInt32("z")
let digits = UInt32("0") ... UInt32("9")

and then create a string with all Unicode scalar values in those
(countable!) ranges:

let string = String(String.UnicodeScalarView(letters.compactMap(UnicodeScalar.init)))
    + String(String.UnicodeScalarView(digits.compactMap(UnicodeScalar.init)))

print(string) // abcdefghijklmnopqrstuvwxyz0123456789

(For Swift before 4.1, replace compactMap by flatMap.)

This works also for non-ASCII characters. Example:

let greekLetters = UInt32("α") ... UInt32("ω")
let greekAlphabet = String(String.UnicodeScalarView(greekLetters.compactMap(UnicodeScalar.init)))
print(greekAlphabet) // αβγδεζηθικλμνξοπρςστυφχψω

Answered By – Martin R

Answer Checked By – Terry (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.