Reverse every other word in string, keep punctuation Swift - string

So I got stuck on a coding challenge that I almost knew the answer too. And I think I have to use the subString call in Swift 4 to get it 100%. I want to reverse every OTHER word in a string, but ignore or keep the punctuation in its original place( index ).
var sample = "lets start. And not worry about proper sentences."
func reverseString(inputString: String) -> String {
let oldSentence = sample.components(separatedBy: " ")
var newSentence = ""
for index in 0...oldSentence.count - 1 {
let word = oldSentence[index]
if newSentence != "" {
newSentence += " "
}
if index % 2 == 1 {
let reverseWord = String(word.reversed())
newSentence += reverseWord
} else {
newSentence += word
}
}
return newSentence
}
reverseString(inputString: sample)
And this would be the expected output.
"lets trats. And ton worry tuoba proper secnetnes."
Notice the punctuation is not reversed.

You shouldn't use components(separatedBy: ) to split a string in words. See this article for the reason. Use enumerateSubstrings and pass in the appropriate option:
func reverseString(inputString: String) -> String {
var index = 1
var newSentence = inputString
inputString.enumerateSubstrings(in: inputString.startIndex..., options: .byWords) { substr, range, _, stop in
guard let substr = substr else { return }
if index % 2 == 0 {
newSentence = newSentence.replacingCharacters(in: range, with: String(substr.reversed()))
}
index += 1
}
return newSentence
}
print(reverseString(inputString: "lets start. And not worry about proper sentences."))
// lets trats. And ton worry tuoba proper secnetnes.
print(reverseString(inputString: "I think, therefore I'm"))
// I kniht, therefore m'I

Related

How to convert this string 20346017621 in to 20-34601762-1

How we can set up a function on node
After first two number and last number, we want to add - hyphen
able to get a string
like this:xxzzzzzzzzx
and convert in to this:xx-zzzzzzzz-x
example of what we need
function tranformer (xxzzzzzzzzx){
NOT SURE HOW TO SOLVE THIS
return xx-zzzzzzzz-x
}
Thanks we really will appreciate this!
Not idea how to mange this task.
function tranformer(st) {
let newStr = ""
for (let i = 0; i < st.length; i++) {
if (i === 2 || i === st.length - 1) {
newStr += "-"
}
newStr += st[i]
}
return newStr
}
Using Slice
let first = st.slice(0, 2)
let middle = st.slice(2, -1)
let last = st.slice(-1)
newStr = first + "-" + middle + "-" + last
console.log(newStr)
First I would decompose the string into an array, and then use splice command to insert a - char at the specified position:
let str = "xxzzzzzzzzx";
str = str.split('');
str.splice(2, 0, '-'); // insert - at pos 2
str.splice(str.length - 1, 0, '-'); // insert - at pos -1
console.log(str.join('')) //-> xx-zzzzzzzz-x

Swift fast low level String lastIndexOf

