How to count vowels and consonants - swift

I'm currently using Xcode 10.1 and am trying to count the amount of vowels and consonants in my given sentence. I declare the constant globally
let sentence = "Here is my sentence"
Then I attempt to call a function with a parameter by the name of "sentenceInput" that under a certain case adds the sentence to the function which is mean to count the amount of vowels and consonants and return the Int values. However when the function is called I'm told that there is only 1 consonant and 0 vowels. Being new to programming and Xcode in general I would very much appreciate the help. Thank you. Code for function:
func findVowelsConsonantsPunctuation(sentenceInput: String) -> (Int, Int, Int) {
var vowels = 0; var consonants = 0; var punctuation = 0
for character in sentenceInput.characters{
switch String(character).lowercased
{
case "a", "e", "i", "o", "u":
vowels += 1
case ".","!",":",";","?", " ", "'", "":
punctuation += 1
default:
consonants += 1
}
return (vowels, consonants, punctuation)
}
}

I would suggest reading up on Set.
With that in mind, you could use CharacterSet and create 3 sets.
// Make your vowels
let vowels = CharacterSet(charactersIn: "aeiouy")
let consonants = CharacterSet.letters.subtracting(vowels)
let punctuation = CharacterSet.punctuationCharacters
Then, you'd want to track the counts of vowels, consonants, and punctuation:
// Make your vowels
let vowels = CharacterSet(charactersIn: "aeiouy")
let consonants = CharacterSet.letters.subtracting(vowels)
let punctuation = CharacterSet.punctuationCharacters
Then, loop through through it.
func sentenceComponents(string: String) -> (Int, Int, Int) {
// Make your vowels
let vowels = CharacterSet(charactersIn: "aeiouy")
let consonants = CharacterSet.letters.subtracting(vowels)
let punctuation = CharacterSet.punctuationCharacters
// Set up vars to track our coutns
var vowelCount = 0
var consonantCount = 0
var punctuationCount = 0
string.forEach { char in
// Make a set of one character
let set = CharacterSet(charactersIn: String(char))
// If the character is a member of a set, incremennt the relevant var
if set.isSubset(of: vowels) { vowelCount += 1 }
if set.isSubset(of: consonants) { consonantCount += 1 }
if set.isSubset(of: punctuation) { punctuationCount += 1 }
}
return (vowelCount, consonantCount, punctuationCount)
}
let testString = "The quick brown fox jumped over the lazy dog."
sentenceComponents(string: testString)
Update: Neater & Easier Read (Maybe?)
I can't stand unlabeled tuples, so here's an update with a typealias that tells you what you've got without having to go over the river & through the woods to figure out what's what in the tuple:
typealias SentenceComponents = (vowels: Int, consonants: Int, punctuation: Int)
func components(in string: String) -> SentenceComponents {
// Make your vowels
let vowels = CharacterSet(charactersIn: "aeiouy")
let consonants = CharacterSet.letters.subtracting(vowels)
let punctuation = CharacterSet.punctuationCharacters
// Set up vars to track our coutns
var vowelCount = 0
var consonantCount = 0
var punctuationCount = 0
string.forEach { char in
// Make a set of one character
let singleCharSet = CharacterSet(charactersIn: String(char))
// If the character is a member of a set, incremennt the relevant var
if singleCharSet.isSubset(of: vowels) { vowelCount += 1 }
if singleCharSet.isSubset(of: consonants) { consonantCount += 1 }
if singleCharSet.isSubset(of: punctuation) { punctuationCount += 1 }
}
return (vowelCount, consonantCount, punctuationCount)
}
let testString = "Smokey, this is not 'Nam. This is bowling. There are rules."
// (vowels 17, consonants 27, punctuation 5)
components(in: testString)

You put your return statement inside the loop, so the first time the loop code executes, the function will return. That is why there will only be one consonant since the switch case statement is only executed once.
You need to put your return statement outside the loop, like this:
func findVowelsConsonantsPunctuation(sentenceInput: String) -> (Int, Int, Int) {
var vowels = 0
var consonants = 0
var punctuation = 0
for character in sentenceInput.characters {
switch String(character).lowercase {
case "a", "e", "i", "o", "u":
vowels += 1
case ".","!",":",";","?", " ", "'", "":
punctuation += 1
default:
consonants += 1
}
}
return (vowels, consonants, punctuation)
}

