Why does Kotlin Nested Lists have this behavior - kotlin

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]]

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]

Create List<Object> from List<List<Object>> in kotlin

I have List<List> and I want to create List where I have all cars from List<List>. There is any way in Kotlin to create that (using map or something)? I don't want to create new list and add items in loops
val listOfLists: List<List<Int>> = listOf(listOf(1, 2, 3), listOf(4, 5))
One way would be to use flatten function. This function just flattens the inner lists
val flatten: List<Int> = listOfLists.flatten() //[1, 2, 3, 4, 5]
But if you need to do some transformation as well as flattening the list then you can do it with flatMap():
val flatMap: List<Int> = listOfLists.flatMap { it.map { it*2 } } //[2, 4, 6, 8, 10]
tl;dr List.flatten()
Let's say your List<List<Car>> is called carLists, then you can simply call val cars: List<Car> = carLists.flatten() if you want to have a single list containing all the cars from the list of lists of cars.
Code only:
val carLists: List<List<Car>> = … // created or received
val cars = carLists.flatten()

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)

How do I make a Kotlin list mutable, add items, and then iterate over the resulting immutable list?

Essentially I need to start with an immutable list of a few items, convert it to a mutable list and add a few items, and then iterate over it with firstOrNull. Here's my scratch file:
val immutableStarter = listOf(1, 2, 3)
val mutable = immutableStarter.toMutableList()
mutable.add(4)
mutable.addAll(listOf(5, 6, 7))
mutable.add(8)
println(mutable)
val result = mutable.firstOrNull { item ->
println("Checking Item $item")
item > 7
} ?: 0
println(result)
The println(mutable) call correctly prints the list with all eight items. However, it seems the firstOrNull operation is only running on the first three items in the list. I only get the "Checking Item $item" output three times. If I add a fourth item to the immutableStarter it checks four times. So the best I can gather, for some reason, it's only iterating over the items in the initial, immutable starting list.
If I wrap lines 10-15 in a try/catch or with run, I get the output I would expect - a "Checking Item $item" printout for each of the 8 items in the list.
Why isn't this working as I have it written, and what is it about wrapping the firstOrNull call in a try/catch or run that makes it work?
================================================
EDIT: Someone asked for the output of my scratch file. Here it is:
Checking Item 1
Checking Item 2
Checking Item 3
val immutableStarter: List<Int>
val mutable: MutableList<Int>
true
true
true
[1, 2, 3, 4, 5, 6, 7, 8]
val result: Int
0
It looks like it may be an issue with how scratch files are evaluated - it looks like IntelliJ may be attempting to evaluate the scratch file asynchronously. Toggling the Use REPL checkbox at the top of IntelliJ, for example, gives me the output I'd expect:
res2: kotlin.Boolean = true
res3: kotlin.Boolean = true
res4: kotlin.Boolean = true
[1, 2, 3, 4, 5, 6, 7, 8]
Checking Item 1
Checking Item 2
Checking Item 3
Checking Item 4
Checking Item 5
Checking Item 6
Checking Item 7
Checking Item 8
8

Unsolved reference error in copyOfRange

I'm trying to copy first 2 elements of my firstArray to my secondArray.
var firstArray = arrayListOf(1,2,3,4)
var secondArray = firstArray.copyOfRange(0,1)
However I'm getting "unsolved reference" error in copyOfRange for some reason.
If I define an Array type for my firstArray I can solve the problem but I don't want to because it can contain different variables in future
The arrayListOf function returns an ArrayList. If this is what you want to use, then you should use the subList function on it:
val firstList = arrayListOf(1, 2, 3, 4)
val secondList = firstList.subList(0, 1)
Note that subList doesn't create a copy, you can make a copy with a toList call, for example:
val secondList = firstList.subList(0, 1).toList()
Or with an explicit call to the ArrayList constructor:
val secondList = ArrayList(firstArray.subList(0,1))
If you want to use an Array instead, use arrayOf and then you can use copyOfRange:
val firstArray = arrayOf(1, 2, 3, 4)
val secondArray = firstArray.copyOfRange(0, 1)