Replace multiple chars with multiple chars in string - kotlin

I am looking for a possibility to replace multiple different characters with corresponding different characters in Kotlin.
As an example I look for a similar function as this one in PHP:
str_replace(["ā", "ē", "ī", "ō", "ū"], ["a","e","i","o","u"], word)
In Kotlin right now I am just calling 5 times the same function (for every single vocal) like this:
var newWord = word.replace("ā", "a")
newWord = word.replace("ē", "e")
newWord = word.replace("ī", "i")
newWord = word.replace("ō", "o")
newWord = word.replace("ū", "u")
Which of course might not be the best option, if I have to do this with a list of words and not just one word. Is there a way to do that?

You can maintain the character mapping and replace required characters by iterating over each character in the word.
val map = mapOf('ā' to 'a', 'ē' to 'e' ......)
val newword = word.map { map.getOrDefault(it, it) }.joinToString("")
If you want to do it for multiple words, you can create an extension function for better readability
fun String.replaceChars(replacement: Map<Char, Char>) =
map { replacement.getOrDefault(it, it) }.joinToString("")
val map = mapOf('ā' to 'a', 'ē' to 'e', .....)
val newword = word.replaceChars(map)

Just adding another way using zip with transform function
val l1 = listOf("ā", "ē", "ī", "ō", "ū")
val l2 = listOf("a", "e", "i", "o", "u")
l1.zip(l2) { a, b -> word = word.replace(a, b) }
l1.zip(l2) will build List<Pair<String,String>> which is:
[(ā, a), (ē, e), (ī, i), (ō, o), (ū, u)]
And the transform function { a, b -> word = word.replace(a, b) } will give you access to each item at each list (l1 ->a , l2->b).

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)

How to remove String from a ListOf Strings in Kotlin?

I have a listOf Strings
val help = listOf("a","b","c")
And want to remove b from the list but not using index because I will get Strings randomly like this
val help = listOf("c","a","b")
How to do this?
You can filter the List using the condition item does not equal "b", like…
fun main(args: Array<String>) {
// example list
val help = listOf("a","b","c")
// item to be dropped / removed
val r = "b"
// print state before
println(help)
// create a new list filtering the source
val helped = help.filter { it != r }.toList()
// print the result
println(helped)
}
Output:
[a, b, c]
[a, c]
Lists by default aren't mutable. You should use mutable lists instead if you want that. Then you can simply do
val help = mutableListOf("a","b","c")
help.remove("b")
or you can do it like this if help really needs to be a non-mutable list
val help = listOf("a","b","c")
val newHelp = help.toMutableList().remove("b")
using a filter like in deHaar's answer is also possible

Removing items in a MutableList with Kotlin

