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

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

Related

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

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

How to split a sequential integer array, if any sequence is missing

Supposre I have an input array of integers. I want to split this array in multiple array based on the missing integer and append it in a new Array. I think split can be used here but not sure how to do it. I want arrayFinal only.
myArray = [0,1,2,4,7,8]
Desired Output
arrayOne = [0,1,2]
arrayTwo = [4]
arrayThree = [7,8]
arrayFinal = [[0,1,2], [4], [7,8]]
That's an algorithm you're asking for so there are a dozen different ways to do it. Since you are going to have to walk through the array's contents to find the missing integers, I would just create an array and append the numbers to it as you go, then create a new array whenever you hit a gap.
You'll probably have to adjust this for any special cases you might have. "Will this always start at 0 and move in a positive direction?" etc.
Try this out:
func splitByMissingInteger(array: [Int]) -> [[Int]]? {
var arrayFinal :[[Int]] = [ [Int]() ]
var i = 0
for num in array{
if arrayFinal[i].isEmpty || (arrayFinal[i].last == nil){
arrayFinal[i].append(num)
} else if num == (arrayFinal[i].last! + 1){
arrayFinal[i].append(num)
} else {
i += 1
arrayFinal.append([Int]())
arrayFinal[i].append(num)
}
}
return arrayFinal
}
You can just sort your array and iterate them in order. Check if the element minus one is equal to the last element of your 2D array, if true append otherwise append a new array with the element and increase the index of the subarrays:
extension Collection where Element == Int {
func grouped() -> [[Element]] {
let elements = Set(self).sorted()
guard let first = elements.first else { return [] }
var result = [[first]]
var i = 0
for element in elements.dropFirst() {
if element-1 == result[i].last! {
result[i].append(element)
} else {
result.append([element])
i += 1
}
}
return result
}
}
let myArray = [0,1,2,4,7,8]
let grouped = myArray.grouped() // [[0, 1, 2], [4], [7, 8]]

Filter array with first and last letter optimization

I have a an array of strings and I want to get all the occurrences that start with a certain character and end with a certain character. Here is how I have done it today:
var arr = ["Aa", "Bb", "Ab", "Abc", "Dd"]
var newArr = [String]()
for str in arr {
if str.characters.first == "A" && str.characters.last == "c" {
newArr.append(str)
}
}
arr = newArr
If feels really messy, but it works. Can I optimize this using filter or similar?
You can use filter with hasPrefix and hasSuffix. Try this:
arr = arr.filter({ $0.hasPrefix("A") && $0.hasSuffix("c") }) // Abc

Check each value inside a nested array of a dictionary Swift

I know this question might seem obvious to some, but i can't get around a proper solution.
I have a dictionary
someDict = [String : [Int]]
Also i have an Integer variable and a string
var someNumber = Int()
var someString = String()
My goal is to compare if someString = someDict key and if yes - compare every Int value inside it's nested array to someNumber (check whether it's smaller or bigger and give some output).
Thank you!
First you look for the key in the dictionary that matches the one you're after — so we loop through all the keys.
Then, once we find a match, we loop through all the contents of that keys value. In this case, its our array of numbers.
let someDict = ["matchingString" : [6,7,5,4]]
var someNumber = 5
var someString = "matchingString"
for (someDictKey, numbers) in someDict {
if someDictKey == someString {
// Key Found
for number in numbers {
if number == someNumber {
// Number Found
} else {
// no matching number found
}
}
} else {
// No matching key found
}
}
Try it in a playground.
You can make use of optional chaining, without the need to explicitly loop over the dictionary entries.
var someDict = [String : [Int]]()
someDict["SomeString"] = [Int](1...5)
let someString = "SomeString"
let someNumber = 2
if someDict[someString]?.contains(someNumber) ?? false {
print("Dict value array for key '\(someString)' contains value \(someNumber).")
}
else {
print("Dict value array for key '\(someString)' does not contain value \(someNumber).")
}
/* Prints: Dict value array for key 'SomeString' contains value 2. */
If we're dealing with a huge dictionary, looping over all dictionary entries kind of defeats the purpose of dictionaries O(1) hash value lookup (i.e., just attempting to access the key directly).
Try this:
var someDict = [String : [Int]]()
someDict["a"] = [1, 2, 3]
someDict["b"] = [4, 5, 6]
var str = "a"
var number = 3
for (key, value) in someDict {
if key == str {
for num in value {
if num == number {
print("matched")
} else {
print("Oops")
}
}
} else {
print("nope")
}
}
You can simply ask the dictionary for the key you're interested into, and enumerate within the corresponding object:
// this helps us get rid of having to unwrap someDict["someString"] first
for intVal in someDict["someString"] ?? [Int]() {
print(intVal < someNumber ? "smaller" : "not smaller")
}
Or, if your interested on finding the numbers smaller that someNumber, you can use the filtering support:
let smallerNumbers = (someDict[someString] ?? [Int]()).filter({$0 < someNumber})

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