Try this below. You had your return statement within your for-loop. Thus you're returning after the first iteration. I also moved your lowercased() above the for-loop. That way it's less processing time to lowercase the character every iteration.
func findVowelsConsonantsPunctuation(sentenceInput: String) -> (Int, Int, Int) {
var vowels = 0; var consonants = 0; var punctuation = 0
sentenceInput = sentenceInput.lowercased()
for character in sentenceInput.characters {
switch character {
case 'a', 'e', 'i', 'o', 'u':
vowels += 1
case '.','!',':',';','?', ' ', '\'', '':
punctuation += 1
default:
consonants += 1
}
}
return (vowels, consonants, punctuation)
}

func findVowelsConsonants (_ sentence: String)-> (Int, Int){
let sentenceLowercase = sentence.lowercased()
var tuple = (numberOfVowels: 0, numberOfConsonants: 0, numberOfPunctuation: 0)
for char in sentenceLowercase{
switch char{
case "a", "e", "i", "o", "u":
tuple.0 += 1
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
tuple.1 += 1
default:
tuple.2 += 1
}
}
return (tuple.0, tuple.1)
}

Related

simple Swift function with Xcode

I am trying to program a simple function and am having some trouble. The purpose of the function is to take a String and if the String does not start with a consonant, remove each vowel until you reach the first consonant. For example if the given String was "eat" the function would return "t". If the given String was "awesome" it would return "wesome"
Below is my code, it compiles without error but I cannot get the desired output. Right now it just outputs an empty string. id appreciate any advice, Thanks
This would be the correct and efficient solution:
func removeFirstLetter(word: String) -> String {
var lyricalWord = word
let vowelArray = ["a", "e", "i", "o", "u", "y"]
for _ in lyricalWord {
// We are moving these to the loop so every time we get updated first character
var firstCharacter = lyricalWord[lyricalWord.startIndex]
var str = String(firstCharacter)
if vowelArray.contains(str) {
lyricalWord.remove(at: lyricalWord.startIndex)
} else {
// If this time the first character is consonant that means our string is ready and we can return it and finish the loop
return lyricalWord
}
}
return lyricalWord
}
print(removeFirstLetter(word: "aeiouty"))
But it can even be improved!
In your loop you iterate every time through the whole array of vowel. However, you have a nice structure called Set which works like hash table.
In simple words, while the function "contains():" calls your array 6 times to compare the letter with every vowel, the same implementation with Set makes only 1 call(not always, but most of the time)! And it's especially profitable when you gonna have bigger collections of data to compare with.
So, here goes the implementation with Set:
func removeFirstLetter(word: String) -> String {
var lyricalWord = word
let vowelArray: Set<String> = ["a", "e", "i", "o", "u", "y"] // The only change is here!
for _ in lyricalWord {
var firstCharacter = lyricalWord[lyricalWord.startIndex]
var str = String(firstCharacter)
if vowelArray.contains(str) {
lyricalWord.remove(at: lyricalWord.startIndex)
} else {
return lyricalWord
}
}
return lyricalWord
}
print(removeFirstLetter(word: "aeiouty"))
Two lines below the position are invalid.
let firstCharacter = lyricalworld[lyricalworld.startIndex]
let str = String(firstCharacter)
Please put it inside the for door.
for _ in lyricalworld {
let firstCharacter = lyricalworld[lyricalworld.startIndex]
let str = String(firstCharacter)
if vowelArray.contains(str){
lyricalworld.remove(at: lyricalworld.startIndex)
}
}
Full Source
func removeFirstLetter(word: String) -> String{
var lyricalworld = word
let vowelArray = ["a", "e", "i", "o", "u", "y"]
for _ in lyricalworld {
let firstCharacter = lyricalworld[lyricalworld.startIndex]
let str = String(firstCharacter)
if vowelArray.contains(str){
lyricalworld.remove(at: lyricalworld.startIndex)
}
}
return lyricalworld
}

Finding The First Non-repeating Character algorithm Swift 4 (Looping over string only once)

