Removing items in a MutableList with Kotlin - 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)

Related

Extracting items from a Map using a step

In Kotlin you can extract list items using a step like this:
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.slice(0..4 step 2))
Is there a similar way to do this for a map? So instead of listOf, I'm using mapOf.
First you need to think about whether it makes sense to use slice on a Map. The Map interface does not guarantee an order, so it does not have the concept of indices that you can select from, which is what slice does.
If your Map is backed by a specific implementation that guarantees an iteration order, such as LinkedHashMap, then it can make sense to convert the Map's entries into a List so you can slice them, and then you can convert the result back to a Map.
val result = someLinkedHashMap
.entries
.toList()
.slice(someRange)
.associate { it.key to it.value } // change the filtered entries list into a new map
The associate function's documentation guarantees a Map implementation that has consistent execution order.
slice() is an extension function for Arrays and Lists.
To convert a Map to a List, first get the entries Set, and then convert the Set into a List.
Run in Kotlin Playground
fun main() {
val numbers = mapOf(
"one" to 1,
"two" to 2,
"three" to 3,
"four" to 4,
"five" to 5,
"six" to 6,
)
// get the Map entries, and convert them to a List - then slice
val sliced = numbers.entries.toList().slice(0..4 step 2)
println(sliced)
}
Output:
[one=1, three=3, five=5]

Kotlin, is it possible to access the pair of a map?

Is it possible to access the whole Pair of a map, not only the key or a value?
Let's say we have a map
map = mapOf(Pair("Example1", 1), Pair("Example2", 2), Pair("Example3",
3))
I would like to access the second pair and put it into a variable, something like I would do with a list:
val ex2 = map[1] #this would result with {"Example2", 2}
And then i would be able to access the pair's key/value like:
ex2.key / ex2.value
More specifically, I would like to use this in my function to return a specific pair of the map.
Not sure if this would help
val mapString = mutableMapOf(1 to "Person", 2 to "Animal")
val (id, creature) = 1 to mapString.getValue(1)
Log.e("MapPair", "$id, $creature")
prints
1, Person
or if you're iterating through the entire map
mapString.forEach {
val (id, creature) = it.key to it.value
Log.e("MapPair", "$id : $creature")
}
prints
1 : Person
2 : Animal
or using Pair
val key = 1
val pair = Pair(key, mapString.getValue(key))
Log.e("MapPair", "$pair")
prints
(1, Person)
or if you're iterating through the entire map using Pair
mapString.forEach {
val pair = Pair(it.key, it.value)
Log.e("MapPair", "$pair")
}
prints
(1, Person)
(2, Animal)
Update: For iterating through the map you can also go with Destructuring Declarations
val mapString = mutableMapOf(1 to "Person", 2 to "Animal")
for ((key, value) in mapString) {
Log.e("MapComponents", "$key, $value")
}
From your comment, it seems like you want to fetch the key corresponding to a given value.
val map = mapOf("Chicken" to 20, "Egg" to 10, "Bread" to 5)
val valueToFind = 20
val key = map.toList().find { it.second == valueToFind }?.first
println(key)
Output:
Chicken
If the value doesn't exist, it will give null.

Replace multiple chars with multiple chars in string

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

Why does Kotlin Nested Lists have this behavior

Take a look at this code example
val list = mutableListOf("1", "2", "3")
val listOfLists = mutableListOf(list, list, list)
val firstList = listOfLists[0] //get reference to *list* variable
firstList[0] = "Hello" // replace "1" in firstList with "Hello"
print(listOfLists)
This is the printed output
[[Hello, 2, 3], [Hello, 2, 3], [Hello, 2, 3]]
If you notice listOfLists variable is never called again in the but it is not being updated. And the update is not just at the first position it is updated at all the position.
My intention is just to update the firstList variable alone.
Why does this happen and how do I go around it?
It's OOP's object behavior man. It affects all the references...
Java handles variables as references and therefore Kotlin as well. Thus all references change after updating one. You'd have to work with copies of the list to ensure these update independently:
val list = listOf("1", "2", "3")
val listOfLists = mutableListOf(
list.toMutableList(),
list.toMutableList(),
list.toMutableList())
val firstList = listOfLists[0]
firstList[0] = "Hello"
print(listOfLists)
Problem
You are putting three references to the same list in another list. Changing the list via one of those references changes the data to which all references point.
Solution
If you don't want that you need to create three independent copies of list for example using the toMutableList() extension function:
val list = mutableListOf("1", "2", "3")
val listOfLists = mutableListOf(
list.toMutableList(),
list.toMutableList(),
list.toMutableList()
)
listOfLists.first()[0] = "Hello"
print(listOfLists)
Output:
[[Hello, 2, 3], [1, 2, 3], [1, 2, 3]]

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")
}