How to capitalize every other letter in a string/array in Swift? - arrays

I am trying to capitalize every even numbered placeholder in a string in Swift. So the character in [0],[2],[4],[6] all get uppercased.
I have a declared variable:
var str = "This is a test"
I have an array that explodes the variable into an array:
let characters = Array(str) //Creates the array "["T", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]\n"
On top of that, I am creating an empty array to input the newly capitalized/lowercased letters
var newCharacters = Array<Character>()
And then declaring index at 0
var index = 0 //declaring the index at 0
I am trying to figure out how to create the for loop that will sniff out the even numbered array item and then capitalize the character found in it.
I have created a for loop that will manipulate the even numbered placeholders in the array, I just do not know the syntax to capitalize the string of every other one:
for letter in characters {
if index % 2 == 0 {
}
}
I am trying to figure out: what the syntax is to capitalize every other letter (even numbers in the array), put them in the new array, and then convert it back to a string.
The end result should be:
"ThIs iS TeSt"

You can combine enumerated, map, and joined in sequence to create the result:
let str = "This is a test"
let result = str.enumerated()
.map { $0.offset % 2 == 0 ? String($0.element).uppercased() : String($0.element) }
.joined()
print(result)
ThIs iS A TeSt
Explanation:
A String can be enumerated as if it were an array of Character. Calling .enumerated() on a String causes it to produces a sequence of (offset: Int, element: Character) tuples.
map takes a sequence and creates an array. The closure following map is called for each element of the sequence in turn and the value that the closure returns becomes the next element in the new array.
$0 is the default name for the value passed to map. In this case, we're passing the (offset: Int, element: Character) tuple. $0.offset is the position of the character in the String, and $0.element is the character.
The ternary operator is used here to return the uppercased() String that is created from the Character if the offset is even or just the String if the offset is odd.
The result of the map is [String], and joined() is then used to join the array of String back into a single String.

One way is using stride:
var str = "This is a test"
var chars = Array(str).map { String($0) }
for i in stride(from: 0, to: chars.count, by: 2) {
chars[i] = chars[i].uppercased()
}
var hiphopcasedStr = chars.joined()
Note that while you're in Unicode land, some characters uppercase to multicharacter strings, so array of Character is not quite appropriate (thus the conversion to array of String).

let str = "This is a test"
let result = str.indices.map {
str.distance(from: str.startIndex, to: $0) % 2 == 0 ?
str[$0...$0].uppercased() :
str[$0...$0].lowercased() }.joined()
print(result) // "ThIs iS A TeSt\n"
or as a mutating method on StringProtocol:
extension StringProtocol where Self: RangeReplaceableCollection {
mutating func capitalizeEveryOther() {
indices.forEach {
replaceSubrange($0...$0, with:
distance(from: startIndex, to: $0) % 2 == 0 ?
self[$0...$0].uppercased() :
self[$0...$0].lowercased() )
}
}
var capitalizingEveryOther: Self {
var result = self
result.capitalizeEveryOther()
return result
}
}
var str5 = "This is a test"
str5.capitalizeEveryOther()
print(str5) // ThIs iS A TeSt

try this code
var str = "This is a test"
var result = [String]()
for (index,element) in str.enumerated() {
var val:String = String(element).lowercased()
if index % 2 == 0 {
val = String(element).uppercased()
}
result.append(val)
}
print(result.joined())
result
ThIs iS A TeSt

Related

Swift: Evaluating if the letters in a String are contained within a longer String

