Kotlin: how to swap character in String - kotlin

I would like to swap a string from "abcde" to "bcdea". So I wrote my code as below in Kotlin
var prevResult = "abcde"
var tmp = prevResult[0]
for (i in 0..prevResult.length - 2) {
prevResult[i] = prevResult[i+1] // Error on preveResult[i]
}
prevResult[prevResult.length-1] = tmp // Error on preveResult[prevResult.lengt-1]
It errors out as stated above comment line. What did I do wrong? How could I fix this and get what I want?

Strings in Kotlin just like in Java are immutable, so there is no string.set(index, value) (which is what string[index] = value is equivalent to).
To build a string from pieces you could use a StringBuilder, construct a CharSequence and use joinToString, operate on a plain array (char[]) or do result = result + nextCharacter (creates a new String each time -- this is the most expensive way).
Here's how you could do this with StringBuilder:
var prevResult = "abcde"
var tmp = prevResult[0]
var builder = StringBuilder()
for (i in 0..prevResult.length - 2) {
builder.append(prevResult[i+1])
}
builder.append(tmp) // Don't really need tmp, use prevResult[0] instead.
var result = builder.toString()
However, a much simpler way to achieve your goal ("bcdea" from "abcde") is to just "move" one character:
var result = prevResult.substring(1) + prevResult[0]
or using the Sequence methods:
var result = prevResult.drop(1) + prevResult.take(1)

You can use drop(1) and first() (or take(1)) to do it in one line:
val str = "abcde"
val r1 = str.drop(1) + str.first()
val r2 = str.drop(1) + str.take(1)
As to your code, Kotlin String is immutable and you cannot modify its characters. To achieve what you want, you can convert a String to CharArray, modify it and then make a new String of it:
val r1 = str.toCharArray().let {
for (i in 0..it.lastIndex - 1)
it[i] = it[i+1]
it[it.lastIndex] = str[0] // str is unchanged
String(it)
}
(let is used for conciseness to avoid creating more variables)
Also, you can write a more general version of this operation as an extension function for String:
fun String.rotate(n: Int) = drop(n % length) + take(n % length)
Usage:
val str = "abcde"
val r1 = str.rotate(1)

Simpler solution: Just use toMutableList() to create a MutableList of Char and then join it all together with joinToString.
Example:
Given a String input, we want to exchange characters at positions posA and posB:
val chars = input.toMutableList()
val temp = chars[posA]
chars[posA] = chars[posB]
chars[posB] = temp
return chars.joinToString(separator = "")

Since Strings are immutable, you will have to copy the source string into an array, make changes to the array, then create a new string from the modified array. Look into:
getChars() to copy the string chars into an array.
Perform your algorithm on that array, making changes to it as needed.
Convert the modified array back into a String with String(char[]).

Related

Convert String into list of Pairs: Kotlin