I am trying to solve code fights interview practice questions, but I am stuck on how to solve this particular problem in swift. My first thought was to use a dictionary with the counts of each character, but then I would have to iterate over the string again to compare, so that doesn't work per the restrictions. Any help would be good. Thank you. Here is the problem and requirements:
Note: Write a solution that only iterates over the string once and uses O(1) additional memory, since this is what you would be asked to do during a real interview.
Given a string s, find and return the first instance of a non-repeating character in it. If there is no such character, return '_'
Here is the code I started with (borrowed from another post)
func firstNotRepeatingCharacter(s: String) -> Character {
var countHash:[Character:Int] = [:]
for character in s {
countHash[character] = (countHash[character] ?? 0) + 1
}
let nonRepeatingCharacters = s.filter({countHash[$0] == 1})
let firstNonRepeatingCharacter = nonRepeatingCharacters.first!
return firstNonRepeatingCharacter
}
firstNotRepeatingCharacter(s:"abacabad")
You can create a dictionary to store the occurrences and use first(where:) method to return the first occurrence that happens only once:
Swift 4
func firstNotRepeatingCharacter(s: String) -> Character {
var occurrences: [Character: Int] = [:]
s.forEach{ occurrences[$0, default: 0] += 1 }
return s.first{ occurrences[$0] == 1 } ?? "_"
}
Swift 3
func firstNotRepeatingCharacter(s: String) -> Character {
var occurrences: [Character:Int] = [:]
s.characters.forEach{ occurrences[$0] = (occurrences[$0] ?? 0) + 1}
return s.characters.first{ occurrences[$0] == 1 } ?? "_"
}
Another option iterating the string in reversed order and using an array of 26 elements to store the characters occurrences
func firstNotRepeatingCharacter(s: String) -> Character {
var chars = Array(repeating: 0, count: 26)
var characters: [Character] = []
var charIndex = 0
var strIndex = 0
s.characters.reversed().forEach {
let index = Int(String($0).unicodeScalars.first!.value) - 97
chars[index] += 1
if chars[index] == 1 && strIndex >= charIndex {
characters.append($0)
charIndex = strIndex
}
strIndex += 1
}
return characters.reversed().first { chars[Int(String($0).unicodeScalars.first!.value) - 97] == 1 } ?? "_"
}
Use a dictionary to store the character counts as well as where they were first encountered. Then, loop over the dictionary (which is constant in size since there are only so many unique characters in the input string, thus also takes constant time to iterate) and find the earliest occurring character with a count of 1.
func firstUniqueCharacter(in s: String) -> Character
{
var characters = [Character: (count: Int, firstIndex: Int)]()
for (i, c) in s.characters.enumerated()
{
if let t = characters[c]
{
characters[c] = (t.count + 1, t.firstIndex)
}
else
{
characters[c] = (1, i)
}
}
var firstUnique = (character: Character("_"), index: Int.max)
for (k, v) in characters
{
if v.count == 1 && v.firstIndex <= firstUnique.index
{
firstUnique = (k, v.firstIndex)
}
}
return firstUnique.character
}
Swift
Use dictionary, uniqueCharacter optional variable with unique characters array to store all uniquely present characters in the string , every time duplication of characters found should delete that character from unique characters array and same time it is the most first character then should update the dictionary with its count incremented , refer following snippet , how end of the iteration through all characters gives a FIRST NON REPEATED CHARACTER in given String. Refer following code to understand it properly
func findFirstNonRepeatingCharacter(string:String) -> Character?{
var uniqueChars:[Character] = []
var uniqueChar:Character?
var chars = string.lowercased().characters
var charWithCount:[Character:Int] = [:]
for char in chars{
if let count = charWithCount[char] { //amazon
charWithCount[char] = count+1
if char == uniqueChar{
uniqueChars.removeFirst()
uniqueChar = uniqueChars.first
}
}else{
charWithCount[char] = 1
uniqueChars.append(char)
if uniqueChar == nil{
uniqueChar = char
}
}
}
return uniqueChar
}
// Use
findFirstNonRepeatingCharacter(string: "eabcdee")

Swift split string to array with exclusion