I could have a list like
["1", "2", "3", ".", "4", "."]
After the first occurrence of my delimiter, I want the duplicates removed
In my case, the above list should become
["1", "2", "3", ".", "4"]
I want all duplicates of "." removed after the first occurrence. Whats the best way?
You can use a temporary MutableSet to easily check if values are duplicates.
fun <T> MutableList<T>.removeDuplicates(): Boolean {
val set = mutableSetOf<T>()
return retainAll { set.add(it) }
}
Explanation: MutableList.retainAll is a function that removes every item for which the lambda returns false. When you add an item to a Set, it returns false if the item already exists in the set. So the first occurrence of each unique item will return true while subsequent occurrences will return false
Edit: It occurred to me that maybe you are interested only in the specific delimiter entry having duplicates. In that case, instead of a Set, I would use just a Boolean to track if it's been found yet. And I use removeAll instead of retainAll to make it easier to read.
fun <T> MutableList<T>.removeDuplicatesOf(delimiter: T): Boolean {
var firstInstanceFound = false
return removeAll { it == delimiter && firstInstanceFound.also { firstInstanceFound = true } }
}
Explanation: removeAll will remove anything for which the lambda returns true. Due to logical short-circuiting, anything that isn't the delimiter will return false before the part after the && is reached. When the first delimiter is found, firstInstanceFound will be false, so the logical statement evaluates to false. The also branch is also hit, so firstInstanceFound will be true for any subsequent delimiters found.
I found two ways. The first is the most 'Java':
// Setup values
val list = mutableListOf("1", "2", "3", ".", "4", ".")
val delim = "."
// Check if list is empty
val size = list.size - 1
if (size < 0) return
// Get first delim index
val firstMatch = list.indexOf(delim) + 1
if (firstMatch < 1) return
// Reverse-iterate the list until delim location
for (i in size downTo minOf(firstMatch, size)) {
if (list[i] == delim) list.removeAt(i)
}
println(list)
Here is smaller Kotlin-style solution:
val list = mutableListOf("1", "2", "3", ".", "4", ".")
val delim = "."
val firstMatch = list.indexOf(delim)
if (firstMatch < 0) return
val newList = list.filterIndexed { index, s -> s != delim || index == firstMatch }
println(newList)
The easy way to do this is using distinct() function, that returns a list without duplicated values
val list = listOf('1', '2', '3', '.', '4', '.')
println(list.distinct()) // [1, 2, 3, ., 4]
To remove these apply for loops and add the items in a new list.Steps
First Convert list into mutable list
val list = listOf("1", "2", "3", ".", "4", ".")
val mutablelist =list.toMutableList()
after this apply for loop and storing data in new Outcoming list
val outcominglist= ArrayList<String>()
for(i in list){
val item = mutablelist[0]
mutablelist.removeAt(0)
if(outcominglist.contains(item)){
}
else{
outcominglist.add(item)
}
}
To print Outcoming list.
print(outcominglist)
Second and the Simplest Method(Use .distinct Method)
val list = listOf('1', '2', '3', '.', '4', '.')
val newlist =list.distinct()
print(newlist)
Since you're using Kotlin, you have the advantage of immutable data types and functions without side-effects. Here's how to do it with an immutable list in a function that doesn't expose any state externally, by making use of fold():
val originalList = listOf("1", "2", "3", ".", "4", ".")
val (filteredList, _) = originalList.fold(
Pair(emptyList<String>(), false)
) { (newList, found), item ->
if (item == "." && !found) Pair(newList + item, true)
else if (item == ".") Pair(newList, true)
else Pair(newList + item, found)
}
println(filteredList)
Result:
[1, 2, 3, ., 4]
fold() takes an initial accumulator value, then applies the function for each element of list, updating the accumulator as it goes.
Here we set the accumulator to a Pair of an empty list, where we will build up the new list, and a boolean, to track if we've already seen ..
For each element of the original list, we return a new pair, adding the item to the new list (if necessary) and updating whether we have already seen .. newList + item doesn't add the item to the immutable list, it returns a new immutable list with the item appended to it. Because we pass the Bool tracking if we've seen . as part of the pair to each iteration, there's no need for a temporary variable outside the function to track this.
Finally, because the accumulated value is a Pair, we use destructuring to extract only the accumulated list (the first value) of the pair with val (filteredList, _) = pair.
For your list, the returned Pair values will look like this:
([1], false)
([1, 2], false)
([1, 2, 3], false)
([1, 2, 3, .], true)
([1, 2, 3, ., 4], true)
([1, 2, 3, ., 4], true)

Idiomatic way to add an element into sub-list in full-immutable hierarchy

I have the following data-structure:
var foo: Map<String, List<String>> = emptyMap()
foo += mapOf(
"1" to listOf("a", "b"),
"2" to listOf("a", "b"),
"3" to listOf("a", "b")
)
Map and sub-list are both immutable. I now want to add the element c to the list of the first element in the map.
I came up with this solutions:
foo += "1" to (foo["1"] ?: emptyList()) + "c"
but is this really the idiomatic way?
Update based on #hotkeys answer
foo += "1" to foo["1"].orEmpty() + "c"
One alternative you could check is the approach that the incubating library kotlinx.collections.immutable uses (or just use that library). It allows you to .mutate { ... } a map, which creates an immutable copy of that map with the applied mutations:
val m = immutableHashMapOf("1" to listOf("a", "b"))
val result = m.mutate { it["1"] = it["1"].orEmpty() + "c" }
See also: .orEmpty()
There's no equivalent to .mutate { ... } in the standard library, though, but you can define your own extension for read-only Map that would do the same.
The declaration and assignment can be joined unless there is some problem.
Make map & list mutable, as the need is to mutate
var foo: MutableMap<String, MutableList<String>> = mutableMapOf(
"1" to mutableListOf("a", "b"),
"2" to mutableListOf("a", "b"),
"3" to mutableListOf("a", "b")
)
we can use when block like below,
when {
foo.containsKey("1") -> foo["1"]?.add("c")
else -> foo["1"] = mutableListOf("c")
}

Kotlin: how to swap character in String

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[]).