Is there an easier approach to convert an Intellij IDEA environment variable into a list of Tuples?
My environment variable for Intellij is
GROCERY_LIST=[("egg", "dairy"),("chicken", "meat"),("apple", "fruit")]
The environment variable gets accessed into Kotlin file as String.
val g_list = System.getenv("GROCERY_LIST")
Ideally I'd like to iterate over g_list, first element being ("egg", "dairy") and so on.
And then ("egg", "dairy") is a tuple/pair
I have tried to split g_list by comma that's NOT inside quotes i.e
val splitted_list = g_list.split(",(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*\$)".toRegex()).toTypedArray()
this gives me first element as [("egg", second element as "dairy")] and so on.
Also created a data class and tried to map the string into data class using jacksonObjectMapper following this link:
val mapper = jacksonObjectMapper()
val g_list = System.getenv("GROCERY_LIST")
val myList: List<Shopping> = mapper.readValue(g_list)
data class Shopping(val a: String, val b: String)
You can create a regular expression to match all strings in your environmental variable.
Regex::findAll()
Then loop through the strings while creating a list of Shopping objects.
// Raw data set.
val groceryList: String = "[(\"egg\", \"dairy\"),(\"chicken\", \"meat\"),(\"apple\", \"fruit\")]"
// Build regular expression.
val regex = Regex("\"([\\s\\S]+?)\"")
val matchResult = regex.findAll(groceryList)
val iterator = matchResult.iterator()
// Create a List of `Shopping` objects.
var first: String = "";
var second: String = "";
val shoppingList = mutableListOf<Shopping>()
var i = 0;
while (iterator.hasNext()) {
val value = iterator.next().value;
if (i % 2 == 0) {
first = value;
} else {
second = value;
shoppingList.add(Shopping(first, second))
first = ""
second = ""
}
i++
}
// Print Shopping List.
for (s in shoppingList) {
println(s)
}
// Output.
/*
Shopping(a="egg", b="dairy")
Shopping(a="chicken", b="meat")
Shopping(a="apple", b="fruit")
*/
data class Shopping(val a: String, val b: String)
Never a good idea to use regex to match parenthesis.
I would suggest a step-by-step approach:
You could first match the name and the value by
(\w+)=(.*)
There you get the name in group 1 and the value in group 2 without caring about any subsequent = characters that might appear in the value.
If you then want to split the value, I would get rid of start and end parenthesis first by matching by
(?<=\[\().*(?=\)\])
(or simply cut off the first and last two characters of the string, if it is always given it starts with [( and ends in )])
Then get the single list entries from splitting by
\),\(
(take care that the split operation also takes a regex, so you have to escape it)
And for each list entry you could split that simply by
,\s*
or, if you want the quote character to be removed, use a match with
\"(.*)\",\s*\"(.*)\"
where group 1 contains the key (left of equals sign) and group 2 the value (right of equals sign)

Kotlin algorithm to reverse a given string

I'm creating an algorithm in Kotlin that should reverse a given string and output it, so, for example, the string would be "Hello World! and the output would be "olleH !dlroW. I know there's a function that does this already but I'm practising with loops and if statements so I'm doing it myself.
So far, I've got most of a working solution, the only problem with the code I have in that it only works with odd length strings, because of a while loop. I've put what I have in this post.
I'm stuck on what to change to make this program work with strings that are of an even length, currently with those strings, the program falls into an infinite loop because the condition is never met.
The exclusion part of the program will jump over characters that are included in the exclusion string, I've already tested this and it works fine, provided the string minus the skipped character is not of even length.
fun main() {
val userInput = ""
val exclusion = ""
val wordsInString = userInput.split(" ")
var wordsSize = wordsInString.size
var wordPointer = 0
while (wordPointer < wordsSize) {
var currentWord = wordsInString[wordPointer]
var charArray = currentWord.toCharArray()
var charPointerOne = 0
var charPointerTwo = currentWord.length - 1
while (charPointerOne != charPointerTwo) {
if (exclusion.contains(charArray[charPointerOne])) {
charPointerOne++
} else if (exclusion.contains(charArray[charPointerTwo])) {
charPointerTwo--
} else {
var charToSwtichOne = charArray[charPointerOne]
var charToSwitchTwo = charArray[charPointerTwo]
charArray[charPointerOne] = charToSwitchTwo
charArray[charPointerTwo] = charToSwtichOne
charPointerOne++
charPointerTwo--
}
}
wordPointer++
var outputString = String(charArray)
print(outputString + " ")
}
}
You need to change the loop control condition to use < instead of !=. because in case of even length strings they simply never meet and jump over. for example if you had a String of length two, then on first iteration charPointerOne will have value 0 and charPointerTwo will have value 1, and on the next iteration charPointerOne will be incremented to 1 and charPointerTwo will be decremented to 0 and these values still satisfy the loop control hence the loop continues. So to fix this change your code as
while (charPointerOne < charPointerTwo)

CharBuffer to string?

How to get the string "hi" from the CharBuffer? toString() does not seem to work.
val a = CharBuffer.allocate(10);
a.put('h');
a.put('i');
val b = a.toString();
Variable states after running the code above:
CharBuffer is pretty low-level and really meant for I/O stuff, so it may seem illogical at first. In your example it actually returned a string containing remaining 8 bytes that you didn't set. To make it return your data you need to invoke flip() like this:
val a = CharBuffer.allocate(10);
a.put('h');
a.put('i');
a.flip()
val b = a.toString();
You can find more in the docs of the Buffer
For more typical use cases it is much easier to use StringBuilder:
val a = StringBuilder()
a.append('h')
a.append('i')
val b = a.toString()
Or even use a Kotlin util that wraps StringBuilder:
val b = buildString {
append('h')
append('i')
}

How to sort a string alphabetically in Kotlin

I want to reorder the string "hearty" to be in alphabetical order: "aehrty"
I've tried:
val str = "hearty"
val arr = str.toCharArray()
println(arr.sort())
This throws an error. I've also tried the .split("") method with the .sort(). That also throws an error.
You need to use sorted() and after that joinToString, to turn the array back into a String:
val str = "hearty"
val arr = str.toCharArray()
println(arr.sorted().joinToString("")) // aehrty
Note: sort() will mutate the array it is invoked on, sorted() will return a new sorted array leaving the original untouched.
So your issue is that CharArray.sort() returns Unit (as it does an in-place sort of the array). Instead, you can use sorted() which returns a List<Char>, or you could do something like:
str.toCharArray().apply { sort() }
Or if you just want the string back:
fun String.alphabetized() = String(toCharArray().apply { sort() })
Then you can do:
println("hearty".alphabetized())

How to convert Object(with value) into Map

I have a object that I want to print it into string [key1=value1&key2=value2...etc] without the null value key value pair and comma into &.
So first of all i think of putting it into a map but it won't work and I don know how it work either.
val wxPayOrderObj = WxPayOrder(appid = "wx0b6dcsad20b379f1", mch_id =
"1508334851", nonce_str = UUID.randomUUID().toString(),sign = null,
body = "QQTopUp", out_trade_no = "20150806125346", total_fee = req.total_fee,
spbill_create_ip = "123.12.12.123",
trade_type = "JSAPI", openid = "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o")
so the output will be
appid=wx0b6dc78d20b379f1&mch_id=150788851&nonce_str=UUID.randomUUID().toString()&
body=QQTopUp&out_trade_no=20150806125346&total_fee=req.total_fee&
spbill_create_ip=123.12.12.123&trade_type=JSAPI&openid=oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
anyone please help me, thanks in advances.
I don't really get your question, but you want to convert object to string (to a format that you want)?
Override the object's toString() to return "[key1=value1&key2=value2...etc]"
example
override fun toString(){
// make sure you compute the data first
val answer = "[key1=$value1&key2=$value2...etc]"
return answer
}
The $ is used in string templates (That's directly writing the name of a variable, the value will be used later to be concatenated) with other strings)