20

Simple question - hopefully, I am trying to generate a simple array of characters, something in the vein of:

// trying to do something like this (pseudo code):
let letters:[Character] = map(0..<26) { i in 'a' + i }

and have tried the following to no avail

let a = Character("a")
let z = Character("z")
let r:Range<Character> = a..<z
let letters:[Character] = map(a..<z) { i in i }

I realize that Swift uses Unicode, what is the correct way to do something like this?

(Note, this is not a question about interop with legacy Obj-C char, strictly in Swift for testing etc).

HangarRash
  • 7,314
  • 5
  • 5
  • 32
Chris Conover
  • 8,889
  • 5
  • 52
  • 68

12 Answers12

23

It's a little cumbersome to get the initial character code (i.e. 'a' in c / Obj-C) in Swift, but you can do it like this:

let aScalars = "a".unicodeScalars
let aCode = aScalars[aScalars.startIndex].value

let letters: [Character] = (0..<26).map {
    i in Character(UnicodeScalar(aCode + i))
}
Mike S
  • 41,895
  • 11
  • 89
  • 84
  • Thanks, was trying to find that mapping in the docs; couldn't. PS. want to try for tangentially related bonus question? http://stackoverflow.com/questions/26152604/swift-slice-startindex-always-0 ;-) – Chris Conover Oct 02 '14 at 00:22
  • Yeah, it's not well documented; you basically have to dig through the auto-generated header file and see what you can make work ;) – Mike S Oct 02 '14 at 00:24
23

Thanks for useful answers.

I'm using one-liner version.

let xs = (97...122).map({Character(UnicodeScalar($0))})

or

let xs = (0..<26).map({Character(UnicodeScalar("a".unicodeScalars.first!.value + $0))})
Daishi Nakajima
  • 1,932
  • 18
  • 26
  • 1
    I like the one-liner; however I found that I needed to unwrap the output of UnicodeScalar() as in: let xs = (0..<26).map({Character(UnicodeScalar("a".unicodeScalars.first!.value + $0)!)}) – Scott Carter Jun 14 '17 at 16:33
12

Xcode 12.5 • Swift 5.4

extension ClosedRange where Bound == Unicode.Scalar {
    static let asciiPrintable: ClosedRange = " "..."~"
    var range: ClosedRange<UInt32>  { lowerBound.value...upperBound.value }
    var scalars: [Unicode.Scalar]   { range.compactMap(Unicode.Scalar.init) }
    var characters: [Character]     { scalars.map(Character.init) }
    var string: String              { String(scalars) }
}

extension String {
    init<S: Sequence>(_ sequence: S) where S.Element == Unicode.Scalar {
        self.init(UnicodeScalarView(sequence))
    }
}

let characters = ("a"..."z").characters  // "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
let string = ("a"..."z").string          // "abcdefghijklmnopqrstuvwxyz"

let range = ClosedRange.asciiPrintable         // {lowerBound " ", upperBound "~"}   32...126
let characters = range.characters  // [" ", "!", """, "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~"]
let string = range.string          // " !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
10

If you just want an array of a known set:

let str = "abcdefghijklmnopqrstuvwxyz"
let characterArray = Array(str)
println(characterArray)

//[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]
Steve Rosenberg
  • 19,348
  • 7
  • 46
  • 53
  • Right, I realize I could type it out, but was wondering how you would map characters as you would in C / Obj-C etc. Thanks – Chris Conover Oct 01 '14 at 23:59
3

With Swift 5, you can use the following Playground sample code in order to get an array of characters from a range of Unicode scalars:

// 1.
let unicodeScalarRange: ClosedRange<Unicode.Scalar> = "A" ... "Z"
// 2.
let unicodeScalarValueRange: ClosedRange<UInt32> = unicodeScalarRange.lowerBound.value ... unicodeScalarRange.upperBound.value
// 3.
let unicodeScalarArray: [Unicode.Scalar] = unicodeScalarValueRange.compactMap(Unicode.Scalar.init)
// 4.
let characterArray: [Character] = unicodeScalarArray.map(Character.init)

