Is there a function to search in a ArrayList from a position? - kotlin

I read carefully the ArrayList documentation in Kotlin and apparently there is no way to search a value in ArrayList starting from a pointer. The alternative is write your own function iterating the right elements in ArrayList and testing the condition.
So I've programmed the following code:
fun <T> ArrayList<T>.findNext(cond: (T) -> Boolean, p: Int = 0): Int {
for (i in p..this.lastIndex)
if (cond(this[i])) return i
return -1
}
data class Person (
var name: String,
var age: Int
)
fun main() {
var v = arrayListOf<Person>()
v.add(Person("Paul", 22))
v.add(Person("Laura", 24))
v.add(Person("Paul", 50))
v.add(Person("Mary", 24))
println(v.findNext({it.name=="Paul"})) // 0
println(v.findNext({it.name=="Paul"}, 1)) // 2
println(v.findNext({it.name=="Paul"}, 3)) // -1
}
Is there something better than this?

You can avoid any intermediate collections:
inline fun <T> List<T>.findNext(p: Int = 0, cond: (T) -> Boolean) =
listIterator(p).withIndex().asSequence().find { cond(it.value) }?.let { it.index + p }
By swapping the arguments you can call it like this:
println(v.findNext {it.name=="Paul"}) // 0
println(v.findNext(1) {it.name=="Paul"}) // 2
println(v.findNext(3) {it.name=="Paul"}) // null

fun main() {
var v = arrayListOf<Person>()
v.add(Person("Paul", 22))
v.add(Person("Laura", 24))
v.add(Person("Paul", 50))
v.add(Person("Mary", 24))
println(v.findNext({ it.name == "Paul" },0))//IndexedValue(index=0, value=Person(name=Paul, age=22))
println(v.findNext({ it.name == "Paul" },2))//IndexedValue(index=2, value=Person(name=Paul, age=50))
println(v.findNext({ it.name == "Paul" },3))//null
}
private fun <T> List<T>.findNext(cond: (T) -> Boolean, position: Int): IndexedValue<T>? {
return withIndex().filter { it.index >= position }.firstOrNull { cond(it.value) }
}

maybe use withIndex and a filter ?
val arrayNames = listOf<String>("Paul", "Ann", "Paul", "Roger","Peter")
arrayNames.withIndex().filter {
it.value == "Paul" //value contains the original name
}.forEach{
println(it.index) //indext contains the position.
}
this will give you the output 0 and 2
for your case (person object instead of String) you will use
it.value.name == "Paul"

Related

Find the list item of Pairs with the minimum value

val index = listOf("abc", "def", "ghi", "jkl", "mno")
.mapIndexed { index, v ->
var t = 0
var p = 0
for (s in v) {
t += ("deh".get(p++).toInt() - s.toInt()).absoluteValue
}
Pair(index, v)
}
.minOf {
val iterator = iterator<Pair<Int, String>>(it)
if (!iterator.hasNext()) throw NoSuchElementException()
var minValue = iterator.next().second
while (iterator.hasNext()) {
val v = selector(iterator.next())
minValue = minOf(minValue, v)
}
return minValue
}
This is an alternative solution and works, but I am wondering if the solution can be done using mapOf as shown above?
val index = listOf("abc", "def", "ghi", "jkl", "jad", "jaa", "mno")
.mapIndexed { index, v ->
var t = 0
var p = 0
for (s in v) {
t += ("jac".get(p++).toInt() - s.toInt()).absoluteValue
}
Pair(index, t)
}.toSortedSet(compareBy { it.second })
.first()
I create a map of Pairs and I want to find the index of the map item where the Pair with the value (the second item in the pair) is the lowest value (minimum) of all the pairs. If possible, I would like to use the minOf function. The first example above will not compile because of bugs in the minOf function. Not sure how to iterate over the map of Pairs.
You can use minBy {} to get the minimum value from a collection, although often it's safer to use minByOrNull {} in case no minimal value can be computed (which could happen if the list is empty).
import kotlin.math.absoluteValue
fun main() {
val minElement = listOf("abc", "def", "ghi", "jkl", "jad", "jaa", "mno")
.minByOrNull { v ->
var t = 0
var p = 0
for (s in v) {
t += ("jac".get(p++).toInt() - s.toInt()).absoluteValue
}
t
}
println(minElement)
}
jad
Run in Kotlin Playground
If you also want to find the index of the minimal value, then you can use withIndex(), which will pair each list element with its index.
import kotlin.math.absoluteValue
fun main() {
val minIndexedElement = listOf("abc", "def", "ghi", "jkl", "jad", "jaa", "mno")
.withIndex() // adds the index to each element
.minByOrNull { (_, v) ->
var t = 0
var p = 0
for (s in v) {
t += ("jac".get(p++).toInt() - s.toInt()).absoluteValue
}
t
}
println(minIndexedElement)
}
IndexedValue(index=4, value=jad)
Run in Kotlin Playground
Another solution would be to extract the character codes from "jar" and from each item, and then to zip the two code lists. zip allows for a transform closure in which the calculation with the two codes can be made. After that sum() gives the wanted value.
data class Result(val index: Int, val string: String, val computedValue: Int)
val list = listOf("abc", "def", "ghi", "jkl", "jad", "jaa", "mno")
val result = list
.mapIndexed { idx, str ->
val codes1 = "jac".toCharArray().map { it.code }
val codes2 = str.toCharArray().map { it.code }
val computed = codes1.zip(codes2) { code1, code2 -> (code1 - code2).absoluteValue }.sum()
Result(idx, str, computed)
}
.minByOrNull { it.computedValue }
println(result) // Output: Result(index=4, string=jad, computedValue=1)
Instead of the helper data class Result a Triple instance could be used:
...
Triple(idx, str, computed)
}
.minByOrNull { it.third }
// Output: (4, jad, 1)
Or if the calculated value is not needed, it could be dropped like that:
...
?.let { it.first to it.second }
// Output: (4, jad)