Okay so my current code works, but I have a feeling it's incredibly inefficient. Essentially, I need to evaluate if a String contains the letters of a String that is shorter or the same length as the first one. (Imagine trying to use the letters that exist in Word A to spell a new word Word B. Word B can be shorter than Word A or the same length but has to only use the letters from Word A and cannot use the same letter twice.)
My current solution is to sort both strings into an array, then index each letter in the Word B array, check if it appears in the Word A array, then remove that character from the Word A array.
let wordOne = "battle"
let wordTwo = "table"
var wordOneSorted = wordOne.sorted()
var wordTwoSorted = wordTwo.sorted()
for letter in wordTwoSorted {
if wordOneSorted.contains(letter) {
print("Valid Letter")
let idx = wordOneSorted.firstIndex(of:letter)
wordOneSorted.remove(at: idx!)
} else {
print("Invalid Letter")
}
}
Prints:
Valid Letter
Valid Letter
Valid Letter
Valid Letter
Valid Letter
This works but it feels clunky and I wanted to see if I'm making a simple task more complicated than I need it to be. I only need an evaluation of the entire comparison, if all the leters work than "True" and if at least one is invalid than "False".
Thank you!
Your code can give a simple good/bad response as follows:
let wordOne = "battle"
let wordTwo = "table"
var letters = wordOne
var good = true
for letter in wordTwo {
if letters.contains(letter) {
let idx = letters.firstIndex(of:letter)
letters.remove(at: idx!)
} else {
good = false
break
}
}
print(good ? "Good" : "Bad")
There's no need to sort the letters of each word. That doesn't make this approach any more efficient. I add the var letters just so the value can be modified as the loop runs.
Here's an alternate approach using NSCountedSet. This isn't a pure Swift class but is provided by Foundation.
let wordOne = "battle"
let wordTwo = "table"
let set1 = NSCountedSet(array: Array(wordOne))
let set2 = NSCountedSet(array: Array(wordTwo))
let extra = set2.filter { set2.count(for: $0) > set1.count(for: $0) }
print(extra.isEmpty ? "Good" : "Bad")
NSCountedSet is a subclass of Set (really of NSSet and NSMutableSet) that adds a count for each element in the set.
The filter makes sure there are enough of each letter. Anything left in extra means wordTwo had more instances of a letter than in wordOne.
As pointed out, using allSatisfy, rather than filter, would be more efficient (though trivial on such short words). Change the last two lines to:
let good = set2.allSatisfy { set2.count(for: $0) <= set1.count(for: $0) }
print(good ? "Good" : "Bad")
I was wondering how I would, and here is an alternative. It's good to have different ways of thinking an issue.
We create a [Character: (Int, Int)] where - keys will be the letter, and first tuple value the number of occurences in str1, and second tuple value the number of occurrences in str2.
Then, we check if all values present in str1 have at leas the same number of occurrences that in str2.
var counter: [Character: (Int, Int)] = [:]
counter = str1.reduce(counter) {
var values = $0
var tuple: (Int, Int) = $0[$1, default: (0, 0)]
values[$1] = (tuple.0 + 1, tuple.1)
return values
}
counter = str2.reduce(counter) {
var values = $0
var tuple: (Int, Int) = $0[$1, default: (0, 0)]
values[$1] = (tuple.0, tuple.1 + 1)
return values
}
let doesInclude = counter.allSatisfy { _, tuple in
tuple.0 >= tuple.1
}
print("\(str1) includes all letters from \(str2): \(doesInclude)")
The value of counter for "abc" vs "cde":
["d": (0, 1), "b": (1, 0), "c": (1, 1), "e": (0, 1), "a": (1, 0)]
The value of counter for "battle" vs "table":
["t": (2, 1), "e": (1, 1), "a": (1, 1), "b": (1, 1), "l": (1, 1)]
I like #HangarRash answer, but you could also use sort to your advantage, i.e. when letters are sorted you can move in both arrays simultaneously and stop as soon as the first difference was found (no need to remove anything):
func isContained(in word1: String, word word2: String) -> Bool {
var word1Sorted = word1.sorted()
var word2Sorted = word2.sorted()
var c1 = word1Sorted.count - 1
var c2 = word2Sorted.count - 1
while c2 >= 0 && c1 >= 0 {
if word1Sorted[c1] == word2Sorted[c2] {
// Found a match - advance to next character in both arrays
print("Valid Letter \(word2Sorted[c2])")
c1 -= 1
c2 -= 1
} else if word1Sorted[c1] > word2Sorted[c2] {
// Ignoring a letter present in wordOne, but not wordTwo
print("Skipping Letter \(word1Sorted[c1])")
c1 -= 1
} else { // wordOneSorted[c1] < wordTwoSorted[c2]
// the letter was not found in wordOneSorted - no need to continue
print("Invalid Letter \(word2Sorted[c2])")
return false
}
}
// If we finished the loop then the result is:
// - We've got to the beginning of word2, meaning we found all letters of it in word1
// - Or we've got to the beginning of word1, meaning not all letters of word 2 were found
return c2 < 0
}
So for example:
let wordOne = "battle"
let wordTwo = "table"
let good = isContained(in: wordOne, word: wordTwo)
print(good ? "Good" : "Bad")
will run on entire array (of the Word 2 at least):
Valid Letter t
Skipping Letter t
Valid Letter l
Valid Letter e
Valid Letter b
Valid Letter a
Good
While if there's a difference, e.g.
let wordOne = "battle"
let wordTwo = "tables"
let good = isContained(in: wordOne, word: wordTwo)
print(good ? "Good" : "Bad")
it may exit much faster:
Valid Letter t
Skipping Letter t
Invalid Letter s
Bad

