simple Swift function with Xcode - swift

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
}

Related

How do you turn a string into a unicode family in Swift?

I'm trying to make a feature in my app that when a user types in a text field, the text converts into a unicode family.
Like below, there is an image of a user typing. And as the user types, you can see different unicode family characters that when a user types on a cell, you can copy the text and paste it somewhere else.
If I would like to turn my text into the black bubble unicode family like the screenshot above, how can I do that?
You can define a character map. Here's one to get you started.
let circledMap: [Character : Character] = ["A": "🅐", "B": "🅑", "C": "🅒", "D": "🅓"] // The rest are left as an exercise
let circledRes = String("abacab".uppercased().map { circledMap[$0] ?? $0 })
print(circledRes)
If your map contains mappings for both upper and lowercase letters then don't call uppercased.
Create whatever maps you want. Spend lots of time with the "Emoji & Symbols" viewer found on the Edit menu of every macOS program.
let invertedMap: [Character : Character] = ["a": "ɐ", "b": "q", "c": "ɔ", "d": "p", "e": "ǝ", "f": "ɟ", "g": "ƃ", "h": "ɥ"]
In a case like the circled letters, it would be nice to define a range where you can transform "A"..."Z" to "🅐"..."🅩".
That actually takes more code than I expected but the following does work:
extension String {
// A few sample ranges to get started
// NOTE: Make sure each mapping pair has the same number of characters or bad things may happen
static let circledLetters: [ClosedRange<UnicodeScalar> : ClosedRange<UnicodeScalar>] = ["A"..."Z" : "🅐"..."🅩", "a"..."z" : "🅐"..."🅩"]
static let boxedLetters: [ClosedRange<UnicodeScalar> : ClosedRange<UnicodeScalar>] = ["A"..."Z" : "🅰"..."🆉", "a"..."z" : "🅰"..."🆉"]
static let italicLetters: [ClosedRange<UnicodeScalar> : ClosedRange<UnicodeScalar>] = ["A"..."Z" : "𝐴"..."𝑍", "a"..."z" : "𝑎"..."𝑧"]
func transformed(using mapping: [ClosedRange<UnicodeScalar> : ClosedRange<UnicodeScalar>]) -> String {
let chars: [UnicodeScalar] = self.unicodeScalars.map { ch in
for transform in mapping {
// If the character is found in the range, convert it
if let offset = transform.key.firstIndex(of: ch) {
// Convert the offset from key range into an Int
let dist = transform.key.distance(from: transform.key.startIndex, to: offset)
// Build new index into value range
let newIndex = transform.value.index(transform.value.startIndex, offsetBy: dist)
// Get the mapped character
let newch = transform.value[newIndex]
return newch
}
}
// Not found in any of the mappings so return the original as-is
return ch
}
// Convert the final [UnicodeScalar] into a new String
var res = ""
res.unicodeScalars.append(contentsOf: chars)
return res
}
}
print("This works".transformed(using: String.circledLetters)) // 🅣🅗🅘🅢 🅦🅞🅡🅚🅢
The above String extension also requires the following extension (thanks to this answer):
extension UnicodeScalar: Strideable {
public func distance(to other: UnicodeScalar) -> Int {
return Int(other.value) - Int(self.value)
}
public func advanced(by n: Int) -> UnicodeScalar {
let advancedValue = n + Int(self.value)
guard let advancedScalar = UnicodeScalar(advancedValue) else {
fatalError("\(String(advancedValue, radix: 16)) does not represent a valid unicode scalar value.")
}
return advancedScalar
}
}

How to return one single string from matching dictionary values?

I am trying to return one string of selected values from a dictionary. When I iterate over the dictionary and the array I am fetching the keys from, I can print out the value, but as different strings, not combined.
So far I have iterated over the collection types, and tried map, filter, reduce to some extent. Also tried to put the results into arrays and dictionaries to use .joined, but I am getting nil all the time.
This is the dictionary I am getting the values from:
let letterDict = ["a": "1", "b": "2", "c": "3", "d": "4"]
This is the array I am querying from:
let characterArray = ["a", "b"]
This is the code I was able to get the results from:
func convertText(_ input: String) -> String {
var expectedText: String?
for character in characterArray {
for (key, value) in letterDict {
if key.contains(character) {
print(value)
expectedText = value
}
}
}
return expectedText ?? input
}
I am trying to return 12, but what I get is:
1
2
all the time. Even when I try to map them to an array, it just returns separate arrays (or dictionaries). And when I try to append to an array, it just returns nil.
Thanks in advance.
You need to append value to expectedText rather than assign it.
func convertText(_ input: String) -> String {
var expectedText = ""
for character in characterArray {
for (key, value) in letterDict {
if key.contains(character) {
expectedText += value
}
}
}
return expectedText.isEmpty ? input : expectedText
}
Here's another way you might do it using compactMap with a [Character : Character] dictionary:
let letterDict: [Character : Character] = ["a": "1", "b": "2", "c": "3", "d": "4"]
let str = "ab"
var newstr = String(str.compactMap { letterDict[$0] })
print(newstr) // 12
This will return the empty string "" if no characters matched, whereas your code returned the original string. If that is the desired result, you could follow this with:
newstr = newstr.isEmpty ? str : newstr
So your function becomes:
func convertText(_ input: String) -> String {
let expectedText = String(input.compactMap { letterDict[$0] })
return expectedText.isEmpty ? input : expectedText
}
print(convertText("ab")) // 12
print(convertText("ef")) // ef

How to count vowels and consonants

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

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.