38

I want to count the number of letters, digits and special characters in the following string:

let phrase = "The final score was 32-31!"

I tried:

for tempChar in phrase {
    if (tempChar >= "a" && tempChar <= "z") {
       letterCounter++
    }
// etc.

but I'm getting errors. I tried all sorts of other variations on this - still getting error - such as:

could not find an overload for '<=' that accepts the supplied arguments

Cœur
  • 37,241
  • 25
  • 195
  • 267
sirab333
  • 3,662
  • 8
  • 41
  • 54

5 Answers5

57

For Swift 5 see rustylepord's answer.

Update for Swift 3:

let letters = CharacterSet.letters
let digits = CharacterSet.decimalDigits

var letterCount = 0
var digitCount = 0

for uni in phrase.unicodeScalars {
    if letters.contains(uni) {
        letterCount += 1
    } else if digits.contains(uni) {
        digitCount += 1
    }
}

(Previous answer for older Swift versions)

A possible Swift solution:

var letterCounter = 0
var digitCount = 0
let phrase = "The final score was 32-31!"
for tempChar in phrase.unicodeScalars {
    if tempChar.isAlpha() {
        letterCounter++
    } else if tempChar.isDigit() {
        digitCount++
    }
}

Update: The above solution works only with characters in the ASCII character set, i.e. it does not recognize Ä, é or ø as letters. The following alternative solution uses NSCharacterSet from the Foundation framework, which can test characters based on their Unicode character classes:

let letters = NSCharacterSet.letterCharacterSet()
let digits = NSCharacterSet.decimalDigitCharacterSet()

var letterCount = 0
var digitCount = 0

for uni in phrase.unicodeScalars {
    if letters.longCharacterIsMember(uni.value) {
        letterCount++
    } else if digits.longCharacterIsMember(uni.value) {
        digitCount++
    }
}

Update 2: As of Xcode 6 beta 4, the first solution does not work anymore, because the isAlpha() and related (ASCII-only) methods have been removed from Swift. The second solution still works.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • yep, that's perfect - how the hell did you find out about "isAlpha" and "isDigit"????? :-) I didn't even know those existed. (Seriously - did you look them up or did you already run into them in prior travels?) – sirab333 Jul 01 '14 at 05:13
  • 1
    @sirab333: `isalpha()` and friends are well known in C, see e.g. https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man3/ctype.3.html#//apple_ref/doc/man/3/ctype. So I had "only" to figure out how to use them from Swift. - But note that this works only for ASCII characters. It does not recognize letters from "foreign languages" such as "ä", "è" or "ø". – Martin R Jul 01 '14 at 05:27
  • and it recognizes `+` and `-` as digit – Christian Dietrich Jul 01 '14 at 05:55
  • Interesting. I never had any C, C#, C++ or any other C - I jumped right into Objective-C and never looked back. All in all then, would you say this is a "legitimate" solution - or is there a more "pure" Swift-only way to do this, without any C involved? – sirab333 Jul 01 '14 at 06:26
  • @ChristianDietrich: Are you sure? My test does not confirm this. – Martin R Jul 01 '14 at 07:14
  • @sirab333: isAlpha() *is* a Swift function (which has the same name as the corresponding C macro). - I have not found anything else in native Swift. But NSCharacterSet from the Foundation frameworks is probably the better tool. I have updated the answer with an example. – Martin R Jul 01 '14 at 08:14
  • @MartinR no i confused that with String("+").toInt() which actually does that – Christian Dietrich Jul 02 '14 at 16:30
  • isAlpha and isDigit are not methods of UnicodeScalar – Gerry Sep 19 '14 at 15:51
  • @Gerry: Yes, that's what I added as "Update 2:" to my answer. – Martin R Sep 19 '14 at 15:53
  • @Warpzit: Yes, quite a lot changed with Swift 3. I have updated the answer, thanks for the feedback. – Martin R Oct 31 '16 at 19:14
5

Use the values of unicodeScalars

let phrase = "The final score was 32-31!"
var letterCounter = 0, digitCounter = 0
for scalar in phrase.unicodeScalars {
    let value = scalar.value
    if (value >= 65 && value <= 90) || (value >= 97 && value <= 122) {++letterCounter}
    if (value >= 48 && value <= 57) {++digitCounter}
}
println(letterCounter)
println(digitCounter)
Matthias Bauch
  • 89,811
  • 20
  • 225
  • 247
Yao
  • 59
  • 1
  • 1
4

For Swift 5 you can do the following for simple strings, but be vigilant about handling characters like "1️⃣" , "④" these would be treated as numbers as well.

let phrase = "The final score was 32-31!"

var numberOfDigits = 0;
var numberOfLetters = 0;
var numberOfSymbols = 0;

phrase.forEach {

    if ($0.isNumber) {
        numberOfDigits += 1;
    }
    else if ($0.isLetter)  {
        numberOfLetters += 1
    }
    else if ($0.isSymbol || $0.isPunctuation || $0.isCurrencySymbol || $0.isMathSymbol) {
        numberOfSymbols += 1;
    }
}

print(#"\#(numberOfDigits)  || \#(numberOfLetters) || \#(numberOfSymbols)"#);
rustylepord
  • 5,681
  • 6
  • 36
  • 49
1

In case you only need one information (letter or number or sign) you can do it in one line:

let phrase = "The final score was 32-31!"

let count = phrase.filter{ $0.isLetter }.count
print(count) // "16\n"

But doing phrase.filter several times is inefficient because it loops through the whole string.

Alexander Langer
  • 320
  • 2
  • 11
0

I've created a short extension for letter and digits count for a String

extension String {
  var letterCount : Int {
    return self.unicodeScalars.filter({ CharacterSet.letters.contains($0) }).count
  }

  var digitCount : Int {
   return self.unicodeScalars.filter({ CharacterSet.decimalDigits.contains($0) }).count
  }
}

or a function to get a count for any CharacterSet you put in

extension String {    
  func characterCount(for set: CharacterSet) -> Int {
    return self.unicodeScalars.filter({ set.contains($0) }).count
  }
}

usage:

let phrase = "the final score is 23-13!"
let letterCount = phrase.characterCount(for: .letters)
Rick Pasveer
  • 480
  • 7
  • 12