How to find a keyword in a decomposed string on swift?

While trying to make a Chatbot, I managed to decompose the sentence, but I don't know how to check if there is a keyword (in my arrays)
my code:
var array1 = ["dog", "cat", "bird"] //my arrays "key words"
var array2 = ["wolf", "sheep", "pig"]
var array3 = ["horse", "frog", "bear"]
var sentence = "" // This would be whatever we write in the text field
let random = Int.random(in: 0...2)
if random == 0 {
sentence = "I like my dog"
}
else if random == 1 {
sentence = "I like my pig"
}
else if random == 2 {
sentence = "I like my horse"
}
let result = sentence.split(separator: " ") //this make the decomposition of the sentence
print(result)
Then I tried to check if there was a keyword but it won't work, this is what I tried:
for counter in 0...array1.count-1 {
if result == array1[counter] {
print("cool!")
}
}
it gaves me: Binary operator '==' cannot be applied to operands of type '[String.SubSequence]' (aka 'Array') and 'String'
Personally, I would just convert every word from your sentence into a String.
let words = sentence.split(separator: " ").map { String($0) }
And used sets instead of arrays:
var keywords1: Set = ["dog", "cat", "bird"] //my arrays "key words"
var keywords2: Set = ["wolf", "sheep", "pig"]
var keywords3: Set = ["horse", "frog", "bear"]
...
if !keywords1.isDisjoint(with: words) {
print("Cool!")
}
Because with sets it's much easier to check for intersection.
result is an array and array1[counter] is a string; you cannot compare an array to a string, you can compare a string/substring to a string/substring or an array to an array. You can compare result == array1 or result[x] == array[y], with x and y being indices of the arrays. If you want to test if a string is within an array, then you can call .contains(...) on an array, see documentation. Yet as your result array is an array of substrings, you need to make sure your test arrays are as well:
let array1 = ["dog", "cat", "bird"] as [Substring]
// ...
for word in array1 {
if result.contains(word) { print("Cool") }
}

How to remove every element in an array after a conditional?