I write Swift application that parse log file.
log file string:
substr1 substr2 "substr 3" substr4
I need to get array: [substr1, substr2, substr 3, substr4]
But if I use something like:
print(stringLine.components(separatedBy: " "))
I got: [substr1, substr2, "substr, 3", substr4].
How to receive array: [substr1, substr2, substr 3, substr4]?
One of the possible solutions is to use map:
let testSting = "substr1 substr2 \"substr3\" substr4"
let mappedString = testString.components(separatedBy: " ").map({$0.replacingOccurrences(of: "\"", with: "")})
print(mappedString) //["substr1", "substr2", "substr3", "substr4"]
This case of the issue is required to use regular expression but this example is provided. So to solve problem in you're case it is possible to go in this way:
var testStingArray = testSting.replacingOccurrences(of: "\"", with: "").components(separatedBy: " ")
var arr = [String]()
var step = 0
while step < testStingArray.count {
var current = testStingArray[step]
var next = step + 1
if next < testStingArray.count {
if testStingArray[next].characters.count == 1 {
current += " " + testStingArray[next]
testStingArray.remove(at: next)
}
}
arr.append(current)
step += 1
}
print(arr)//["substr1", "substr2", "substr 3", "substr4"]
You'd better work with regular expression:
let pattern = "([^\\s\"]+|\"[^\"]+\")"
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let line = "substr1 substr2 \"substr 3\" substr4"
let arr = regex.matches(in: line, options: [], range: NSRange(0..<line.utf16.count))
.map{(line as NSString).substring(with: $0.rangeAt(1)).trimmingCharacters(in: CharacterSet(charactersIn: "\""))}
print(arr) //->["substr1", "substr2", "substr 3", "substr4"]
Alternatively you could split the string based on a CharacterSet and then filter out the empty occurrences:
let stringLine = "substr1 substr2 \"substr3\" substr4"
let array = stringLine.components(separatedBy: CharacterSet(charactersIn: "\" ")).filter { !$0.isEmpty }
print (array)
Output: ["substr1", "substr2", "substr3", "substr4"]
But this will not work correctly if there is a " somewhere in one of the 'substrings', then that specific substring will also be split.
Or, simply iterate over the characters and maintain state about the quoted parts:
//: Playground - noun: a place where people can play
import UIKit
extension String {
func parse() -> [String] {
let delimiter = Character(" ")
let quote = Character("\"")
var tokens = [String]()
var pending = ""
var isQuoted = false
for character in self.characters {
if character == quote {
isQuoted = !isQuoted
}
else if character == delimiter && !isQuoted {
tokens.append(pending)
pending = ""
}
else {
pending.append(character)
}
}
// Add final token
if !pending.isEmpty {
tokens.append(pending)
}
return tokens
}
}
print ("substr1 substr2 \"substr 3\" substr4".parse()) // ["substr1", "substr2", "substr 3", "substr4"]
print ("\"substr 1\" substr2 \"substr 3\" substr4".parse()) // ["substr 1", "substr2", "substr 3", "substr4"]
print ("a b c d".parse()) // ["a", "b", "c", "d"]
Note: this code doesn't take into account that double quotes "" might be used to escape a single quote. But I don't know if that's a possibility in your case.
https://tburette.github.io/blog/2014/05/25/so-you-want-to-write-your-own-CSV-code/

attribute multiple values to a single key ( dictionary) in Swift

I'd like to ask how can I attribute several values to the same key then use those (same) key (from several values).
the exercise comes from https://www.weheartswift.com/dictionaries/
it's just an adaptation of his code.
i'm first creating a dictionary like this with multiple values for one keys:
var code = [
"X" : "a","b",
"Y" : "c","d",
"Z" : "e","f",
...
]
Then I'd like when I enter words containing a b c d e or f, it changes those letters to X Y or Z depending the dictionary
var encodedMessage = "abcdef"
var decoder: [String:[String]] = [:]
// reverse the code
for (key, value) in code {
decoder[value] = key
}
//an error occurs here, what can i do to fix it?
var decodedMessage = ""
for char in encodedMessage {
var character = "\(char)"
if let encodedChar = decoder[character] {
// letter
decodedMessage += encodedChar
} else {
// space
decodedMessage += character
}
}
and since i prefer decoding the message without divide "letter" and "space" is there any better and easier way?
so it will be like, there won't be "space"
println(decodedMessage)
i'd like the decodedMessage is XXYYZZ
thank you already for those who can help.
Regards
Here:
let encodedMessage = "abcdef"
var code = ["X": ["a", "b"], "Y": ["c", "d"], "Z": ["e", "f"]]
var decoder: [String:String] = [:]
// reverse the code
for key in code.keys {
for newCode in code[key]! {
decoder[newCode] = key
}
}
var decodedMessage = ""
for char in encodedMessage.characters {
var character = "\(char)"
if let encodedChar = decoder[character] {
// letter
decodedMessage += encodedChar
} else {
// space
decodedMessage += character
}
}
You just need to store them as an array.