I need an implementation of lastIndexOf that is as fast as possible.
I am finding that the String advance function is extremely slow.
I tried using the c function strrchr, and tried copying the string to NSData and using pointers but I can't get the syntax right.
My string will always have 1 byte characters and the string i'm searching for "|" is always 1 byte also.
Any implementation using advance will be too slow but here is the fastest example I could find:
func indexOf(target: String, startIndex: Int) -> Int
{
var startRange = advance(self.startIndex, startIndex)
var range = self.rangeOfString(target, options: NSStringCompareOptions.LiteralSearch, range: Range<String.Index>(start: startRange, end: self.endIndex))
if let range = range {
return distance(self.startIndex, range.startIndex)
} else {
return -1
}
}
func lastIndexOf(target: String) -> Int
{
var index = -1
var stepIndex = self.indexOf(target)
while stepIndex > -1
{
index = stepIndex
if stepIndex + target.length < self.length
{
stepIndex = indexOf(target, startIndex: stepIndex + target.length)
}
else
{
stepIndex = -1
}
}
return index
}
This is an example of the string I need to parse.
var str:String = "4|0|66|5|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|6|0|3259744|6352141|46|14|1|0|7|7|3259744|6352141|4|1|0|8|8|3259744|6352141|4|0|22|9|0|3259744|6352141|2|3|Room1|2|72|86330534|1|0|10|9|3259744|6352141|4|1|0|11|10|3259744|6352141|4|1|0|12|11|3259744|6352141|4|1|0|13|12|3259744|6352141|4|0|4|14|0|3259744|6352141|46|24|0|5|15|0|3259744|6352141|46|654|0|66|0|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|16|0|3259744|6352141|46|4sageReceived:4|0|66|5|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|6|0|3259744|6352141|46|14|1|0|7|7|3259744|6352141|4|1|0|8|8|3259744|6352141|4|0|22|9|0|3259744|6352141|2|3|Room1|2|72|86330534|1|0|10|9|3259744|6352141|4|1|0|11|10|3259744|6352141|4|1|0|12|11|3259744|6352141|4|1|0|13|12|3259744|6352141|4|0|4|14|0|3259744|6352141|46|24|0|5|15|0|3259744|6352141|46|654|0|66|0|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|16|0|3259744|6352141|46|4352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|6|0|3259744|6352141|46|14|1|0|7|7|3259744|6352141|4|1|0|8|8|3259744|6352141|4|0|22|9|0|3259744|6352141|2|3|Room1|2|72|86330534|1|0|10|9|3259744|6352141|4|1|0|11|10|3259744|6352141|4|1|0|12|11|3259744|6352141|4|1|0|13|12|3259744|6352141|4|0|4|14|0|3259744|6352141|46|24|0|5|15|0|3259744|6352141|46|654|0|66|0|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|16|0|3259744|6352141|46|4TCPListener.onReceived: 4|0|66|5|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|6|0|3259744|6352141|46|14|1|0|7|7|3259744|6352141|4|1|0|8|8|3259744|6352141|4|0|22|9|0|3259744|6352141|2|3|Room1|2|72|86330534|1|0|10|9|3259744|6352141|4|1|0|11|10|3259744|6352141|4|1|0|12|11|3259744|6352141|4|1|0|13|12|3259744|6352141|4|0|4|14|0|3259744|6352141|46|24|0|5|15|0|3259744|6352141|46|654|0|66|0|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.2212834|0|4|16|0|3259744|6352141|46|4preParse
4|0|66|5|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|Mc02|efland,nc|36.027992|-79.221283"
Here is a Swift 2.0 Answer
func lastIndexOf(s: String) -> Int? {
if let r: Range<Index> = self.rangeOfString(s, options: .BackwardsSearch) {
return self.startIndex.distanceTo(r.startIndex)
}
return Optional<Int>()
}
Tests
func testStringLastIndexOf() {
let lastIndex = "0|2|45|7|9".lastIndexOf("|")
XCTAssertEqual(lastIndex, 8)
}
func testStringLastIndexOfNotFound() {
let lastIndex = "0123456789".lastIndexOf("|")
XCTAssertEqual(lastIndex, nil);
}
You can use strrchr in Swift
import Darwin
let str = "4|0|66|5|0|3259744|6352141|1|3259744"
func stringLastIndexOf(src:String, target:UnicodeScalar) -> Int? {
let c = Int32(bitPattern: target.value)
return src.withCString { s -> Int? in
let pos = strrchr(s, c)
return pos != nil ? pos - s : nil
}
}
stringLastIndexOf(str, "|") // -> {Some 28}
stringLastIndexOf(str, ",") // -> nil
You can use Objective C files in a Swift project; in these you can use plain C code and make a function which uses strrchr. Then you can call this from Swift.
If you do this in order to get all substring delimited by "|", you might test this approach:
import Foundation
let s = "4|0|66|5|0|3259744|6352141|1|3259744|WSMxt208L54yZ5irtHC3|..."
let a = s.componentsSeparatedByString("|")
The built in functions are sometimes very fast and you may be getting the required performance even by using String.
If you really need to get only the position of the last "|", you could work with utf16 representation, where advancing over the characters should be faster.
I think this should work:
let utf16String = s.utf16
var i = s.utf16Count - 1
while i >= 0 {
if utf16String[i] == 124 {
break
}
i--
}
println(i)
If the characters are guaranteed as single byte, the data is huge and performance is critical then it may be worth converting to an array of bytes (UInt8) and perform the operations directly on them. You can then convert the part that you need back to a String.
Also note that Optimised builds may be much faster than Debug builds so you should do any performance testing with the optimiser on. It may also be worth checking that the optimised versions are too slow at the moment.