So I have an array of strings in Scala called pronunciationArray like this:
["EH1","N", "D", "P", "ER0", "EH1", "N", "TH", "AH0", "S", "IY2", "Z"]
and i want to write an if else statement that reads the array in reverse, and as soon as it finds a string with a number in it, it either removes all the strings after or puts everything before into a separate array.
so for the above example, id want it to stop at "IY2", then either create a new array with only ["IY2", "Z"] or remove every string after and leave the original array with, like I said, ["IY2", "Z"]. The number itself isnt an integer btw, its part of the string, and the numbers range from 0-2.
I've tried for loop in reverse with an if else that looks for the numbers 0, 1, 2 but it returns every string with a number, so it returns [IY2, AH0, EH1, ER0, EH1] it doesnt stop as soon as it finds the first string with a number. And i'm not sure how to put everything before that string into a new array if I even found a way to stop it.
for (sounds <- pronunciationArray.reverse) {
val number0 = sounds.contains("0")
if (number0 == true) {
println(sounds)
} else if (number0 == true){
println(sounds)
} else if (number1 == true){
println(sounds)
} else {
-1
}
}
I want it to return just ["IY2", "Z"] but it returned [IY2, AH0, EH1, ER0, EH1]
One of the algorithm you can solve with is
find the index of element from the last where element has 0, 1 or 2
split the array at above index
take the last element of first array and whole second array
Example,
val pronunciationArray = Array("EH1","N", "D", "P", "ER0", "EH1", "N", "TH", "AH0", "S", "IY2", "Z")
def takeUntil(inputArray: Array[String], condition: String => Boolean): Array[String] = {
val findIndexFromLast = inputArray.reverse.zipWithIndex.collectFirst {
case (elem, index) if condition.apply(elem) => index
}
findIndexFromLast match { // pattern match to check if there exists element with 0, 1, or 2
case Some(indexFromLast) =>
inputArray.splitAt(inputArray.length - indexFromLast) match {
case (head, tail) => head.last +: tail
}
case None => // no element with 0, 1, 2 exists
inputArray
}
}
takeUntil(
inputArray = pronunciationArray,
condition = elem => elem.contains("0") || elem.contains("1") || elem.contains("2")) //gives [IY2, Z]
The other way to solve the same using .span(predicate) which is better version of .splitAt(index)
def takeUntil2(inputArray: Array[String]): Array[String] = {
inputArray.reverse.span {
element =>
!element.contains("0") && !element.contains("1") && !element.contains("2")
} match {
case (head, tail) => tail.take(1) ++ head.reverse
}
}
val result = takeUntil2(inputArray = pronunciationArray)
Now, using scala way you can extend Array to have custom function,
implicit class ArrayOps(array: Array[String]) {
def takeUntil(predicate: String => Boolean): Array[String] = {
val splitAt = array.reverse.span(predicate.apply)
splitAt._2.take(1) ++ splitAt._1.reverse
}
}
val res = pronunciationArray
.takeUntil(predicate = elem => !elem.contains("0") && !elem.contains("1") && !elem.contains("2"))
Similar question:
How to implement 'takeUntil' of a list?
How do I pattern match arrays in Scala?
I'm not completely clear on your objective, but Array has a span function that splits it into two pieces based on a condition--the first piece is a prefix of the array where everything satisfies the condition and the second piece is the remainder.
Let's say your condition was that the string contains 2 rather than contains a number (just because I'm lazy--it could be any function f: String => Boolean). If we reverse the array and then split on the negation of the function, we get:
scala> a.reverse.span(!_.contains("2"))
res5: (Array[String], Array[String]) = (Array(Z),Array(IY2, S, AH0, TH, N, EH1, ER0, P, D, N, EH1))
We know that the first element of the second array satisfies our condition, so we can just assemble the result:
scala> val (start, end) = a.reverse.span(!_.contains("2"))
start: Array[String] = Array(Z)
end: Array[String] = Array(IY2, S, AH0, TH, N, EH1, ER0, P, D, N, EH1)
scala> val result = end.take(1) ++ start.reverse
result: Array[String] = Array(IY2, Z)
All the reversing is perhaps not the most efficient, but gets the job done.
Scala arrays offer a lastIndexWhere method which takes a predicate and returns the index of the last element which fulfills the predicate:
val arr = Array("EH1","N", "D", "P", "ER0", "EH1", "N", "TH", "AH0", "S", "IY2", "Z")
val idx = arr.lastIndexWhere(e => e.intersect(Array('0', '1', '2')).size > 0)
and then you call the takeRight method to get the desired result
val res = arr.takeRight(arr.size - idx)

RemoveAtIndex crash from swift array