Convert collection of numbers to range formatted string with Kotlin

I have a very large list of numbers. I need to pass this list of numbers as a URL query parameter. Since these lists can get so large, it could potentially cause the request URL to exceed the allowed length of a URL; also, it's a bit difficult to debug a string of sequential numbers (E.G. 1,2,3,..,500,782). To remedy these issues, I would like to convert the sequential number list string to one that is formatted using a range notation (E.G. -5..-3,1..500,782). How do I create this range notation string using Kotlin and how do I parse the string back to a collection of numbers also using Kotlin?
This will convert a Collection<Int> to a string that uses the "range notation" specified:
fun Collection<Int>.toRangesString(): String {
if (this.isEmpty()) {
return ""
}
if (this.size <= 2) {
return this.toSortedSet().joinToString(",")
}
val rangeStrings = mutableListOf<String>()
var start: Int? = null
var prev: Int? = null
for (num in this.toSortedSet()) {
if (prev == null) {
start = num
prev = num
continue
}
if (num != (prev + 1)) {
_addRangeString(rangeStrings, start!!, prev)
start = num
prev = num
continue
}
prev = num
}
if (start != null) {
_addRangeString(rangeStrings, start, prev!!)
}
return rangeStrings.joinToString(",")
}
private fun _addRangeString(rangeStrings: MutableList<String>, start: Int, prev: Int) {
rangeStrings.add(
when {
(start == prev) -> start.toString()
((start + 1) == prev) -> "${start},${prev}"
else -> "${start}..${prev}"
}
)
}
...and this will parse those range notated strings into a Set<Int>:
fun parseRangesString(str: String): Set<Int> {
if (str.isBlank()) {
return setOf()
}
val ranges = str.trim().split(",")
val numbers = mutableListOf<Int>()
for (range in ranges) {
if (range.contains("..")) {
val (start, end) = range.split("..")
numbers.addAll(start.toInt()..end.toInt())
continue
}
numbers.add(range.toInt())
}
return numbers.toSet()
}
...and, finally, even better than using a huge collection of numbers, you can use Kotlin's IntRange (or LongRange) class:
fun toIntRanges(str: String): Collection<IntRange> = _toRanges(str, ::_createIntRange)
fun toLongRanges(str: String): Collection<LongRange> = _toRanges(str, ::_createLongRange)
private fun <T : ClosedRange<*>> _toRanges(str: String, createRange: (start: String, end: String) -> T): Collection<T> {
if (str.isBlank()) {
return listOf()
}
val rangeStrs = str.trim().split(",")
val ranges = mutableListOf<T>()
for (rangeStr in rangeStrs) {
if (rangeStr.contains("..")) {
val (start, end) = rangeStr.split("..")
ranges.add(createRange(start, end))
continue
}
ranges.add(createRange(rangeStr, rangeStr))
}
return ranges.toList()
}
private fun _createIntRange(start: String, end: String) = IntRange(start.toInt(), end.toInt())
private fun _createLongRange(start: String, end: String) = LongRange(start.toLong(), end.toLong())

How can I take varying chunks out of a Kotlin Sequence?

