I can reverse every word in a string functionally without using a loop, but when I try to reverse EVERY OTHER WORD. I run into problems. I can do it with a loop but not functionally. What am I not seeing here?
Functionally (every word):
import UIKit
let input = "This is a sample sentence"
func reverseWords(input: String) -> String {
let parts = input.components(separatedBy: " ")
let reversed = parts.map { String($0.reversed()) }
return reversed.joined(separator: " ")
}
reverseWords(input: input)
With loop (EVERY OTHER WORD):
var sampleSentence = "This is a sample sentence"
func reverseWordsInSentence(sentence: String) -> String {
let allWords = sampleSentence.components(separatedBy:" ")
var newSentence = ""
for index in 0...allWords.count - 1 {
let word = allWords[index]
if newSentence != "" {
newSentence += " "
}
if index % 2 == 1 {
let reverseWord = String(word.reversed())
newSentence += reverseWord
} else {
newSentence += word
}
}
return newSentence
}
reverseWordsInSentence(sentence: sampleSentence)
With a slight modification of your reverseWords you can reverse every other word. Use enumerated() to combine a word with its position, and then use that to reverse odd words:
let input = "one two three four five"
func reverseOddWords(input: String) -> String {
let parts = input.components(separatedBy: " ")
let reversed = parts.enumerated().map { $0 % 2 == 0 ? String($1.reversed()) : $1 }
return reversed.joined(separator: " ")
}
print(reverseOddWords(input: input))
eno two eerht four evif
Or you could pattern your function after Swift's sort and pass the filter closure to the reverseWords function:
let input = "one two three four five"
func reverseWords(_ input: String, using filter: ((Int) -> Bool) = { _ in true }) -> String {
let parts = input.components(separatedBy: " ")
let reversed = parts.enumerated().map { filter($0) ? String($1.reversed()) : $1 }
return reversed.joined(separator: " ")
}
// default behavior is to reverse all words
print(reverseWords("one two three four five"))
eno owt eerht ruof evif
print(reverseWords("one two three four five", using: { $0 % 2 == 1 }))
one owt three ruof five
print(reverseWords("one two three four five", using: { [0, 3, 4].contains($0) }))
eno two three ruof evif
let everyThirdWord = { $0 % 3 == 0 }
print(reverseWords("one two three four five", using: everyThirdWord))
eno two three ruof five
Use stride() to generate a sequence of indexes of every other word.
Then use forEach() to select each index in the stride array and use it to mutate the word at that index to reverse it.
import UIKit
let string = "Now is the time for all good programmers to babble incoherently"
var words = string.components(separatedBy: " ")
stride(from: 0, to: words.count, by: 2)
.forEach { words[$0] = String(words[$0].reversed()) }
let newString = words.joined(separator: " ")
print(newString)
The output string is:
"woN is eht time rof all doog programmers ot babble yltnerehocni"
Related
//MARK: - Reverse every other word
var sampleSentence = "Hey my name is Chris, what is yours?"
func reverseWordsInSentence(sentence: String) -> String {
let allWords = sentence.components(separatedBy: " ")
var newSentence = ""
for word in allWords {
if newSentence != "" {
newSentence += " "
}
let reverseWord = String(word//.characters.reverse()) //Problem
newSentence += word
}
return newSentence
}
I made a tutorial by "Lets Build That App" on YouTube but his video is from 2016. He wrote it like you can see above but the characters function of the String doesn't exist!
There is a few issues regarding the Swift version of that tutorial. First the character property from string has been removed. Second collection reverse method has been renamed to reversed. Third that logic is flawed because it would reverse the punctuations after the words. You can use string enumerateSubstrings in range, use byWords option, to get the range of the words in the sentence and reverse each word as follow:
func reverseWordsInSentence(sentence: String) -> String {
var sentence = sentence
sentence.enumerateSubstrings(in: sentence.startIndex..., options: .byWords) { _, range, _, _ in
sentence.replaceSubrange(range, with: sentence[range].reversed())
}
return sentence
}
var sampleSentence = "Hey my name is Chris, what is yours?"
reverseWordsInSentence(sentence: sampleSentence) // "yeH ym eman si sirhC, tahw si sruoy?"
try this for reverse:
let reverseWord = String(word.reversed())
newSentence += reverseWord
instead of:
let reverseWord = String(word)//.characters.reverse()) //Problem
newSentence += word
output:
I have: str1 = "this is the first day in my work" and str2 = "this is a great day" and I want to return the matched words as string from the previous two strings str1 & str2 and then store them in a new variable
The new variable str3: String should have this text "this is day"
I have found this in my searching but i need to return a string with matches ..
func isAnagram() -> Bool {
let str1 = "this is the first day in my work"
let str2 = "this is a great day"
func countedSet(string: String) -> NSCountedSet {
let array = string.map { (character) -> String in
return String(character)
}
return NSCountedSet(array: array)
}
return countedSet(string: str1).isEqual(countedSet(string: str2))
}
If order in the final string doesn't matter, this would be an easy solution:
let str1 = "this is the first day in my work"
let str2 = "this is a great day"
let words1 = Set(str1.split(separator: " "))
let words2 = Set(str2.split(separator: " "))
let str3 = words1.intersection(words2).reduce("") { $0 + $1 + " "}
If order matters:
...
let str3 = words1.intersection(words2).sorted {
words1.index(of: $0)! < words1.index(of: $1)!
}.reduce("") { $0 + $1 + " "}
You can use String method enumerateSubstrings(in: Range) using .byWords options to get the words in your string sentences and use filter to remove the words no contained in the second string:
extension StringProtocol where Index == String.Index {
var words: [String] {
var result: [String] = []
enumerateSubstrings(in: startIndex..., options: .byWords) { (substring, _, _, _) in
result.append(substring!)
}
return result
}
func matchingWords(in string: String) -> [String] {
return string.words.filter(words.contains)
}
}
Note that this preserves the order of occurrences and doesn't fail if there is punctuation in the string:
let str1 = "this is the first day in my work"
let str2 = "this is a great day"
let matchingWords = str1.matchingWords(in: str2) // ["this", "is", "day"]
let str3 = matchingWords.joined(separator: " ") // "this is day"
I write Swift application that parse log file.
log file string:
substr1 substr2 "substr 3" substr4
I need to get array: [substr1, substr2, substr 3, substr4]
But if I use something like:
print(stringLine.components(separatedBy: " "))
I got: [substr1, substr2, "substr, 3", substr4].
How to receive array: [substr1, substr2, substr 3, substr4]?
One of the possible solutions is to use map:
let testSting = "substr1 substr2 \"substr3\" substr4"
let mappedString = testString.components(separatedBy: " ").map({$0.replacingOccurrences(of: "\"", with: "")})
print(mappedString) //["substr1", "substr2", "substr3", "substr4"]
This case of the issue is required to use regular expression but this example is provided. So to solve problem in you're case it is possible to go in this way:
var testStingArray = testSting.replacingOccurrences(of: "\"", with: "").components(separatedBy: " ")
var arr = [String]()
var step = 0
while step < testStingArray.count {
var current = testStingArray[step]
var next = step + 1
if next < testStingArray.count {
if testStingArray[next].characters.count == 1 {
current += " " + testStingArray[next]
testStingArray.remove(at: next)
}
}
arr.append(current)
step += 1
}
print(arr)//["substr1", "substr2", "substr 3", "substr4"]
You'd better work with regular expression:
let pattern = "([^\\s\"]+|\"[^\"]+\")"
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let line = "substr1 substr2 \"substr 3\" substr4"
let arr = regex.matches(in: line, options: [], range: NSRange(0..<line.utf16.count))
.map{(line as NSString).substring(with: $0.rangeAt(1)).trimmingCharacters(in: CharacterSet(charactersIn: "\""))}
print(arr) //->["substr1", "substr2", "substr 3", "substr4"]
Alternatively you could split the string based on a CharacterSet and then filter out the empty occurrences:
let stringLine = "substr1 substr2 \"substr3\" substr4"
let array = stringLine.components(separatedBy: CharacterSet(charactersIn: "\" ")).filter { !$0.isEmpty }
print (array)
Output: ["substr1", "substr2", "substr3", "substr4"]
But this will not work correctly if there is a " somewhere in one of the 'substrings', then that specific substring will also be split.
Or, simply iterate over the characters and maintain state about the quoted parts:
//: Playground - noun: a place where people can play
import UIKit
extension String {
func parse() -> [String] {
let delimiter = Character(" ")
let quote = Character("\"")
var tokens = [String]()
var pending = ""
var isQuoted = false
for character in self.characters {
if character == quote {
isQuoted = !isQuoted
}
else if character == delimiter && !isQuoted {
tokens.append(pending)
pending = ""
}
else {
pending.append(character)
}
}
// Add final token
if !pending.isEmpty {
tokens.append(pending)
}
return tokens
}
}
print ("substr1 substr2 \"substr 3\" substr4".parse()) // ["substr1", "substr2", "substr 3", "substr4"]
print ("\"substr 1\" substr2 \"substr 3\" substr4".parse()) // ["substr 1", "substr2", "substr 3", "substr4"]
print ("a b c d".parse()) // ["a", "b", "c", "d"]
Note: this code doesn't take into account that double quotes "" might be used to escape a single quote. But I don't know if that's a possibility in your case.
https://tburette.github.io/blog/2014/05/25/so-you-want-to-write-your-own-CSV-code/
a string such as ! !! yuahl! ! , I want delete ! and , when I code like this
for index in InputName.characters.indices {
if String(InputName[index]) == "" || InputName.substringToIndex(index) == "!" {
InputName.removeAtIndex(index)
}
}
have this error " fatal error: subscript: subRange extends past String end ", how should I do? THX :D
Swift 5+
let myString = "aaaaaaaabbbb"
let replaced = myString.replacingOccurrences(of: "bbbb", with: "") // "aaaaaaaa"
If you need to remove characters only on both ends, you can use stringByTrimmingCharactersInSet(_:)
let delCharSet = NSCharacterSet(charactersInString: "! ")
let s1 = "! aString! !"
let s1Del = s1.stringByTrimmingCharactersInSet(delCharSet)
print(s1Del) //->aString
let s2 = "! anotherString !! aString! !"
let s2Del = s2.stringByTrimmingCharactersInSet(delCharSet)
print(s2Del) //->anotherString !! aString
If you need to remove characters also in the middle, "reconstruct from the filtered output" would be a little bit more efficient than repeating single character removal.
var tempUSView = String.UnicodeScalarView()
tempUSView.appendContentsOf(s2.unicodeScalars.lazy.filter{!delCharSet.longCharacterIsMember($0.value)})
let s2DelAll = String(tempUSView)
print(s2DelAll) //->anotherStringaString
If you don't mind generating many intermediate Strings and Arrays, this single liner can generate the expected output:
let s2DelAll2 = s2.componentsSeparatedByCharactersInSet(delCharSet).joinWithSeparator("")
print(s2DelAll2) //->anotherStringaString
I find that the filter method is a good way to go for this sort of thing:
let unfiltered = "! !! yuahl! !"
// Array of Characters to remove
let removal: [Character] = ["!"," "]
// turn the string into an Array
let unfilteredCharacters = unfiltered.characters
// return an Array without the removal Characters
let filteredCharacters = unfilteredCharacters.filter { !removal.contains($0) }
// build a String with the filtered Array
let filtered = String(filteredCharacters)
print(filtered) // => "yeah"
// combined to a single line
print(String(unfiltered.characters.filter { !removal.contains($0) })) // => "yuahl"
Swift 3
In Swift 3, the syntax is a bit nicer. As a result of the Great Swiftification of the old APIs, the factory method is now called trimmingCharacters(in:). Also, you can construct the CharacterSet as a Set of single-character Strings:
let string = "! !! yuahl! !"
string.trimmingCharacters(in: [" ", "!"]) // "yuahl"
If you have characters in the middle of the string you would like to remove as well, you can use components(separatedBy:).joined():
let string = "! !! yu !ahl! !"
string.components(separatedBy: ["!", " "]).joined() // "yuahl"
H/T #OOPer for the Swift 2 version
func trimLast(character chars: Set<Character>) -> String {
let str: String = String(self.reversed())
guard let index = str.index(where: {!chars.contains($0)}) else {
return self
}
return String((str[index..<str.endIndex]).reversed())
}
Note:
By adding this function in String extension, you can delete the specific character of string at last.
for index in InputName.characters.indices.reversed() {
if String(InputName[index]) == "" || InputName.substringToIndex(index) == "!" {
InputName.removeAtIndex(index)
}
}
Also you can add such very helpful extension :
import Foundation
extension String{
func exclude(find:String) -> String {
return stringByReplacingOccurrencesOfString(find, withString: "", options: .CaseInsensitiveSearch, range: nil)
}
func replaceAll(find:String, with:String) -> String {
return stringByReplacingOccurrencesOfString(find, withString: with, options: .CaseInsensitiveSearch, range: nil)
}
}
you can use this:
for example if you want to remove "%" the percent from 10%
if let i = text.firstIndex(of: "%") {
text.remove(at: i) //10
}
How would I split a string to include the separators?
Lets say I had a string such as...
let myString = "apple banana orange grapes"
If I used
let separatedString = myString.componentsSeparatedByString(" ")
my resulting array would be
["apple","banana","orange","grapes"]
How would I achieve a result of
["apple ","banana ","orange ","grapes"]
array.map lets you process the resulting array an add the space back in.
let separatedString = myString
.componentsSeparatedByString(" ")
.map { "\($0) " }
That last line iterates over all strings in the split up array and puts them in $0, and returns a new string with the space added back in which gets used as the replacement for the original string.
Alternative using regular expression:
let myString = "apple banana orange grapes"
let pattern = "\\w+\\s?"
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let matches = regex.matchesInString(myString, options:[], range: NSMakeRange(0, myString.characters.count))
.map { (myString as NSString).substringWithRange($0.range)}
print(matches) // -> ["apple ", "banana ", "orange ", "grapes"]
Solution
Since you updated your question, it looks now you no longer want a new space on the last word.
So here's my updated code
let text = "apple banana orange grapes"
let chunks: [String] = text
.componentsSeparatedByString(" ")
.reverse()
.enumerate()
.map { $0.element + ( $0.index == 0 ? "" : " ") }
.reverse()
print(chunks) // ["apple ", "banana ", "orange ", "grapes"]
Multiple separators
Thank to #vadian for the suggestion
let text = "apple banana\norange grapes"
let chunks: [String] = text
.componentsSeparatedByCharactersInSet(.whitespaceAndNewlineCharacterSet())
.reverse()
.enumerate()
.map { $0.element + ( $0.index == 0 ? "" : " ") }
.reverse()
print(chunks) // ["apple ", "banana ", "orange ", "grapes"]