Selecting a tuple index using a variable in Swift

That is what i am trying to do:
var i = 0
var string = "abcdef"
for value in string
{
value.[Put value of variable i here] = "a"
i++
}
How can i insert the value of i in the code?
Easiest is probably just convert it to an NSMutableString:
let string = "abcdef".mutableCopy() as NSMutableString
println( "\(string)")
for var i = 0; i < string.length; ++i {
string.replaceCharactersInRange(NSMakeRange(i, 1), withString: "a")
}
println( "\(string)")
Yes, it's a bit ugly but it works.
A much cleaner way is to use Swifts map function:
var string = "abcdef"
let result = map(string) { (c) -> Character in
"a"
}
println("\(result)") // aaaaaa
You should just be able to use the following but this doesn't compile:
map(string) { "a" }
In you comments you mention you want to split up the string on a space, you can just use this for that:
let stringWithSpace = "abcdef 012345"
let splitString = stringWithSpace.componentsSeparatedByString(" ")
println("\(splitString[0])") // abcdef
println("\(splitString[1])") // 012345

How to replace nth character of a string with another

How could I replace nth character of a String with another one?
func replace(myString:String, index:Int, newCharac:Character) -> String {
// Write correct code here
return modifiedString
}
For example, replace("House", 2, "r") should be equal to "Horse".
Solutions that use NSString methods will fail for any strings with multi-byte Unicode characters. Here are two Swift-native ways to approach the problem:
You can use the fact that a String is a sequence of Character to convert the string to an array, modify it, and convert the array back:
func replace(myString: String, _ index: Int, _ newChar: Character) -> String {
var chars = Array(myString) // gets an array of characters
chars[index] = newChar
let modifiedString = String(chars)
return modifiedString
}
replace("House", 2, "r")
// Horse
Alternately, you can step through the string yourself:
func replace(myString: String, _ index: Int, _ newChar: Character) -> String {
var modifiedString = String()
for (i, char) in myString.characters.enumerate() {
modifiedString += String((i == index) ? newChar : char)
}
return modifiedString
}
Since these stay entirely within Swift, they're both Unicode-safe:
replace("🏠🏑🏠🏑🏠", 2, "🐴")
// 🏠🏑🐴🏑🏠
In Swift 4 it's much easier.
let newString = oldString.prefix(n) + char + oldString.dropFirst(n + 1)
This is an example:
let oldString = "Hello, playground"
let newString = oldString.prefix(4) + "0" + oldString.dropFirst(5)
where the result is
Hell0, playground
The type of newString is Substring. Both prefix and dropFirst return Substring. Substring is a slice of a string, in other words, substrings are fast because you don't need to allocate memory for the content of the string, but the same storage space as the original string is used.
I've found this solution.
var string = "Cars"
let index = string.index(string.startIndex, offsetBy: 2)
string.replaceSubrange(index...index, with: "t")
print(string)
// Cats
Please see NateCook answer for more details
func replace(myString: String, _ index: Int, _ newChar: Character) -> String {
var chars = Array(myString.characters) // gets an array of characters
chars[index] = newChar
let modifiedString = String(chars)
return modifiedString
}
For Swift 5
func replace(myString: String, _ index: Int, _ newChar: Character) -> String {
var chars = Array(myString) // gets an array of characters
chars[index] = newChar
let modifiedString = String(chars)
return modifiedString
}
replace("House", 2, "r")
This is no longer valid and deprecated.
You can always use swift String with NSString.So you can call NSString function on swift String.
By old stringByReplacingCharactersInRange: you can do like this
var st :String = "House"
let abc = st.bridgeToObjectiveC().stringByReplacingCharactersInRange(NSMakeRange(2,1), withString:"r") //Will give Horse
For modify existing string:
extension String {
subscript(_ n: Int) -> Character {
get {
let idx = self.index(startIndex, offsetBy: n)
return self[idx]
}
set {
let idx = self.index(startIndex, offsetBy: n)
self.replaceSubrange(idx...idx, with: [newValue])
}
}
}
var s = "12345"
print(s[0])
s[0] = "9"
print(s)
I've expanded upon Nate Cooks answer and transformed it into a string extension.
extension String {
//Enables replacement of the character at a specified position within a string
func replace(_ index: Int, _ newChar: Character) -> String {
var chars = Array(characters)
chars[index] = newChar
let modifiedString = String(chars)
return modifiedString
}
}
usage:
let source = "House"
let result = source.replace(2,"r")
result is "Horse"
I think what #Greg was trying to achieve with his extension is this:
mutating func replace(characterAt index: Int, with newChar: Character) {
var chars = Array(characters)
if index >= 0 && index < self.characters.count {
chars[index] = newChar
let modifiedString = String(chars)
self = modifiedString
} else {
print("can't replace character, its' index out of range!")
}
}
usage:
let source = "House"
source.replace(characterAt: 2, with: "r") //gives you "Horse"
After looking at the Swift Docs, I managed to make this function:
//Main function
func replace(myString:String, index:Int, newCharac:Character) -> String {
//Looping through the characters in myString
var i = 0
for character in myString {
//Checking to see if the index of the character is the one we're looking for
if i == index {
//Found it! Now instead of adding it, add newCharac!
modifiedString += newCharac
} else {
modifiedString += character
}
i = i + 1
}
// Write correct code here
return modifiedString
}
Please note that this is untested, but it should give you the right idea.
func replace(myString:String, index:Int, newCharac:Character) -> String {
var modifiedString = myString
let range = Range<String.Index>(
start: advance(myString.startIndex, index),
end: advance(myString.startIndex, index + 1))
modifiedString.replaceRange(range, with: "\(newCharac)")
return modifiedString
}
I would prefer to pass a String than a Character though.
Here's a way to replace a single character:
var string = "This is the original string."
let offset = 27
let index = string.index(string.startIndex, offsetBy: offset)
let range = index...index
print("ORIGINAL string: " + string)
string.replaceSubrange(range, with: "!")
print("UPDATED string: " + string)
// ORIGINAL string: This is the original string.
// UPDATED string: This is the original string!
This works with multi-character strings as well:
var string = "This is the original string."
let offset = 7
let index = string.index(string.startIndex, offsetBy: offset)
let range = index...index
print("ORIGINAL string: " + string)
string.replaceSubrange(range, with: " NOT ")
print("UPDATED string: " + string)
// ORIGINAL string: This is the original string.
// UPDATED string: This is NOT the original string.
var s = "helloworld"
let index = ((s.count) / 2) // index is 4
let firstIndex = s.index(s.startIndex, offsetBy: index)
let secondIndex = s.index(s.startIndex, offsetBy: index)
s.replaceSubrange(firstIndex...secondIndex, with: "*")
print("Replaced string is: \(s)") //OUTPUT IS: hell*world
This is working fine to replace string using the index.
String class in Swift (till v5 and maybe later) is what other languages call a StringBuilder class, and for performance reasons, Swift does NOT provide setting character by index; If you don't care about performance a simple solution could be:
public static func replace(_ string: String, at index: Int, with value: String) {
let start = string.index(string.startIndex, offsetBy: index)
let end = string.index(start, offsetBy: 1)
string.replaceSubrange(start..<end, with: value)
}
Or as an extension:
extension String {
public func charAt(_ index: Int) -> Character {
return self[self.index(self.startIndex, offsetBy: index)];
}
public mutating func setCharAt(_ index: Int, _ new: Character) {
self.setCharAt(index, String(new))
}
public mutating func setCharAt(_ index: Int, _ new: String) {
let i = self.index(self.startIndex, offsetBy: index)
self.replaceSubrange(i...i, with: new)
}
}
Note how above needs to call index(...) method to convert integer to actual-index!? It seems, Swift implements String like a linked-list, where append(...) is really fast, but even finding the index (without doing anything with it) is a linear-time operation (and gets slower based on concatenation count).
public void createEncodedSentence() {
StringBuffer buff = new StringBuffer();
int counter = 0;
char a;
for (int i = 0; i < sentence.length(); i++) {
a = sentence.charAt(i);
if (a == '.') {
buff.append('*');
}
if (a != ' ' && a != '.') {
counter++;
}
if (counter % 3 == 0) {
buff.append("");
}
buff.append(sentence.charAt(i));
}
encodedSentence = buff.toString();
}
Strings in swift don't have an accessor to read or write a single character. There's an excellent blog post by Ole Begemann describing how strings in swift work.
Note: the implementation below is wrong, read addendum
So the right way is by taking the left part of the string up to the index -1 character, append the replacing character, then append the string from index + 1 up to the end:
func myReplace(myString:String, index:Int, newCharac:Character) -> String {
var modifiedString: String
let len = countElements(myString)
if (index < len) && (index >= 0) {
modifiedString = myString.substringToIndex(index) + newCharac + myString.substringFromIndex(index + 1)
} else {
modifiedString = myString
}
return modifiedString
}
Note: in my implementation I chose to return the original string if the index is not in a valid range
Addendum Thanks to #slazyk, who found out that my implementation is wrong (see comment), I am providing a new swift only version of the function.
func replace(myString:String, index:Int, newCharac:Character) -> String {
var modifiedString: String
if (index < 0) || (index >= countElements(myString)) {
modifiedString = myString
} else {
var start = myString.startIndex
var end = advance(start, index)
modifiedString = myString[start ..< end]
modifiedString += newCharac
start = end.successor()
end = myString.endIndex
modifiedString += myString[start ... end]
}
return modifiedString
}
#codester's answer looks very good, and it's probably what I would use myself.
It would be interesting to know how performances compare though, using a fully swift solution and bridging to objective-c instead.
Here is an efficient answerΒ :
import Foundation
func replace(myString:String, index:Int, newCharac:Character) -> String {
return myString.substringToIndex(index-1) + newCharac + myString.substringFromIndex(index)
}

