Get top N elements in SortedMap? - kotlin

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.

Related

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

Kotlin: remove identical adjacent members of an array

If I have the following array:
[1,1,1,2,2,1,1,1,1,2,2,3]
Is there any built in method in Kotlin which will filter out adjacent elements of the same value, resulting in:
[1,2,1,2,3]
It's important that the order is preserved.
P.S. My actual use case isn't integers, it's an object which implements equals.
I don't think there is a standard function to do this.
But it is easy to build one with mapOrNull:
fun <T : Any> Iterable<T>.removeAdjacent(): List<T> {
var last: T? = null
return mapNotNull {
if (it == last) {
null
} else {
last = it
it
}
}
}
There's a one-line solution, using zipWithNext():
list.zipWithNext().filter{ it.first != it.second }.map{ it.first } + list.last()
That creates a list of pairs of adjacent elements; we then filter out the identical pairs, and take the first of each remaining pair.  That will have omitted the last one, so we have to add that in separately.
That works with any element type, using the object's own notion of equality (via its equals() method); this includes nullable types (unlike another answer).  And it's stateless so ‘pure’ functional (which you may or may not consider a good thing!).
It handles one-element lists, but not empty lists; for completeness, you'd have to handle those separately.  And it would fit very neatly into an extension function:
fun <T> List<T>.compress() = when (isEmpty()) {
true -> listOf()
else -> zipWithNext().filter{ it.first != it.second }.map{ it.first } + last()
}
Functional solution using fold:
val result = listOf(1,1,1,2,2,1,1,1,1,2,2,3)
.fold(mutableListOf<Int>()) { currentList, currentItem ->
if (currentList.isEmpty()) { // Applies only to the very first item
mutableListOf(currentItem)
} else {
if (currentItem != currentList.last()) {
currentList.apply { add(currentItem) }
} else {
currentList
}
}
}

Сheck if map contains substring. Kotlin