If I have a Kotlin sequence, every invocation of take(n) restarts the sequence.
val items = generateSequence(0) {
if (it > 9) null else it + 1
}
#Test fun `take doesn't remember position`() {
assertEquals(listOf(0, 1), items.take(2).toList())
assertEquals(listOf(0, 1, 2), items.take(3).toList())
}
Is there an easy way of write say, another(n) such that
#Test fun `another does remember position`() {
assertEquals(listOf(0, 1), items.another(2).toList())
assertEquals(listOf(2, 3, 4), items.another(3).toList())
}
I suppose that I have to have something that isn't the Sequence to keep the state, so maybe what I'm actually asking for is a nice definition of fun Iterator<T>.another(count: Int): List<T>
Sequence does not remember its position, but its iterator does remember:
val iterator : Iterator<Int> = items.iterator()
Now all you need is something like take(n) but for Iterator<T>:
public fun <T> Iterator<T>.another(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
var count = 0
val list = ArrayList<T>(n)
for (item in this) {
list.add(item)
if (++count == n)
break
}
return list
}
What about this:
#Test
fun `another does remember position`() {
val items: Sequence<Int> = generateSequence(0) {
if (it > 9) null else it + 1
}
val (first, rest) = items.another(2)
assertEquals(listOf(0, 1), first.toList())
assertEquals(listOf(2, 3, 4), rest.another(3).first.toList())
}
fun <T> Sequence<T>.another(n: Int): Pair<Sequence<T>, Sequence<T>> {
return this.take(n) to this.drop(n)
}
To answer the last part of your question:
I suppose that I have to have something that isn't the Sequence to keep the state, so maybe what I'm actually asking for is a nice definition of fun Iterator.another(count: Int): List
One such implementation would be:
fun <T> Iterator<T>.another(count: Int): List<T> {
val collectingList = mutableListOf<T>()
while (hasNext() && collectingList.size < count) {
collectingList.add(next())
}
return collectingList.toList()
}
This passes your test if you use the iterator produced by the sequence:
#Test
fun `another does remember position`() {
val items = generateSequence(0) {
if (it > 9) null else it + 1
}.iterator() //Use the iterator of this sequence.
assertEquals(listOf(0, 1), items.another(2))
assertEquals(listOf(2, 3, 4), items.another(3))
}
To me what you've described is an iterator, since it's something that allows you to go over a collection or sequence etc. but also remember its last position.
NB the implementation above wasn't written to take into consideration what should happen for non-positive counts passed in, and if the count is larger than what's left to iterate over you'll be returned a list which has smaller size than n. I suppose you could consider this an exercise for yourself :-)
Sequence does not remember its position, but its iterator does remember:
val iterator : Iterator<Int> = items.iterator()
Unfortunately there is no take(n) for an iterator, so to use the one from stdlib you need to wrap iter into an Iterable:
val iterable : Iterable<Int> = items.iterator().asIterable()
fun <T> Iterator<T>.asIterable() : Iterable<T> = object : Iterable<T> {
private val iter = this#asIterable
override fun iterator() = iter
}
That makes itareble.take(n) remember its position, but unfortunately there is a of-by-one error because the standard .take(n) asks for one element too many:
public fun <T> Iterable<T>.take(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
if (this is Collection<T>) {
if (n >= size) return toList()
if (n == 1) return listOf(first())
}
var count = 0
val list = ArrayList<T>(n)
for (item in this) {
if (count++ == n)
break
list.add(item)
}
return list.optimizeReadOnlyList()
}
That can be fixed with a little tweak:
public fun <T> Iterable<T>.take2(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
if (this is Collection<T>) {
if (n >= size) return toList()
if (n == 1) return listOf(first())
}
var count = 0
val list = ArrayList<T>(n)
for (item in this) {
list.add(item)
//count++
if (++count == n)
break
}
return list
}
Now both of you tests pass:
#Test fun `take does not remember position`() {
assertEquals(listOf(0, 1), items.take2(2).toList())
assertEquals(listOf(0, 1, 2), items.take2(3).toList())
}
#Test fun `another does remember position`() {
assertEquals(listOf(0, 1), iter.take2(2).toList())
assertEquals(listOf(2, 3, 4), iter.take2(3).toList())
}
You could create a function generateStatefulSequence which creates a sequence which keeps its state by using a second sequence's iterator to provide the values.
The iterator is captured in the closure of that function.
On each iteration the seed lambda ({ i.nextOrNull() }) of the returned sequence starts off with the next value provided by the iterator.
// helper
fun <T> Iterator<T>.nextOrNull() = if(hasNext()) { next() } else null
fun <T : Any> generateStatefulSequence(seed: T?, nextFunction: (T) -> T?): Sequence<T> {
val i = generateSequence(seed) {
nextFunction(it)
}.iterator()
return generateSequence(
seedFunction = { i.nextOrNull() },
nextFunction = { i.nextOrNull() }
)
}
Usage:
val s = generateStatefulSequence(0) { if (it > 9) null else it + 1 }
println(s.take(2).toList()) // [0, 1]
println(s.take(3).toList()) // [2, 3, 4]
println(s.take(10).toList()) // [5, 6, 7, 8, 9, 10]
Try it out
Here is a nice definition of fun Iterator<T>.another(count: Int): List<T> as requested:
fun <T> Iterator<T>.another(count: Int): List<T> =
if (count > 0 && hasNext()) listOf(next()) + this.another(count - 1)
else emptyList()
As another workaround (similar to the suggestion by Willi Mentzel above) would be to create a asStateful() extension method that converts any sequence into a one that will remember the position, by wrapping it into an Iterable that always yields the same iterator.
class StatefulIterable<out T>(wrapped: Sequence<T>): Iterable<T> {
private val iterator = wrapped.iterator()
override fun iterator() = iterator
}
fun <T> Sequence<T>.asStateful(): Sequence<T> = StatefulIterable(this).asSequence()
Then you can do:
val items = generateSequence(0) {
if (it > 9) null else it + 1
}.asStateful()
#Test fun `stateful sequence does remember position`() {
assertEquals(listOf(0, 1), items.take(2).toList())
assertEquals(listOf(2, 3, 4), items.take(3).toList())
}
Try it here: https://pl.kotl.in/Yine8p6wn