print(characterArray)
/*
 prints: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
 */
  1. Create a range of Unicode scalars that match the code points for uppercased latin alphabet Unicode block.
  2. Because this first range is not strideable (you can't iterate on it), convert it to a range of Unicode scalar numeric representations using Unicode.Scalar's value property.
  3. Iterate over your range of Unicode scalar numeric representations in order to create an array of Unicode scalars.
  4. Iterate over your array of Unicode scalars in order to create an array of characters.

As an alternative, you can use one of the sample codes below if you need to start from a range of Characters or a range of Strings:

let unicodeScalarRange: ClosedRange<Character> = "A" ... "Z"
let unicodeScalarValueRange = unicodeScalarRange.lowerBound.unicodeScalars[unicodeScalarRange.lowerBound.unicodeScalars.startIndex].value ... unicodeScalarRange.upperBound.unicodeScalars[unicodeScalarRange.lowerBound.unicodeScalars.startIndex].value
let unicodeScalarArray = unicodeScalarValueRange.compactMap(Unicode.Scalar.init)
let characterArray = unicodeScalarArray.map(Character.init)

print(characterArray)
/*
 prints: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
 */
let unicodeScalarRange: ClosedRange<String> = "A" ... "Z"
let unicodeScalarValueRange = unicodeScalarRange.lowerBound.unicodeScalars[unicodeScalarRange.lowerBound.unicodeScalars.startIndex].value ... unicodeScalarRange.upperBound.unicodeScalars[unicodeScalarRange.lowerBound.unicodeScalars.startIndex].value
let unicodeScalarArray = unicodeScalarValueRange.compactMap(Unicode.Scalar.init)
let characterArray = unicodeScalarArray.map(Character.init)

print(characterArray)
/*
 prints: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
 */
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
2

(11...36).map { String($0 - 1, radix: $0) }

강성용
  • 31
  • 1
  • 1
    Welcome to Stack Overflow, please explain why this answers the question. – JJJ Mar 10 '19 at 00:06
  • 4
    If anyone is interested in why this clever hack works, it's because the radix parameter converts each integer in the range to that base. There's no need to vary the base; this is the same thing, albeit slightly more obvious: (10...35).map {String($0, radix: 36)} So 0..9 are 0..9, 10 is "a", 11 is "b", etc. It only works because 26 letters is less than 36. – Jim Haungs Jun 06 '20 at 02:03
2

Swift 5, patch for @Mike S's

    let aScalars = "a".unicodeScalars
    let aCode = aScalars[aScalars.startIndex].value
    
    let letters: [Character] = (0..<26).map {
        i in
        Character(Unicode.Scalar(aCode + i) ?? aScalars[aScalars.startIndex])
    }
dengST30
  • 3,643
  • 24
  • 25
1

Details

  • Xcode Version 10.3 (10G8), Swift 5

Solution 1

// MAKR: - ClosedRange extensions

extension ClosedRange where Bound == Unicode.Scalar {
    var representationRange: ClosedRange<UInt32> { return lowerBound.value...upperBound.value }
    var scalars: [Bound] { return representationRange.compactMap(Bound.init) }
}

extension ClosedRange where Bound == Character {
    var scalars: [Unicode.Scalar]? {
        guard lowerBound.unicodeScalars.count == 1, upperBound.unicodeScalars.count == 1 else { return nil }
        return (lowerBound.unicodeScalars.first! ... upperBound.unicodeScalars.first!).scalars
    }
    var all: [Bound]? { return scalars?.map(Character.init) }
}

extension ClosedRange where Bound == String  {
    var scalars: [Unicode.Scalar]? {
        guard   lowerBound.unicodeScalars.count == 1, upperBound.unicodeScalars.count == 1,
                let first = lowerBound.first, let last = upperBound.first else { return nil }
        return (first...last).scalars
    }
    var all: [Bound]? { return scalars?.map(String.init) }
}

// MAKR: - Array extensions

extension Array where Element == Character {
    init?(_ range: ClosedRange<Element>) {
        guard let array = range.all else { return nil }
        self = array
    }
}

extension Array where Element == String {
    init?(_ range: ClosedRange<Element>) {
        guard let array = range.all else { return nil }
        self = array
    }
}

extension Array where Element == Unicode.Scalar { init(_ range: ClosedRange<Element>) { self = range.scalars } }

Usage 1

func test(value: Any) { print("-- \(type(of: value)) : \(value)") }

print("====================")
test(value: ("a"..."z").scalars ?? [])
test(value: ("a"..."z").all ?? [])
test(value: ("aa"..."z").all ?? [])
test(value: ("a"..."zz").all ?? [])
print("====================")
test(value: (Character("a")...Character("z")).scalars ?? [])
test(value: (Character("a")...Character("z")).all ?? [])
print("====================")
test(value: (Unicode.Scalar("a")...Unicode.Scalar("z")).scalars)
print("====================")
test(value: [Unicode.Scalar]("a"..."z"))
test(value: [Character]("a"..."z") ?? [])
test(value: [String]("a"..."z") ?? [])
test(value: [String]("aa"..."z") ?? [])
test(value: [String]("a"..."zz") ?? [])

Usage 1 log

====================
-- Array<Scalar> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<String> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<String> : []
-- Array<String> : []
====================
-- Array<Scalar> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<Character> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
====================
-- Array<Scalar> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
====================
-- Array<Scalar> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<Character> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<String> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<String> : []
-- Array<String> : []

Solution 2

extension Unicode.Scalar: Strideable {
    public typealias Stride = Int
    public func distance(to other: Unicode.Scalar) -> Stride { return abs(Int(value) - Int(other.value)) }
    public func advanced(by n: Stride) -> Unicode.Scalar { return Unicode.Scalar(value + UInt32(n)) ?? self }
}

extension Character: Strideable {
    public typealias Stride = Int
    public func distance(to other: Character) -> Stride {
        guard unicodeScalars.count == 1, other.unicodeScalars.count == 1 else { return 0 }
        return unicodeScalars.first!.distance(to: other.unicodeScalars.first!)
    }
    public func advanced(by n: Stride) -> Character {
        guard unicodeScalars.count == 1 else { return self }
        return Character(unicodeScalars.first!.advanced(by: n))
    }
}

extension Array where Element == String {
    init?(_ range: ClosedRange<Element>) {
        guard   range.lowerBound.unicodeScalars.count == 1, range.upperBound.unicodeScalars.count == 1,
                let first = range.lowerBound.unicodeScalars.first, let last = range.upperBound.unicodeScalars.first else { return nil }
        self = [Unicode.Scalar](first...last).map(String.init)
    }
}

Usage 2

func test(value: Any) { print("-- \(type(of: value)) : \(value)") }

test(value: [Unicode.Scalar]("a"..."z"))
test(value: [Character]("a"..."z"))
test(value: [String]("a"..."z"))
test(value: Array("a"..."z"))
test(value: Array(Character("a")...Character("z")))
test(value: Array(Unicode.Scalar("a")...Unicode.Scalar("z")))

Usage 2 log

-- Array<Scalar> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<Character> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Optional<Array<String>> : Optional(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"])
-- Optional<Array<String>> : Optional(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"])
-- Array<Character> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<Scalar> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
Vasily Bodnarchuk
  • 24,482
  • 9
  • 132
  • 127
0

Solving for:

// trying to do something like this (pseudo code):
// let letters:[Character] = map(0..<26) { i in 'a' + i }

with Swift 4.2 / 5.0:

extension Character: Strideable {
    public typealias Stride = Int

    // https://stackoverflow.com/questions/39982335/creating-a-countableclosedrangecharacter
    public func distance(to other: Character) -> Character.Stride {
        let stride = Int(String(self).unicodeScalars.first!.value) - Int(String(other).unicodeScalars.first!.value)
        return abs(stride)
    }

    public func advanced(by n: Character.Stride) -> Character {
        return Character(UnicodeScalar(String(self).unicodeScalars.first!.value + UInt32(n))!)
    }
}

extension ClosedRange where Element == Character {

    var characters: [Character] { return Array(self) }
}

yields:

let letters: [Character] = ("A"..."Z").characters
print(letters)
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
Mark
  • 183
  • 1
  • 7
0

This is a small refinement to @Mike S's answer. You can simply get the ASCII value of the first character in the English alphabet and iterate as follows.

let aScalars = ("a" as Character).asciiValue!

let letters: [Character] = (0..<26).map {
   i in Character(UnicodeScalar(aScalars + i))
}

Complexity

  • Runtime: O(n)

  • Memory: O(n)

  • Where n is the total letters (Character) in the

    English alphabet.

Avi Levin
  • 1,868
  • 23
  • 32
0

One-liner: (combining compactMap and map)

let alphabet = (UnicodeScalar("a").value...UnicodeScalar("z").value).compactMap { UnicodeScalar($0) }.map { String($0) }
print(alphabet) // abcdefg....
Sentry.co
  • 5,355
  • 43
  • 38
-4

You can simply use let alphabets: [UnicodeScalar] = Array("A"..."Z")

coolly
  • 343
  • 1
  • 6
  • 16