I have a
val map = Map<String,String>
map.put("Nurseiyt","android")
I want to get a value by subString like:
map["Nurs"] should return "android"
is it possible?
Use kotlin.Collections, there are methods like filter.
Two things - it's better to use regular expression. So, you can even get better control what will be returned. And the second one, there can be more than one elements matched to that regex. So that's why I return list.
fun <T> substringKey(map: Map<String, T>, regex: Regex): List<T> {
return map.filter { it.key.contains(regex) }
.map { it.value }
}
If you want to use that notation you need to create your own map and override proper operator. What's worth to notice, you cannot return list of values then. So, in this case I just return first found value.
class SubstringMap<V> : HashMap<String, V>() {
override operator fun get(key: String): V? {
return this.entries.first { it.key.contains(key) }.value
}
}
fun main() {
val map = SubstringMap<String>()
map["Nurseiyt"] = "android"
println(map["Nurs"]) // "android"
}
And as the last thing - in kotlin you can create your own operator, like withKeyPart. This would be much better than overriding default operator (because I wouldn't expect that [] operator will work in different way than usual.
infix fun <V> Map<String, V>.withKeyPart(keyPart: String): List<V> {
return this.filter { it.key.contains(keyPart) }
.map { it.value }
}
and then call it like this:
fun main() {
val map = HashMap<String, String>()
map withKeyPart "KeyPart" // infix notation
map.withKeyPart("KeyPart") // standard call
}
Filtering the map, as per other answers, is simple and straightforward, but it doesn't scale well; it takes time proportional to the size of the map, so if the map could grow big, it could get very slow.
If you're always going to be searching for a leading substring, i.e. the start of a map key, then a better general solution is a data structure called a trie.  This lets you search efficiently, with just one lookup per character.
Of course, writing one from scratch may not be justified for your project.  But there are third-party implementations you could use, such as this one in Apache Commons.  Or see the answers to this question.
write top level function like this
fun HashMap<String, String>.getContainskeyValue(search: String): String?
{
var returnList = ArrayList<String?>()
this.keys.filter { it.contains(search) }.map {
returnList.add(this[it])
}
return returnList.first()
//if you want all keys 'contains' values just return list
/* Ex
map.put("Nurseiyt", "android")
map.put("Nurseiyt1", "androidone")
map.put("Nurseirt2", "andrrroidtwo")
val isContainsdata = map.getContainskeyValue("N")
println(" result " + containsdata)
output :result [andrrroidtwo, android, androidone]
*/
}
then call like this
val map = HashMap<String, String>()
map.put("Nurseiyt", "android")
val containsdata = map.getContainskeyValue("Nurs")
println(" result " + containsdata)
output
android

Find and return first match in nested lists in Kotlin?

Consider the following two classes:
class ObjectA(val objectBs: List<ObjectB>,
val otherFields: Any)
class ObjectB(val key: String,
val otherFields: Any)
The task is to find and return the first ObjectB with a certain key in a List of ObjectA.
Just achieving the goal is simple enough, but doing it nicely and efficiently seems rather tricky. I can't find anything like a "firstIn" or "findIn" function that would allow me to return another type than ObjectA when iterating on a list of ObjectA.
I have a few approaches, one of which looks pretty nice, but is very inefficient:
listOfA.mapNotNull {
it.objectBs.firstOrNull {
item -> item.key == wantedKey
}
}.firstOrNull()
The obvious inefficiency of this code is that it will not stop iterating through listOfA when it has found a match (and there can only be one match, just to be clear).
Approaches using filter or find have similar problems, requiring redundant iterations through at least one list of ObjectB.
Is there something in kotlins standard library that would cover such a use case?
If you want an elegant solution you can just do a flatMap like this:
val result: ObjectB? = listOfA.flatMap { it.objectBs }.firstOrNull { it.key == "myKey" }
If you want the efficiency you can do something like this:
val result: ObjectB? = objectAs.firstOrNull {
it.objectBs.map(ObjectB::key).contains("myKey")
}?.objectBs?.firstOrNull { it.key == "myKey" }
You can also wrap these in an Optional and put it in a function so the users of this operation can have a clean API:
fun List<ObjectA>.findFirstObjectB(key: String): Optional<ObjectB> {
return Optional.ofNullable(firstOrNull {
it.objectBs.map(ObjectB::key).contains(key)
}?.objectBs?.firstOrNull { it.key == key })
}
By converting all the nested elements to a flattened Sequence, they can be iterated lazily, and the overhead of unnecessary iteration is eliminated. This trick is done by combining asSequence and flatMap:
listOfA.asSequence().flatMap { it.objectBs.asSequence() }.find { it.key == wantedKey }
I wrote and ran the following code to ensure that it works as expected:
class PrintSequenceDelegate<out T>(private val wrappedSequence: Sequence<T>) : Sequence<T> by wrappedSequence {
override fun iterator(): Iterator<T> {
val wrappedIterator = wrappedSequence.iterator()
return object : Iterator<T> by wrappedIterator {
override fun next(): T =
wrappedIterator.next().also { println("Retrieving: $it") }
}
}
}
fun <T> Sequence<T>.toPrintDelegate() = PrintSequenceDelegate(this)
fun main() {
val listOfLists = List(3) { i -> List(3) { j -> "$i$j" } }
println("List of lists: $listOfLists")
val found = listOfLists.asSequence().toPrintDelegate().flatMap { it.asSequence().toPrintDelegate() }.find { it == "11" }
println(if (found != null) "Found: $found" else "Not found")
}
Output:
List of lists: [[00, 01, 02], [10, 11, 12], [20, 21, 22]]
Retrieving: [00, 01, 02]
Retrieving: 00
Retrieving: 01
Retrieving: 02
Retrieving: [10, 11, 12]
Retrieving: 10
Retrieving: 11
Found: 11
Thus we see that the elements (12) after the element found in the containing nested list are not iterated, neither are the following nested lists ([20, 21, 22]).
Nothing fancy, but it does the job efficiently:
fun findBWithKey(listOfA: List<ObjectA>, wantedKey: String): ObjectB? {
listOfA.forEach {
it.objectBs.forEach { item ->
if(item.key == wantedKey){
return item
}
}
}
return null
}
I also like to use map and first, but doing the given task efficiently gets unecessary hard using those extension functions.
A simple flatMap does the trick:
listOfA.flatMap { it.objectBs }.first { it.key == wantedKey }
This will basically give you an intermediate List with all of them combined so that you can easily query the first matching one.
I would look in to coroutines or sequences if performance is critical.
You can optimize your code slightly by using firstOrNull on listOfA as well:
listOfA.filterNotNull().firstOrNull { item ->
item.objectBs.firstOrNull { it.key == wantedKey } != null
}
I would do some performance testing to see if this code is causing any issues before making it overly complex.

Equivalent of Iterator.Remove in Kotlin HashMap?

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}