How do I cycle through the entire alphabet with Swift while assigning values?

I am trying to cycle through the entire alphabet using Swift. The only problem is that I would like to assign values to each letter.
For Example: a = 1, b = 2, c = 3 and so on until I get to z which would = 26.
How do I go through each letter in the text field that the user typed while using the values previously assigned to the letters in the alphabet?
After this is done, how would I add up all the letters values to get a sum for the entire word. I am looking for the simplest possible way to accomplish this but works the way I would like it to.
edit/update: Xcode 12.5 • Swift 5.4
extension Character {
static let alphabetValue = zip("abcdefghijklmnopqrstuvwxyz", 1...26).reduce(into: [:]) { $0[$1.0] = $1.1 }
var lowercased: Character { .init(lowercased()) }
var letterValue: Int? { Self.alphabetValue[lowercased] }
}
extension String {
var wordValue: Int { compactMap(\.letterValue).reduce(0, +) }
}
Character("A").letterValue // 1
Character("b").letterValue // 2
Character("c").letterValue // 3
Character("d").letterValue // 4
Character("e").letterValue // 5
Character("Z").letterValue // 26
"Abcde".wordValue // 15
I'd create a function something like this...
func valueOfLetter(inputLetter: String) -> Int {
let alphabet = ["a", "b", "c", "d", ... , "y", "z"] // finish the array properly
for (index, letter) in alphabet {
if letter = inputLetter.lowercaseString {
return index + 1
}
}
return 0
}
Then you can iterate the word...
let word = "hello"
var score = 0
for character in word {
score += valueOfLetter(character)
}
Assign the letters by iterating over them and building a dictionary with letters corresponding to their respective values:
let alphabet: [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"
]
var alphaDictionary = [String: Int]()
var i: Int = 0
for a in alphabet {
alphaDictionary[a] = ++i
}
Use Swift's built-in Array reduce function to sum up the letters returned from your UITextViewDelegate:
func textViewDidEndEditing(textView: UITextView) {
let sum = Array(textView.text.unicodeScalars).reduce(0) { a, b in
var sum = a
if let d = alphaDictionary[String(b).lowercaseString] {
sum += d
}
return sum
}
}
I've just put together the following function in swiftstub.com and it seems to work as expected.
func getCount(word: String) -> Int {
let alphabetArray = Array(" abcdefghijklmnopqrstuvwxyz")
var count = 0
// enumerate through each character in the word (as lowercase)
for (index, value) in enumerate(word.lowercaseString) {
// get the index from the alphabetArray and add it to the count
if let alphabetIndex = find(alphabetArray, value) {
count += alphabetIndex
}
}
return count
}
let word = "Hello World"
let expected = 8+5+12+12+15+23+15+18+12+4
println("'\(word)' should equal \(expected), it is \(getCount(word))")
// 'Hello World' should equal 124 :)
The function loops through each character in the string you pass into it, and uses the find function to check if the character (value) exists in the sequence (alphabetArray), and if it does it returns the index from the sequence. The index is then added to the count and when all characters have been checked the count is returned.
Maybe you are looking for something like this:
func alphabetSum(text: String) -> Int {
let lowerCase = UnicodeScalar("a")..."z"
return reduce(filter(text.lowercaseString.unicodeScalars, { lowerCase ~= $0}), 0) { acc, x in
acc + Int((x.value - 96))
}
}
alphabetSum("Az") // 27 case insensitive
alphabetSum("Hello World!") // 124 excludes non a...z characters
The sequence text.lowercaseString.unicodeScalars ( lower case text as unicode scalar ) is filtered filter keeping only the scalars that pattern match ~= with the lowerCase range.
reduce sums all the filtered scalar values shifted by -96 (such that 'a' gives 1 etc.). reduce starts from an accumulator (acc) value of 0.
In this solution the pattern match operator will just check for the scalar value to be between lowerCase.start (a) and lowerCase.end (z), thus there is no lookup or looping into an array of characters.