How to simplify this in Kotlin

Is there a shorter way in Kotlin to write the following code:
private fun getMonth(monthText: String): Int {
var x = arrayOf("january", "february", "jumper").indexOf(monthText)
if (x >= 0)
return x
x = arrayOf("Januari", "Februari", "Maret").indexOf(monthText)
if (x >= 0)
return x
throw Exception("Not found")
}
I have to repeat the array search for many languages and want to avoid having to repeat duplicate code. Note: The returned value must indicate the month.
For performance reasons, you should better hash than search
private val monthByName = mapOf(
"january" to 0,
"february" to 1,
"jumper" to 2,
"Januari" to 0,
"Februari" to 1,
"Maret" to 2
)
private fun getMonth(monthText: String) = monthByName[monthText] ?: throw NoSuchElementException(monthText)
Maybe not the nicest way:
I first build a set with all possible mappings of month to index. I define the lists and process them with mapIndexed to get the index value. Afterwards I flatten those to get a set with Pair<String, Int>
val monthsToIndex by lazy {
val lists = listOf(
arrayOf("january", "february", "jumper"),
arrayOf("Januari", "Februari", "Maret")
);
lists.map { it.mapIndexed { i, s -> s to i } }.flatten().toSet()
};
The Search is now a find in that set + a null check to throw the exception:
private fun getMonth(monthText: String): Int {
return monthsToIndex.find { it.first == monthText }?.second ?: throw Exception("Not found")
}
A benefit of that approach would be a very concise place where you initialise all values - and only once.
Update:
You can also convert this into a map as Frank Neblung has suggested - this would definitely speed up the search:
val monthsToIndex by lazy {
val lists = listOf(
arrayOf("january", "february", "jumper"),
arrayOf("Januari", "Februari", "Maret")
);
lists.map { it.mapIndexed { i, s -> s to i } }.flatten().toMap()
};
private fun getMonth(monthText: String): Int {
return monthsToIndex[monthText] ?: throw Exception("Not found")
}
With an array of arrays:
private fun getMonth(monthText: String): Int {
val array = arrayOf(
arrayOf("january", "february", "jumper"),
arrayOf("Januari", "Februari", "Maret")
)
val months = array.firstOrNull { it.contains(monthText) }
if (months != null) return months.indexOf(monthText)
throw Exception("Not found")
}
The only inefficiency of this I can think of is that the array where monthText will be found, is scanned twice: once with contains() and then with indexOf().
I would probably hold some lists (or maps) containing your month texts and iterate over them, e.g.:
val englishMonths = sequenceOf("January", "February", "March" /*, ... */).mapIndexed { index, s -> s to index }.toMap()
val xxxMonths = sequenceOf("january", "february", "jumper"/*, ... */).mapIndexed { index, s -> s to index }.toMap()
// and all the other month mappings you need
Maybe you want to put those in an appropriate locale-mapping as well.
And your actual function would then just iterate over them:
fun getMonth(monthText : String) = sequenceOf(
// all the month lists that are relevant:
englishMonths, xxxMonths /*, ... */
)
.mapNotNull { it[monthText] }
.firstOrNull()
Which now would return null if no match was found. You can also just add ?: throw WhateverExceptionSuites() if you do not like a nullable type there.
I think something like this would work (Note: For brevity I only listed 3 months for 2 languages. There are in fact 12 months for multiple languages):
private fun getMonth(monthText: String): Int {
var x = arrayOf("january", "february", "march", "jumper", "Januari", "Februari", "Maret").indexOf(monthText)
if (x < 0)
throw Exception("Month not found")
return (x % 12) + 1
}
I would do it like this:
private fun getMonth(monthText: String): Int {
val a = arrayOf("january", "february", "jumper")
val b = arrayOf("Januari", "Februari", "Maret")
val result = a.zip(b)
.mapIndexed{idx, it -> if (it.first == monthText || it.second == monthText) idx else null }
.mapNotNull{it}.firstOrNull()
return result ?: throw Exception("Not found")
}

