Swift: generate an array of (Swift) characters

2020-08-11 11:11发布

问题:

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).

回答1:

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))
}


回答2:

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))})


回答3:

Xcode 10 • Swift 4.2

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


回答4:

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]


回答5:

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"]
 */


回答6:

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"]


回答7:

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"]


回答8:

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



回答9:

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