attribute multiple values to a single key ( dictionary) in Swift - 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.

Related

Get all values into dictionary and create a String with a specific format

I have a dictionary with this structure:
a: [1,2]
b: [3,4]
c: [5,6]
and I need to return a string with this structure.
a,b,c\n1,3,5\n2,4,6
I solved the first part of the string. But to get the rest of the String. I try to iterate into my dictionary to get the first elements for each key in my dictionary and then get the rest for each value into the array.
Is there an easier way to get this?
Once you know what's the order of the keys (alpha ?), you can use this:
let dict: [String: [Int]] = ["a": [1,2], "b": [3, 4], "c": [5, 6]]
let keys = dict.keys.sorted() //Or do whatever you want here to get your target order
var matrix: [[String]] = []
keys.forEach {
guard let arrayAsInt = dict[$0] else { return }
let arrayAsString = arrayAsInt.map{ "\($0)" }
matrix.append( [$0] + arrayAsString)
}
print("Matrix: \(matrix)")
let transposed = matrix.transposed()
print("Transposed Matrix: \(transposed)")
let output = transposed.map { $0.joined(separator: ",")}.joined(separator: "\n")
print(output)
The outputs:
$>Matrix: [["a", "1", "2"], ["b", "3", "4"], ["c", "5", "6"]]
$>Transposed Matrix: [["a", "b", "c"], ["1", "3", "5"], ["2", "4", "6"]]
$>a,b,c
1,3,5
2,4,6
Obvisouly the "\n" might be invisible and be an actual new line
a,b,c
1,3,5
2,4,6
Being
a,b,c\n1,3,5\n2,4,6
What's the idea behind that? Create a matrix and use the transpose (it's used in maths with matrix, it's one of the basic modification of a matrix).
First transform the [String: [Int]] into a [[String]], where each element would be key followed by its values. I transformed it there as String for simpler code later.
Why doing that? Because the matrix value is easy to get from your initial dict. the transposed value is harder (not impossible) to get from dict but easier from matrix, and the transposed is quickly transformed into your format.
So my thinking was the reverse:
Get a structure from your output, then how to get it, it's a transpose, so I need to get the initial input as it, etc.
With the help of a code for Transpose Matrix (that accept String elements).
extension Collection where Self.Iterator.Element: RandomAccessCollection {
// PRECONDITION: `self` must be rectangular, i.e. every row has equal size.
func transposed() -> [[Self.Iterator.Element.Iterator.Element]] {
guard let firstRow = self.first else { return [] }
return firstRow.indices.map { index in
self.map{ $0[index] }
}
}
}
Any code (there a various) working ones, should the trick. I took it from here.
As pointed by #Leo Dabus, you can remove the Self.Iterator.Element
from the extension code (twice). I just wanted to it as such, not modifying the initial answer since it's not mind.
What you are looking for, besides composing the final string, is how to transpose a collection (this would work with collections of different sizes as well):
extension Sequence {
func max<T: Comparable>(_ predicate: (Element) -> T) -> Element? {
self.max(by: { predicate($0) < predicate($1) })
}
}
extension Collection where Element: RandomAccessCollection, Element.Indices == Range<Int> {
func transposed() -> [[Element.Element]] {
(0..<(max(\.count)?.count ?? .zero)).map {
index in compactMap { $0.indices ~= index ? $0[index] : nil }
}
}
}
let dict = ["a": [1,2,3],
"b": [4,5,6],
"c": [7,8,9]]
let sorted = dict.sorted(by: {$0.key < $1.key})
let result = sorted.map(\.key).joined(separator: ",") + "\n" +
sorted.map(\.value).transposed().map {
$0.map(String.init).joined(separator: ",")
}.joined(separator: "\n")
result // "a,b,c\n1,4,7\n2,5,8\n3,6,9"
A dictionary is an unordered collection so you need to sort it according to any specific key. Here I sort the dictionary according to the key if you don't care about an order you can just remove sort.
let dict: [String: Any] = ["a": [1,2], "b": [3,4], "c": [5,6]]
let sortedKey = dict.keys.sorted(by: <)
let key = sortedKey.joined(separator: ",")
var firstValues: [String] = []
var secondValues: [String] = []
sortedKey.forEach { (key) in
if let dictValue = dict[key] as? [Int],
let firstValue = dictValue.first,
let secondValue = dictValue.last {
firstValues.append("\(firstValue)")
secondValues.append("\(secondValue)")
}
}
let finalString = key + "\n" + firstValues.joined(separator: ",") + "\n" + secondValues.joined(separator: ",")
print(finalString) // "a,b,c\n1,3,5\n2,4,6"

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

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
}

How to compare to arrays whether the objects of one array are there in the other or not

let say we have:
var a = ["z", "x"]
var b = ["z", "x", "c", "d"]
now i want to check whether objects of array a are there in array b or not..
thanks for your time..
The simplest way is to use map in combination with contains:
let matches = map(a) { return contains(b, $0) }
contains checks to see if an item is in a collection
map is used to convert one array into another array with an element-by-element mapping.
As pointed out by #marcos, there are similar variants to answer the any and all questions as well
let any = a.reduce(false) { acc, item in return acc || contains(b, item) }
let all = a.reduce(true) { acc, item in return acc && contains(b, item) }
Just use the map:
var a = [1, 7, 5, 2]
var b = [1, 2, 3, 4]
let d = a.map { b.contains($0) }
print(d) // [true, false, false, true]
I think this following code snippet wil work:
var booleanArray = [Bool]()
for x in a{
if !contains(b, x){
booleanArray.append(false)
}
else{
booleanArray.append(true)
}
You can use a for in loop and then use the array contains function:
Swift 2.0
var resultsArray : [Bool] = [Bool]()
let a = ["a","b","c"]
let b = ["a","c"]
for letter in a {
if b.contains(letter) == true {
// Letter exists in a array
resultsArray.append(true)
} else {
// Letter does not exist in a array
resultsArray.append(false)
}
}
Swift 1.2
Replace b.contains(letter) with:
contains(b, letter)
You could convert the arrays to Set to use the intersect method
let a = ["a","c"]
let b = ["a","b","c","d"]
let aSet = Set(a)
let bSet = Set(b)
let intersection = aSet.intersect(bSet) // ["a", "c"]

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.