List<List<Char>> + List<Char> = List<Any>?

I have a below code which works.
class ListManipulate(val list: List<Char>, val blockCount: Int) {
val result: MutableList<List<Char>> = mutableListOf()
fun permute(sequence: List<Int> = emptyList(), start: Int = 0, count: Int = blockCount) {
if (count == 0) {
result.add(constructSequence(sequence))
return
}
for (i in start .. list.size - count) {
permute(sequence + i, i + 1, count - 1)
}
}
private fun constructSequence(sequence: List<Int>): List<Char> {
var result = emptyList<Char>()
for (i in sequence) {
result += list[i]
}
return result
}
}
However, when I change the result from MutableList to normal List, i.e.
var result: List<List<Char>> = emptyList()
// ...
result += constructSequence(sequence)
I got this error Type mismatch. Require: List<List<Char>>; Found: List<Any>
The full code as below
class ListManipulate(val list: List<Char>, val blockCount: Int) {
var result: List<List<Char>> = emptyList()
fun permute(sequence: List<Int> = emptyList(), start: Int = 0, count: Int = blockCount) {
if (count == 0) {
result += constructSequence(sequence)
return
}
for (i in start .. list.size - count) {
permute(sequence + i, i + 1, count - 1)
}
}
private fun constructSequence(sequence: List<Int>): List<Char> {
var result = emptyList<Char>()
for (i in sequence) {
result += list[i]
}
return result
}
}
Why result + constructSequence(sequence) would result in List<Any> instead of List<List<Char>>?
Is there a way I could still use the normal List> and not the mutable list?
CTRL + click on the + in IDEA, you'll see that it takes you to the following function:
/**
* Returns a list containing all elements of the original collection and then all elements of the given [elements] collection.
*/
public operator fun <T> Collection<T>.plus(elements: Iterable<T>): List<T> {
/* ... */
}
Which means that you add all the individual elements of elements to the receiver. That is, you'll add all T's to the List<List<T>>. Since List<T> is not T, you'll get List<Any> as a result.
The problem is that += is overloaded. If it sees an Iterable, Array or Sequence it behaves differently. You have to explicitly use plusElement() to achieve the behaviour you intend.
Consider the following code.:
class ListManipulate(val list: List<Char>, val blockCount: Int) {
var result: List<List<Char>> = emptyList()
fun permute(sequence: List<Int> = emptyList(), start: Int = 0, count: Int = blockCount) {
if (count == 0) {
result = result.plusElement(constructSequence(sequence))
return
}
for (i in start..list.size - count) {
permute(sequence + i, i + 1, count - 1)
}
}
private fun constructSequence(sequence: List<Int>): List<Char> =
List(sequence.size, { i -> list[sequence[i]] })
}
PS: I also took the liberty to update your constructSequence() to something more concise.
Btw: += uses addAll internally.
/**
* Returns a list containing all elements of the original collection and then all elements of the given [elements] collection.
*/
public operator fun <T> Collection<T>.plus(elements: Iterable<T>): List<T> {
if (elements is Collection) {
val result = ArrayList<T>(this.size + elements.size)
result.addAll(this)
result.addAll(elements)
return result
} else {
val result = ArrayList<T>(this)
result.addAll(elements)
return result
}
}
Side note: you can also do:
result.toMutableList().add(constructSequence(sequence))
It is fine to return a MutableList, the only difference really is that the List interface doesnt have the manipulation methods. Internally both are represented by an ArrayList
#SinceKotlin("1.1")
#kotlin.internal.InlineOnly
public inline fun <T> List(size: Int, init: (index: Int) -> T): List<T> = MutableList(size, init)