Equivalent of Iterator.Remove in Kotlin HashMap? - kotlin

Sorry if this is a dumb question-- but in java I am used to doing something like the following:
Iterator whatever = entrySet.iterator()
while (whatever.hasNext()) {
for (int i = 0; i < 4; i++) {
if (i == 3) {
whatever.remove(whatever.next().key)
}
}
}
(Pseudocode and the logic makes no sense)
However, the "remove" function doesn't exist for a hashmap in Kotlin. I understand you can use removeIf for a single condition, but I want to loop through a bunch of different conditions before I decide what to remove-- all without dodging a concurrent modification exception.
What is the way to do this in Kotlin?
Thank you for your time!

In Kotlin you can use removeIf on mutable map entries without accessing the iterator explicitly.
val entrySet = HashMap<String, String>()
entrySet.entries.removeIf {
// some predicate
}
will remove all entries that match the predicate

val addedItems = HashMap<String, Int>()
fun main() {
addedItems["Apple"] = 3
addedItems["Lemon"] = 4
println(addedItems)
addedItems.values.removeAll { it == 4 } //or remove by keys ".keys.removeAll{}
println(addedItems)
}
Output:
{Apple=3, Lemon=4}
{Apple=3}

Related

Putting Elements in MutableList of Kotlin

fun main() {
var list1 = mutableListOf<Any>()
for(i in 0 until 5) {
list1.set(i,i)
}
println(list1)
}
Above Code Gives Index 0 out of Bound for Length 0.
What is the Mistake. How do i put elemnts in the MutableList using Index.
You are using the wrong method here.
According to the documentation of set :"It replaces the element and add new at given index with specified element."
Here you declare an empty mutableList. So trying to replace at a certain index will give you an Array Out Of Bounds exception.
If you want to add a new element you need to use the add method : "It adds the given element to the collection."
So if we use add method it can be write like this :
fun main() {
var list1 = mutableListOf<Any>()
for(i in 0 until 5) {
list1.add(i,i)
}
println(list1)
}
Or without using index parameter :
fun main() {
var list1 = mutableListOf<Any>()
for(i in 0 until 5) {
list1.add(i)
}
println(list1)
}
You can still use the set method (even if it's not the best way) by declaring the initial length of your mutable list like #lukas.j said:
fun main() {
var list1 = MutableList<Any>(5) {it}
for(i in 0 until 5) {
list1.set(i,i)
}
println(list1)
}

Mapping array elements using a function in Kotlin

New to Kotlin from Python. In Python, I can simply use the code below to pass each element of a List to a multiline function and return an iterator of the result.
countArr = list(map(countReps, arr))
In Kotlin, I found that I had to do the following. Am I missing something?
fun LetterCountI(str: String): String {
val arr = str.split(" ")
var transform:(String) -> Int = {countReps(it)}
val countArr = arr.map(transform)
val mxIndex:Int
var ans:String
if (countArr.max()!=1){
mxIndex = countArr.indexOf(countArr.max())
ans = arr[mxIndex]
} else {
ans = "-1"
}
return ans;
}
fun countReps(str: String): Int {
var m = mutableMapOf<Char, Int>()
var v:Int
for (c in str){
if (c in m.keys){
v = m[c]?:0
m.put(c,v+1)
} else {
m.put(c,1)
}
}
return m.values.max() ?: 0
}```
I'm having a bit of a hard time understanding your code, but one thing I can tell you is that you can replace
var transform:(String) -> Int = {countReps(it)}
val countArr = arr.map(transform)
with
val countArr = arr.map(::countReps)
In addition to the line you ask about, just about all of that code could be rewritten more concisely and idiomatically in Kotlin. For example:
fun String.wordWithMostRepeatedLetters()
= split(" ")
.associateWith{ it.maxRepeatedLetters() }
.filter{ it.value > 1 }
.maxByOrNull{ it.value }?.key ?: "-1"
fun String.maxRepeatedLetters()
= groupBy{ it }.map{ it.value.size }.maxOrNull() ?: 0
I've renamed the functions to try to explain what they give; replaced the countArr list with a map from each word to its count, so that you don't need to re-scan it to find the word resulting; and changed both functions to take a String receiver instead of a parameter. Then, because each variable was only used once, I removed them and made it all in-line, using an expression body for each function.
Some of those things don't always improve clarity, of course, especially for long functions — but I hope it demonstrates how concise Kotlin can be. (Hopefully without sacrificing maintainability. Which version would be easier to read? Which would be more likely to harbour subtle bugs?)
It's still not clear what the hard-coded "-1" return value indicates, though… If no word has any repeated letters, a null return would be more idiomatic. (Or it would be simpler just to return the first word, removing the filter() call, and returning null only if the string is blank.)

incrementing hash map count in Kotlin

I have the function below. However, when I pass a string to it, I get the following error:
error: operator call corresponds to a dot-qualified call 'charCountMap.get(c).plus(1)' which is not allowed on a nullable receiver 'charCountMap.get(c)'. charCountMap.put(c, charCountMap.get(c) + 1)
private fun characterCount(inputString:String) {
val charCountMap = HashMap<Char, Int>()
val strArray = inputString.toCharArray()
for (c in strArray)
{
if (charCountMap.containsKey(c))
{
charCountMap.put(c, charCountMap.get(c) + 1)
}
else
{
charCountMap.put(c, 1)
}
}
}
The Kotlin Standard Library has groupingBy and eachCount for this purpose, you don't need to do any of this manually:
private fun characterCount(inputString:String) {
val charCountMap : Map<Char, Int> = inputString.groupingBy { it }.eachCount()
}
Note that I put the type on charCountMap for clarity, but it can be left off and inferred.
There is nice compute method in HashMap for this:
private fun characterCount(inputString:String) = hashMapOf<Char, Int>().also { charCountMap ->
inputString.forEach { charCountMap.compute(it) { _, v -> if (v == null) 1 else v + 1 } }
}
Both the other answers are correct. Todd's answer is right, you don't need to write a function for this. Just use the standard library. And if you are going to write a function that updates maps, Михаил Нафталь's suggestion to use compute() to handle updating existing values is also good.
However, if you're just doing this an an exercise, here are three suggestions to fix/improve your algorithm:
Instead of get(), use getValue(), which does not return null. It will raise an exception if the element does not exist, but you already checked for that.
Use the [] operator instead of put() (no need to, it's just nicer syntax).
You don't need to call toCharArray() because Strings are already iterable.
if (charCountMap.containsKey(c))
{
charCountMap[c] = charCountMap.getValue(c) + 1
}
else
{
charCountMap[c] = 1
}
Rewriting the whole thing using standard formatting:
fun characterCount(inputString: String): Map<Char, Int> {
val charCountMap = mutableMapOf<Char, Int>()
for (c in inputString) {
if (charCountMap.containsKey(c)) {
charCountMap[c] = charCountMap.getValue(c) + 1
} else {
charCountMap[c] = 1
}
}
return charCountMap
}

Get top N elements in SortedMap?

Is there any correct way to get top N elements from SortedMap? My variant is:
val sortedMap = map.filterValues { it in sortedValues }.toSortedMap()
if (sortedMap.size <= 20) {
return sortedMap
}
var result = mutableMapOf<String, Int>()
for ((key, value) in sortedMap) {
result[key] = value
if (result.size == 20) {
break
}
}
headMap is also a good solution for getting a portion of this map, for example
return when {
sortedMap.size >= 20 -> {
sortedMap.headMap(sortedMap.keys.elementAt(20))
}
else -> {
sortedMap
}
}
more from the docs here
val first20AsList: List<Map.Entry<String, Int>> = sortedMap.asIterable().take(20)
val first20AsMap: Map<String, Int> = first20AsList.associate { it.toPair() }
Solution
In my opinion the best way is to use asSequence() because it is evaluated lazily:
return sortedMap.asSequence().take(20).map{ it.toPair() }.toMap()
Information on Lazy evaluation
In general it is often favorable to use lazy variants of iterable containers, because it means that in many cases not the whole datastructure needs to be evaluated but just the portion of data that is required.
In your case that would be the first N in the SortedMap. If it really happens to result in a performance advantage is questionable, but at least it is possible.
Some Information from stack-overflow and kotlin on lazy sequences:
Kotlin's Iterable and Sequence look exactly same. Why are two types required?
Sequences # kotlinlang.org
Old Answer:
This answer was improved in some details thanks to the comment by #IR42. Before I used the spread operator for conversion to a sorted-map again (via sortedMapOf), and map { it.key to it.value } instead of { it.toPair() }:
return sortedMapOf(
*sortedMap.asSequence().take(20)
.map{ it.key to it.value }
.toList().toTypedArray())
What u could try is flatMap{} from where u can get list of those items and then use take() function on that list to determine amount of it. Example:
val list = mapOf<Int, Boolean>(
1 to true,
2 to false,
3 to true
).toSortedMap().flatMap {
listOf(it)
}.take(2)
println(list[1].key) // <-- 2
take(n)
Returns a list containing first n elements.

Taking sequence elements fulfilling a predicate then continuing from there in Kotlin

In Kotlin sequences have a takeWhile function that will let you take items as long as they adhere to a given predicate. What I'd like to do is take items according to that predicate, use them in some way, then alter the predicate and take the next "batch". So far I haven't really found a way of doing this purely with what sequences and iterators offer.
Following snippet of code illustrates the problem. The primeGenerator() function returns a Sequence of prime (Long) numbers. Suppose that I want to make lists with each list having prime numbers with the same number of digits. On creating each list I'd use it for some purpose. If the list conforms to what I was searching the iteration can end, otherwise move onto the next list.
val primeIt = primeGenerator().iterator()
var digits = 1
var next: Long? = null
val currentList = ArrayList<Long>()
while (digits < 4) {
next?.also { currentList.add(it) }
next = primeIt.next()
if (next.toString().length > digits) {
println("Primes with $digits: $currentList")
currentList.clear()
digits++
}
}
In this case it ends once the number of digits exceeds 3. This works fine, but I was wondering if there is some way to achieve the same with operations chained purely on the sequence or an iterator of it. Basically chunking the sequence but based on a predicate rather than a set size. The prime number example above is just for illustration, I'm after the general principle, not something that'd only work for this case.
There are no such functions in standard library for large (or infinite) sequences, but you may write such function by yourself (although it requires some extra code):
class BufferedIterator<T>(private val iterator: Iterator<T>) : Iterator<T> {
var current: T? = null
private set
var reachedEnd: Boolean = false
private set
override fun hasNext(): Boolean = iterator.hasNext().also { reachedEnd = !it }
override fun next(): T = iterator.next().also { current = it }
}
fun <T> Iterator<T>.buffered() = BufferedIterator(this)
fun <T> BufferedIterator<T>.takeWhile(predicate: (T) -> Boolean): List<T> {
val list = ArrayList<T>()
if (reachedEnd) return list
current?.let {
if (predicate(it)) list += it
}
while (hasNext()) {
val next = next()
if (predicate(next)) list += next
else break
}
return list
}
fun main() {
val sequence = sequence {
var next = 0
while (true) {
yield(next++)
}
}
val iter = sequence.iterator().buffered()
for (i in 0..3) {
println(iter.takeWhile { it.toString().length <= i })
}
}
With this approach you can easily work even with infinite sequences.
I believe there is a way to accomplish what you want using the standard library. Limit the sequence first and then groupBy the number of digits.
val Int.numberOfDigits
get() = this.toString().length
sequenceOf(1,22,333).takeWhile{ it.numberOfDigits < 3 }.groupBy{ it.numberOfDigits }.values
If you want to avoid the eager evaluation of groupBy you could use groupingBy instead and then reduce potentially leaving the accumulator blank.
ardenit's answer seems like the best reusable approach. Since taking "chunks" of a sequence requires some state it doesn't seem likely something easily done in a purely functional manner. Delegating the state to a separate class enveloping the sequence makes sense.
Here's a small snippet showing what I ended up using. This assumes the sequence will not be empty and is (technically) infinite or further results aren't requested at some point.
class ChunkedIterator<T>(seq: Sequence<T>) {
private val it = seq.iterator()
var next: T = it.next()
fun next(predicate: (T) -> Boolean): List<T> {
val result = ArrayList<T>();
while (predicate.invoke(next)) {
result.add(next)
next = it.next();
}
return result
}
}
one way you could achieve this is by getting an iterator from your your original sequence and then building a new sequence out of it for each "take" -
val itr = seq.iterator()
val batch1 = itr.asSequence().takeWhile { predicate1(it) }.toList()
val batch2 = itr.asSequence().takeWhile { predicate2(it) }.toList()