How to split a string into multiple strings if spaces are detected (GM:Studio)

I made a console program, but the problem is that it doesn't allow parameters to be inserted. So I'm wondering how would I split a single string into multiple strings to achieve what I need. E.g.: text="msg Hello" would be split into textA="msg" and textB="Hello"
This is the main console code so far (just to show the idea):
if (keyboard_check_pressed(vk_enter)) {
text_console_c = asset_get_index("scr_local_"+string(keyboard_string));
if (text_console_c > -1) {
text_console+= "> "+keyboard_string+"#";
script_execute(text_console_c);
text_console_c = -1;
}
else if (keyboard_string = "") {
text_console+= ">#";
}
else {
text_console+= "> Unknown command: "+keyboard_string+"#";
};
keyboard_string = "";
}
I cant recommend spliting string with iteration by char, because when u try split very very very long string, then time to split is very long and can freeze thread for a short/long time. Game maker is single threaded for now.
This code is much faster.
string_split
var str = argument[0] //string to split
var delimiter = argument[1] // delimiter
var letDelimiter = false // append delimiter to each part
if(argument_count == 3)
letDelimiter = argument[2]
var list = ds_list_create()
var d_at = string_pos(delimiter, str)
while(d_at > 0) {
var part = string_delete(str, d_at , string_length(str))
if(letDelimiter)
part = part + delimiter
str = string_delete(str, 1, d_at)
d_at = string_pos(delimiter, str)
ds_list_add(list, part)
if(d_at == 0 && str != "")//last string without delimiter, need to add too
ds_list_add(list, str)
}
return list;
Dont forget ds_list_destroy after you iterate all strings
for example:
var splited = string_split("first part|second part", '|')
for(splited) {
//do something with each string
}
ds_list_destroy(splited)
Something like this may help, haven't tested it out but if you can follow what is going on its a good place to start.
Text = "msg Hello"
counter = 0
stringIndex = 0
for (i = 0; i < string_length(text); i++)
{
if string_char_at(text,i) == " "
{
counter++
stringIndex = 0
} else {
string_insert(string_char_at(text,i),allStrings(counter),stringIndex)
stringIndex++
}
}
allStrings should be an array containing each of the separate strings. Whenever a " " is seen the next index of allStrings starts having it's characters filled in. stringIndex is used to add the progressive characters.

Resources