I have an array of Strings containing user inputted values. I have a string containing several words (the number of words in the string varies). I want to increment an Int every time one of the words in the string matches a word in the array.
I'm using this method:
var searchWordsArr: [String] = [] \\filled by user input
var message: String = "" \\random number of words
var numRelevantWords = 0
var i = 0
while i < self.searchWordsArr.count {
i+=1
if message.contains(self.searchWordsArr[i-1]) {
numRelevantWords += 1
}
}
In my first example, the string contained 25 words and the array contained 3 words. The 3 words came up a total of 12 times in the string. Using the above method, the value of numRelevantWords was 2.
I would use regex. Some day soon, Swift will have native regular expressions. But until it does, you have to resort to Foundation:
let words = ["the", "cat", "sat"]
let input = "The cat sat on the mat and the mat sat on the catastrophe"
var result = 0
let pattern = "\\b(" + words.joined(separator:"|") + ")\\b"
do {
let regex = try NSRegularExpression(pattern: pattern, options: .caseInsensitive)
let match = regex.matches(in: input, options: [], range: NSRange(location: 0, length: input.utf16.count))
result += match.count // 7, that's your answer
} catch {
print("illegal regex")
}
Add them to a NSCountedSet:
let searchWords = NSCountedSet()
searchWords.add("Bob")
searchWords.add("John")
searchWords.add("Bob")
print(searchWords.count(for: "Bob")) // 2
"pure" Swift (no Foundation)
let message = "aa bb aaaa ab ac aa aa"
let words = message.characters.split(separator: " ").map(String.init)
let search:Set = ["aa", "bb", "aa"]
let found = words.reduce(0) { (i, word) -> Int in
var i = i
i += search.contains(word) ? 1 : 0
return i
}
print(found, "word(s) from the message are in the search set")
prints
4 word(s) from the the message are in the search set
UPDATE (see discussion)
using Set
var search: Set<String> = [] // an empty set with Element == String
search.insert("aa") // insert word to set
using Array
var search: Array<String> = [] // an empty array
search.append("aa") // append word to array
maybe you are looking for
let message = "the cat in the hat"
let words = message.characters.split(separator: " ").map(String.init)
let search:Set = ["aa", "bb", "pppp", "the"]
let found = search.reduce(0) { (i, word) -> Int in
var i = i
i += words.contains(word) ? 1 : 0
return i
}
print(found, "word(s) from the search set found in the message")
prints
1 word(s) from the search set found in the message
if you would like to produce the same as with accepted answer
let words = ["the", "cat", "sat"]
let input = "The cat sat on the mat and the mat sat on the catastrophe"
let inputWords = input.lowercased().characters.split(separator: " ").map(String.init)
let found = inputWords.reduce(0) { (i, word) -> Int in
var i = i
i += words.contains(word.lowercased()) ? 1 : 0
return i
}
print(found, "word(s) from the search set found in the message")
prints
7 word(s) from the search set found in the message
Related
I have an array that describes a list of auto parts (Swift/IOS,array already in such structure arrived from other source.):
let parts = [
"Wheel = 230$",
"Door = 200$",
"Wheel = 300$",
"Seat = 150$",
"Seat = 150$",
"Rugs = 100$"]
I need to calculate the sum of the prices of the auto parts for each type of part. Here's the result I'm looking for:
let expectedResult = [
"wheel 530$",
"Door 200$",
"Seat 300$",
"Rugs 100$"
]
I don’t understand how to do it.
Here is a solution where I loop through the array and split each element on "=" and then sum the values together using a dictionary. Once that is done the dictionary is converted back to an array
let parts = [
"Wheel = 230$",
"Door = 200$",
"Wheel = 300$",
"Seat = 150$",
"Seat = 150$",
"Rugs = 100$"]
var values = [String: Int]() // Used to calculate totals per part
parts.forEach { string in
let split = string.split(separator: "=")
// Make sure the split worked as expected and that we have an integer value after the =
guard split.count == 2,
let value = Int(String(split[1]).trimmingCharacters(in: .whitespaces).dropLast()) else { return }
let key = String(split[0]).trimmingCharacters(in: .whitespaces)
// Sum values over the part name
values[key, default: 0] += value
}
// Convert back to an array of strings
let output = values.map { "\($0.key) \($0.value)$"}
print(output)
this is my first time with swift and I have some troubles with an school project
I have the following:
var tokens = ["if", "for", "(", ")", "while", "var"]
var test = "if, identifier, for, (, ), while, var1, 3var, ResultVariable, i dentifier";
var lineas = [String]()
I converted the "test" into "lineas" array:
lineas = data.components(separatedBy: ", ")
Then I created the following regex pattern to find ids in the "test" string (id is any variable name)
//id: any word that begins with lowercase and can have 0 or more alphanumerics characters
let id = "[a-z][\\w]*"
let regexId = try! NSRegularExpression(pattern: id, options: [])
The result of the regex is saved in a String and then into in array
let idString = regexId.stringByReplacingMatches(in: test, options: [], range: NSRange(location: 0, length: test.count), withTemplate: "($0)")
let idArray = idString.components(separatedBy: "\n")
Then I try to compare the "idArray" with the "tokens" array to obtain only the ids
let idResults = idArray.filter({!tokens.contains($0)})
print("\nID found\n")
for ids in idResults{
print(ids)
}
But the output I got is:
ID found:
["(if)", "(for)", "(identifier)", "(for)", "(while)", "(var1)", "3(var)", "R(esultVariable)", "(i) (dentifier)"]
When the output I need is:
ID found:
["identifier, var1"]
And this is because "3var, ResultVariable, i dentifier" are not identifiers because they start with numer, uppercase or have an space
Here is a solution not based on regex that instead uses the filter function in different ways
var tokens = ["if", "for", "(", ")", "while", "var"]
var test = "if, identifier, for, (, ), while, var1, 3var, ResultVariable, i dentifier";
var lineas = test.components(separatedBy: ", ")
let identifiers = lineas
.filter {!tokens.contains($0)} // Not a token
.filter { // Starts with lowercase letter
guard let first = $0.first else { return false }
return first.isLetter && first.isLowercase
}
.filter { $0.rangeOfCharacter(from: .whitespaces) == nil } // No whitespace
Of course the above can be merged in to one call of filter but I left it as 3 separate calls for clarity.
I am trying to change my string value in an array after shuffling another array, how am i to do this?
Example:
var array1 = [1,2,3,4,5,6]
var stringarray = ["\\(array1[0]) = 1")
array1.shuffle()
print(stringarray)
How am i to change the original stringarray value to the new shuffled value?
Thank you
The task:
#IBAction func nextQuestion(_ sender: Any) {
if levelSelected == 1 {
questionLabel.text = standardRules.randomElement()
}
players.shuffle()
print(players)
standardRules has a string value that takes the value of players[0]
Essentially what i am trying to do is this:
I am trying to grab 2 random values that are not the same in a string like this
var players = ["Jack, John, Michael, Peter"]
var playersArray = ["\(players.randomElement) and \(players.randomElement) has to battle")
How am i to do this, so it grabs 2 different values?
You could make it very easy by this code :
var players = ["Jack", "John", "Michael", "Peter"]
// get the first random element
var random1 = players.randomElement()
//delete that element so you can't duplicate it
players = players.filter{$0 != random1}
//get your second radom element
var random2 = players.randomElement()
//add your element again
players.append(random1!)
Looks like you're missing some core concepts of the Swift language. When you create your stringarray it makes a call of array1.description and stores the result into an array. From that point any modifications of the original array will not change anything in stringarray.
So if you want to pick two different players from an array you need to do something like that:
let index1 = Int.random(in: (0 ..< players.count))
var index2: Int
repeat {
index2 = Int.random(in: (0 ..< players.count))
} while index1 == index2
let matchText = "\(players[index1]) and \(players[index2] has to battle)"
I would try replacing:
var stringarray = ["\\(array1[0]) = 1")
array1.shuffle()
with:
var stringarray = array1.shuffle()
I got this problem. I have a
val line:String = "PE018201804527901"
that matches with this
regex : (.{2})(.{4})(.{9})(.{2})
I need to extract each group from the regex to an Array.
The result would be:
Array["PE", "0182","018045279","01"]
I try to do this regex:
val regex = """(.{2})(.{4})(.{9})(.{2})""".r
val x= regex.findAllIn(line).toArray
but it doesn't work!
regex.findAllIn(line).subgroups.toArray
Note that findAllIn does not automatically anchor the regex pattern, and will find a match inside a much longer string. If you need to only allow matches inside 17 char strings, you can use a match block like this:
val line = "PE018201804527901"
val regex = """(.{2})(.{4})(.{9})(.{2})""".r
val results = line match {
case regex(g1, g2, g3, g4) => Array(g1, g2, g3, g4)
case _ => Array[String]()
}
// Demo printing
results.foreach { m =>
println(m)
}
// PE
// 0182
// 018045279
// 01
See a Scala demo.
It also handles no match scenario well initializing an empty string array.
If you need to get all matches and all groups, then you will need to grab the groups into a list and then add the list to a list buffer (scala.collection.mutable.ListBuffer):
val line = "PE018201804527901%E018201804527901"
val regex = """(.{2})(.{4})(.{9})(.{2})""".r
val results = ListBuffer[List[String]]()
val mi = regex.findAllIn(line)
while (mi.hasNext) {
val d = mi.next
results += List(mi.group(1), mi.group(2), mi.group(3), mi.group(4))
}
// Demo printing
results.foreach { m =>
println("------")
println(m)
m.foreach { l => println(l) }
}
Results:
------
List(PE, 0182, 018045279, 01)
PE
0182
018045279
01
------
List(%E, 0182, 018045279, 01)
%E
0182
018045279
01
See this Scala demo
Your solution #sheunis was very helpful, finally I resolved it with this method:
def extractFromRegex (regex: Regex, line:String): Array[String] = {
val list = ListBuffer[String]()
for(m <- regex.findAllIn(line).matchData;
e <- m.subgroups)
list+=e
list.toArray
}
Because your solution with this code:
val line:String = """PE0182"""
val regex ="""(.{2})(.{4})""".r
val t = regex.findAllIn(line).subgroups.toArray
Shows the next exception:
Exception in thread "main" java.lang.IllegalStateException: No match available
at java.util.regex.Matcher.start(Matcher.java:372)
at scala.util.matching.Regex$MatchIterator.start(Regex.scala:696)
at scala.util.matching.Regex$MatchData$class.group(Regex.scala:549)
at scala.util.matching.Regex$MatchIterator.group(Regex.scala:671)
at scala.util.matching.Regex$MatchData$$anonfun$subgroups$1.apply(Regex.scala:553)
at scala.util.matching.Regex$MatchData$$anonfun$subgroups$1.apply(Regex.scala:553)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.immutable.List.foreach(List.scala:318)
at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
at scala.collection.AbstractTraversable.map(Traversable.scala:105)
at scala.util.matching.Regex$MatchData$class.subgroups(Regex.scala:553)
at scala.util.matching.Regex$MatchIterator.subgroups(Regex.scala:671)
I'm trying to replace the just first character of a string. Using this answer, I'm able to replace all current characters with the one I want, but what I really need is to only replace the character if it's at the start of a word.
For example, replace c with t:
Original String: Jack is cool
New Sting: Jack is tool
My Current Code:
let aString: String = "jack is cool"
let toArray: Array = aString.componentsSeparatedByString(" ")
let newString: String = aString.stringByReplacingOccurrencesOfString("c", withString: "t", options: NSStringCompareOptions.LiteralSearch, range: nil)
This would print out as:
"jatk is tool"
I assume I have to do something with the range attribute in the newString.
I would use regex. \\b called word boundary which matches between a word character and a non-word character. You may also use negative lookbehind instead of \\b like (?<!\\S)c
var regex:NSRegularExpression = NSRegularExpression(pattern: "\\bc", options: NSRegularExpressionOptions.CaseInsensitive, error: &ierror)!
var modString = regex.stringByReplacingMatchesInString(myString, options: nil, range: NSMakeRange(0, stringlength), withTemplate: "t")
var aString = 'jack is cool';
var newString = aString.replace('c','t');
var a
var oldString = 'Jack is cool';
var newString = oldString.split(' ');
var x = newString[newString.indexOf('cool')].toString();
x = x.replace('c','t');
newString.splice(newString.indexOf('cool'),1,x);
newString = newString.join(' ').toString();
alert(newString);
You can define your range to start and end at the startIndex to make sure it replaces only occurences at the first character of the string:
let coolString = "Cool"
let newString = coolString.stringByReplacingOccurrencesOfString("C", withString: "T", options: [], range: coolString.startIndex...coolString.startIndex)
You can use String method replace() to replace only the first character of a String
var str = "Cool"
str.replaceRange(str.startIndex...str.startIndex, with: "T")
print(str) // "Tool"
To apply it only to the last word:
var str = "Jack is Cool"
var words = str.componentsSeparatedByString(" ")
if !words.isEmpty {
if !words[words.count-1].isEmpty {
words[words.count-1].replaceRange(str.startIndex...str.startIndex, with: "T")
print( words.last!) // "Tool\n"
}
}
let sentence = words.joinWithSeparator(" ") // "Jack is Tool"