I have an array of letters, and want to match the characters against the letters and then do something to that letter (in this case turn it yellow) and then remove that matched character from the characters array.
If I have a word1 like "recruitment" and specialLetters like "ment" the removeAtIndex works fine, but in the below example which includes 2 s's in [ness] I get this crash:
fatal error: Array index out of range
Reading other posts on here suggest it is dangerous to remove items from an array when in use, but how come it works ok with some words and not others? I thought enumerating the array would only give each letter one index? And any suggestion on how to fix it so it works for all types of characters?
var letters = Array(word1) // [r,a,n,d,o,m,n,e,s,s]
var characters = Array(specialLetters) // [n,e,s,s]
// delete the special letters
for (index, element) in enumerate(characters) {
if letter == element {
tile.letterLabel.textColor = UIColor.yellowColor()
// remove that character from the array so can't be matched twice
characters.removeAtIndex(index)
}
}
So you have an array of Character(s):
let letters = Array("randomness")
An array of special Character(s)
let specials = Array("ness")
And you want to remove from letters, the specials right?
Here it is:
let set = Set(specials)
let filtered = letters.filter { !set.contains($0) }
filtered // ["r", "a", "d", "o", "m"]
Update
This version keep also consider the occurrences of a char
let letters = Array("randomness")
let specials = Array("ness")
var occurrencies = specials.reduce([Character:Int]()) { (var dict, char) in
dict[char] = (dict[char] ?? 0) + 1
return dict
}
let filtered = letters.filter {
if let num = occurrencies[$0] where num > 0 {
occurrencies[$0] = num - 1
return false
} else {
return true
}
}
filtered // ["r", "a", "d", "o", "m", "n"]
Yes it is very dangerous to modify the number of items in an array while being enumerated.
Imagine you have an array of three items, the range is 0...2. In the first iteration you delete the first item, then array[2] is now array[1] and there is no item at index 2 any more.
Either you enumerate the array backwards, so the index of the removed item is always equal to or higher than the current index or you use a variable to collect the indexes to be deleted and delete the characters by range.
Of course Swift has more reliable functions to accomplish this task as mentioned by the others therefore it's not needed to use enumerate In this case.
May i suggest a different approach where instead of removing from the initial array you make a new one, from which you can exclude the characters you don't want to appear in your array in the first place.
PS: keep in mind the following code works with Swift 2.0 Xcode 7 beta 6
var letters : [Character] = ["a","b", "c"]
var lettersToBeRemoved : [Character] = ["a"]
var newLetters : [Character] = []
for i in letters
{
for j in lettersToBeRemoved
{
if i == j
{
continue
}
newLetters.append(i)
}
}
print(newLetters)
just fixed it after reading the SDs post here: Swift buttons hidden fatal error: Array index out of range
I updated this question in case it helps anyone else.
I added an if statement to check the index was still in range. Not really sure why this works but it does :-)
var letters = Array(word1) // [r,a,n,d,o,m,n,e,s,s]
var characters = Array(specialLetters) // [n,e,s,s]
// delete the special letters
for (index, element) in enumerate(characters) {
if letter == element {
tile.letterLabel.textColor = UIColor.yellowColor()
// remove that character from the array so can't be matched twice.
// first check that the index is still in range.
if index < characters.count { // added this if statement
characters.removeAtIndex(index)
}
}
}
For this purpose (removing elements of an array), you may want to use an indexing generator. Let me give an example:
var array = [1, 2, 3, 4]
var ig = array.generate()
var index = 0
var element = ig.next()
while element != nil {
if element == 2 {
array.removeAtIndex(index)
} else {
index = index + 1
}
element = ig.next()
}

Swift code: how do you build a Dictionary of Array of String based on Dictionary entry being nil

