1

I've seen other answers to this question, but I'm just trying to do it differently. Yet whatever I do I can't make my types match.

func ContainsOnlyAlphabets(_ word : String) -> Bool{
    let letters = CharacterSet.letters // Set<Character>

    let trimmed = word.trimmingCharacters(in: .whitespaces)

    let characterViewArray = Array(trimmed.characters) // Array<characterView>
    let characterArray = characterViewArray.map{Character($0)} // Error: Can't create Chars
    let wordCharactersSet = Set(characterArray) // Set<Character>

    let intersection = wordCharactersSet.intersection(letters)

    return intersection.count == characterArray.count

}

I had to do all the Set,Char,String,Array conversions but still couldn't get it right :(.

cannot invoke initializer for type 'Character' with an argument list of type '((String.CharacterView._Element))'

mfaani
  • 33,269
  • 19
  • 164
  • 293
  • 1
    What about `if trimmed.rangeOfCharacter(from: letters.inverted) == nil` ? – Martin R Mar 21 '17 at 20:59
  • 2
    Note that `CharacterSet` and `Set` are different types, so your approach computing `wordCharactersSet.intersection(letters)` cannot work. – Martin R Mar 21 '17 at 21:00
  • 1
    one line solution `return word.trimmingCharacters(in: .whitespaces).rangeOfCharacter(from: CharacterSet.letters.inverted) == nil` – Leo Dabus Mar 21 '17 at 21:13

2 Answers2

4

Your code

let characterViewArray = Array(trimmed.characters)

already creates a Array<Character>, so you could simple skip the next line and create a Set<Character> with

let wordCharactersSet = Set(characterViewArray)

But that does not really help, because Set<Character> and CharacterSet are different types, so that

let intersection = wordCharactersSet.intersection(letters)

does not compile. Possible alternatives are

return trimmed.rangeOfCharacter(from: letters.inverted) == nil

or

return CharacterSet(charactersIn: trimmed).isSubset(of: letters)

If your intention is to allow both letters and whitespace characters then it could look like this:

func containsOnlyLettersAndWhitespace(_ word : String) -> Bool{
    var allowedSet = CharacterSet.letters
    allowedSet.formUnion(CharacterSet.whitespaces)

    return word.rangeOfCharacter(from: allowedSet.inverted) == nil
    // Alternatively:
    return CharacterSet(charactersIn: word).isSubset(of: allowedSet.inverted)
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • So would it be correct to say if you want to work with `CharacterSet`s & `Set` then convert the `Set` to `CharacterSet` using `CharacterSet(charactersIn:_)` – mfaani Mar 21 '17 at 21:42
  • @Honey: `CharacterSet(charactersIn:_)` takes a `String` as parameter, so you can pass the string directly, without creating a `Set`. – Martin R Mar 21 '17 at 21:46
  • yes, sorry my mistake....only problem I have now is I can't do `.count` on a `CharacterSet`. Is there any workaround? My current solution is `let intersection = letters.intersection(CharacterSet(charactersIn:trimmed)) return intersection.count == characterViewArray.count` Only that the `.count` on `intersection` doesn't work because `CharacterSet`s don't have count... – mfaani Mar 21 '17 at 21:54
  • @Honey: That's why I suggested `isSubset(of:)` or `rangeOfCharacter(from:)` – you don't need the array anymore. (`intersection` is a `CharacterSet` and `characterViewArray` an array, you cannot compare them) – Martin R Mar 21 '17 at 21:56
  • @actually Right, seeing David Barry's solution I realized that it's stupid to compare two `set`s by their `count`. Comparing the `set`s themselves is enough. – mfaani Mar 21 '17 at 22:12
1

As MartinR noted, CharacterSet is not equivalent to Set<Character>

The closest I could get to your original solution was to create a CharacterSet from the trimmed string and apply some of your original algorithm to that:

func ContainsOnlyAlphabets(_ word : String) -> Bool{
    let letters = CharacterSet.letters

    let trimmed = word.trimmingCharacters(in: .whitespaces)
    let wordCharacterSet = CharacterSet(charactersIn:trimmed)

    let intersection = wordCharacterSet.intersection(letters)

    return intersection == wordCharacterSet
}

Keeping it strictly in the realm of CharacterSet and operations on that, you could also use:

func ContainsOnlyAlphabets(_ word : String) -> Bool{
    return CharacterSet.letters.isSuperset(of:
        CharacterSet(charactersIn: word.trimmingCharacters(in: .whitespaces))
    )
}

That said, I think I'd still go with Martin's solution:

func ContainsOnlyAlphabets(_ word : String) -> Bool{
    return word
        .trimmingCharacters(in: .whitespaces)
        .rangeOfCharacter(from: CharacterSet.letters.inverted) == nil
}

as being more intuitive.

David Berry
  • 40,941
  • 12
  • 84
  • 95
  • You're solution returns false for `containsOnlyAlphabets("Thanks David")`. Something seems to be wrong for how it handles the white spaces – mfaani Mar 21 '17 at 22:00
  • 1
    @Honey: `word.trimmingCharacters` (as in your question) removes only whitespace from both ends of the string, not *all* whitespace. If you want to allow arbitrary whitespace then the easiest solution would be to add that to the allowed character set. – Martin R Mar 21 '17 at 22:08
  • @MartinR Thank you so much, followed what you said using`letters.insert(" ")` and it no longer returns false – mfaani Mar 21 '17 at 22:11