I have been trying to understand how to do some simple things with Swift using Dictionary and Array types.
I started by trying to write the simple functions below for manipulating a list of strings and bucketing them into the same array when the strings are anagrams of one another..!
Everything here should run in a playground on Xcode.
Questions:
Why can I not use toString() to convert Character Array to a String. I have to iterate over the Array of Character and build the string using += ?
What is the best way to add an Array to the Dictionary in the fly as I find that the Dictionary is empty for that string index so I need to create a new Array to hold the strings that sage the same letters. The confusion about a Dictionary not holding the types you think but actually holding Type? (that is Option Type - in my case String?, optional String).
See the crazy code below.
groupAnagrams(
listofWords: pass an array of String (immutable)
)
returns
Dictionary of Array of String (grouped anagrams)
A summary of numbers of actions taken in a Array of String
A log of actions taken in an Array of String
Purpose
Group the words that are anagrams together into buckets
The func will create a Dictonary indexed on a String that is the order string of characters, e.g.
["bats", "stab"] => [ "abst" : ["rats", "stab"] ]
Function charArrayToString
func charArrayToString(charArray ca: [Character]) -> String {
var s = ""
for c in ca {
s += String(c)
}
return s
}
func groupAnagrams(#listOfWords: [String]) -> [String:[String]] {
var s = ""
var d: [String:[String]] = [String:[String]]()
var i = 0, j = 0, k = 0
var log = [String]()
for s in listOfWords {
var s1:[Character] = [Character](s)
// var s1 = s
sort(&s1) { (a: Character, b: Character) -> Bool in return String(a) < String(b) }
//??? var s2 = toString(s1) // converts the Array of Character to a string (but it's for the for "[a,b,c,d]" and not "abcd" as you'd expect!
var s3 = charArrayToString(charArray: s1)
// Array already exists, add a string ELSE create the String array [String]() and add the fisst element
if let p = d[s3] {
// Array exists but d[s3] is "immutable" as it's optional array of String (type [String]?)
++i
var arr = d[s3]!
arr += [s]
d[s3] = arr
log += "Add string \(arr.count) \(s) to array \(s3) \n"
} else {
// ELSE: Create new String array
++j
d[s3] = [String]()
var arr = d[s3]!
arr += [s]
d[s3] = arr
log += "Create array for \(s3) with string \(s) \n"
}
++k
// break
}
var summary = [String]()
summary += ["\(i) strings added to already created arrays"]
summary += ["\(j) arrays created"]
summary += ["\(k) words processed"]
summary[0] // inspect
summary[1] // inspect
summary[2] // inspect
log // inspect
return d
}
// Same as the array version of the function but just adds stings to the paratner list of the function
func groupAnagramsList(strings: String...) -> [String:[String]] {
return groupAnagrams(listOfWords: strings)
}
Calls to the various functions created:
var listOfWords = [String]() + ["bats", "star", "tree", "stab", "rats", "reet", "pong", "peel", "leep", "path", "type", "pyte", "ypte"]
var anagrams = groupAnagrams(listOfWords: listOfWords)
var otherWay = groupAnagramsList("bats", "star", "tree", "stab", "rats", "reet", "pong", "peel", "leep", "path", "type", "pyte", "ypte")
UPDATE FROM Swift Beta 5:
if d[s3] == nil {
d[s3] = [String]()
++j
} else {
++i
}
d[s3]! += [s]
log += ["Add string \(d[s3]!.count) \(s) to array \(s3) \n"]
The check for nil on the Dictionary d is valid! In this context the complicated optional / immutable behavior is gone and the code is much easier to understand.
Note: I had to change
log += "Add string (d[s3]!.count) (s) to array (s3) \n"
To:
log += ["Add string (d[s3]!.count) (s) to array (s3) \n"]
Because you now need to explicitly add an Array of String to an Array of String. You cannot assume that += on a String will append a String to the Array. But it's easy, just wrap the string in '[' and ']'.
I still don't know why I cannot just cast a Character Array to a String!
Maybe a smart thing would be to use Swift's built-in Objective-C APIs to eliminate duplicate values. E.g:
let unique = NSSet(array: swiftArrayOfNonUniqueStrings).allObjects
The problem is, applying this to your character-sorted, duplicates-containing string array will just return the character-sorted version of your original string. (You can obtain such an array by returning alphaList below.)
As for sorting the characters within the string, I believe that iterating through the characters to build the string is efficient enough. You can write this a bit more succinctly
func alphabetizeStrings(list : [String]) -> [String] {
var newlist = [String]()
var alphaList = [String]()
for s in list {
var charArray = [Character](s)
charArray.sort({ (a: Character, b: Character) -> Bool in return String(a) < String(b)})
var sortedString = ""
for c in charArray {
sortedString += c
}
if NSArray(array: alphaList).containsObject(sortedString) == false {
newlist.append(s)
}
alphaList.append(sortedString)
}
return newlist
}
Thus:
let swiftarray = ["foo", "foo", "bar", "bra", "bat"]
let unique = alphabetizeStrings(swiftarray)
// ["foo", "bar", "bat"]

Resources