I have managed to removeAll from a MutableList in-place successfully. This call modifies the receiver list to remove all elements matching the given predicate.
I would like to modify the receiver list by keeping only the first n elements but I cannot figure out how to do it since take and slice calls return a new collection.
Is there an in-place version of take or slice functions on MutableList?
An example of how I would like to use the new function: myList.keepFirst(5).
For keepFirst(n) and keepLast(n), you can get a subList(), then clear() it. This works because subList returns a "view" of the list, not a copy.
// suppose "l" is a MutableList
// keepFirst(5)
l.subList(5, l.size).clear()
// keepLast(5)
l.subList(0, l.size - 5).clear()
Another way this might be achieved:
private fun <T> MutableList<T>.keepFirst(n: Int) {
while (size > n) {
removeLast()
}
}
Related
I've been using kotlin arrow quite a bit recently, and I've ran into a specific use case that has me stuck.
Let's say I have a collection of some object that I want to convert to another datatype using a convert function. Let's also say that this convert function has an ability to fail-- but instead of throwing an exception, it will just return an Either, where Either.Left() is a failure and Either.Right() is the mapped object. What is the best way to handle this use case? Some sample code below:
val list: Collection<Object> // some collection
val eithers: List<Either<ConvertError, NewObject>> = list.map { convert(it) } // through some logic, convert each object in the collection
val desired: Either<ConvertError, Collection<NewObject>> = eithers.map { ??? }
fun convert(o: Object) : Either<ConvertError, NewObject> { ... }
Essentially, I'd like to call a mapping function on a collection of data, and if any of the mappings respond with a failure, I'd like to have an Either.Left() containing the error. And then otherwise, I'd like the Either.Right() to contain all of the mapped objects.
Any ideas for a clean way to do this? Ideally, I'd like to make a chain of function calls, but have the ability to percolate an error up through the function calls.
You can use Arrow's computation blocks to unwrap Either inside map like so:
import arrow.core.Either
import arrow.core.computations.either
val list: ListObject> // some collection
val eithers: List<Either<ConvertError, NewObject>> = list.map { convert(it) } // through some logic, convert each object in the collection
val desired: Either<ConvertError, Collection<NewObject>> = either.eager {
eithers.map { convert(it).bind() }
}
fun convert(o: Object) : Either<ConvertError, NewObject> { ... }
Here bind() will either unwrap Either into NewObject in the case Either is Right, or it will exit the either.eager block in case it finds Left with ConvertError. Here we're using the eager { } variant since we're assigning it to a val immediately. The main suspend fun either { } block supports suspend functions inside but is itself also a suspend function.
This is an alternative to the traverse operator.
The traverse operation will be simplified in Arrow 0.12.0 to the following:
import arrow.core.traverseEither
eithers.traverseEither(::convert)
The traverse operator is also available in Arrow Fx Coroutines with support for traversing in parallel, and some powerful derivatives of this operation.
import arrow.fx.coroutines.parTraverseEither
eithers.parTraverseEither(Dispatcheres.IO, ::convert)
This is a frequent one, what you're looking for is called traverse. It's like map, except it collects the results following the aggregation rules of the content.
So, list.k().traverse(Either.applicative()) { convert(it) } will return Either.Left is any of the operations return Left, and Right<List< otherwise.
How about arrow.core.IterableKt#sequenceEither?
val desired: Either<ConvertError, Collection<NewObject>> = eithers.sequenceEither()
In Kotlin I created an extension function to initialise a new ArrayList with custom items, like this:
fun <T> arrayListFrom(vararg item: T): ArrayList<T> {
return item.toMutableList() as ArrayList<T>
}
In this way I can easily create an arraylist like this
arrayListFrom(MyCustomItem(1), MyCustomItem(2))
... without creating a new-empty one, and adding all elements to it one by one
Kotlin has so many useful functions for collections, I cannot imagine I need this extension for easy arrayList initialisation, but couldn't find another simple way. Am I missing out on some useful Kotlin function here?
arrayListOf(items)
So you can just do
arrayListOf(MyCustomItem(1), MyCustomItem(2))
One more easy way to initialize ArrayList not exactly one line.
object: ArrayList<instanceType>() {
init{
add(instance1)
add(instance2)
}
}
Another nifty trick is to leverage Kotlin's basic Array type. Its constructor has an optional init function which allows for cool and simple inits like so val newArray = Array(2) { MyCustomItem(i+1) } which would make [ MyCustomItem(1), MyCustomItem(2) ].
To get an arrayList out of it, just add toCollection(ArrayList()) to the end like so
val newArrayList = Array(2) { MyCustomItem(i+1) }.toCollection(ArrayList())
and presto! The array gets created, and the items get sent to a destination arrayList. You can use the arrayList as needed, and it was done in only one line!
Disclaimer: It's probably slower given the transfer involved under the hood, so use with care of course!
I have a sortedBy{} statement which intends to sort a List by the length of the String elements:
var animals: List<String> = listOf("tiger", "cat", "dragon", "elephant")
fun strLength(it: String) = it.length
animals.sortedBy { strLength(it) }
animals.forEach {println(it)}
However it only prints the initial order. Any idea why?
You have to assign the output of sortedBy.
animals = animals.sortedBy { strLength(it) }
Because, like many other functions in Kotlin, sortedBy doesn’t mutate the input & honour immutability. So it returns a new collection. So it mitigates side-effects. Kotlin encourages this immutable approach. However, there are mutable counterparts of these collections if required.
sortedBy does not sort the list, instead it returns a new list which has the elements sorted. If you don't want a new list, simply use sortBy.
Kotlin provides Array.isArrayOf() for checking if an array is of a certain type.
It's used like this
if(object.isArrayOf<String>())
And defined like this
/**
* Checks if array can contain element of type [T].
*/
#Suppress("REIFIED_TYPE_PARAMETER_NO_INLINE")
public fun <reified T : Any> Array<*>.isArrayOf(): Boolean =
T::class.java.isAssignableFrom(this::class.java.componentType)
But it's only for Array. I need to check ArrayList.
I thought to change the signature like so.
#Suppress("REIFIED_TYPE_PARAMETER_NO_INLINE")
public fun <reified T : Any> ArrayList<*>.isArrayListOf(): Boolean =
T::class.java.isAssignableFrom(this::class.java.componentType)
but class.java.componentType is specific to Array
How can I check what type of ArrayList I have?
I should clarify, I only care if its one of 3 types, so I don't need a completely open-ended way of checking.
If you want to check the type of a list you can do:
when (list.firstOrNull()) {
is String -> { /*do something*/ }
is Int -> { /*do another thing*/ }
else -> { /*do something else*/ }
}
And if you need to use the list of a certain type you can use:
list.filterInstance</*the type you need*/>()
Hope this works for you.
You can't. Arrays are the only generic type for which this is possible (because they aren't really generic in the same sense, Kotlin just hides it).
The only thing you can do is look at its contents, but of course
that won't work for empty lists;
if a list contains e.g. a String, it could be ArrayList<String>, ArrayList<CharSequence>, ArrayList<Any>, etc.
For this purpose:
I need to direct it into the appropriate Bundle method. bundle.putStringArrayList(), bundle.putIntegerArrayList(), ect
neither should be a problem, I believe.
If the list is of one type then you can convert the list to array using: toTypedArray() and after you can then check the type using: isArrayOf
But this would be inefficient since you are converting the list to array, better if you can just directly guess or retrieved the first item of the list.
I want to know actually how .indices works and what is the main difference is between this two for loops.
for (arg in args)
println(arg)
or
for (i in args.indices)
println(args[i])
And what is use of withIndex() function
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
These are just different ways to iterate over an array, depending on what you need access to within the body of the for loop: the current element (first case), the current index (second case), or both (third case).
If you're wondering about how they work under the hood, you can just jump into the code of the Kotlin runtime (Ctrl+B in IntelliJ), and find out.
For indices specifically, this is pretty simple, it's implemented as an extension property that returns an IntRange which a for loop can then iterate over:
/**
* Returns the range of valid indices for the array.
*/
public val <T> Array<out T>.indices: IntRange
get() = IntRange(0, lastIndex)
As mentioned in the documentation indices is the index value from the list.
Returns an IntRange of the valid indices for this collection.
For more details refer to the documentation
indices returns IntRange(range of index from first to the last position) of collection, for example:
val array= arrayOf(10,20,30,40,50,60,70)
println("Indices: "+array.indices) // output: Indices: 0..6
In the question, both the loop are doing the same thing and just different way of iterate any collection(as #zsmb13 mentioned) but, the 2nd for loop does not create an iterator object as the first one does.
indices returns IntRange so you must check these useful methods of IntRange
When I wrote
for (i in 0..searchResultsTemp.items.size-1 )
{ " la-la-la "},
I wanted to work with each item of the collection, but I wouldn't use forEach operator, I would have access to the index of the collection item.
Then the built-in Android Studio helper suggested to me to automatically replace this expression with
for (i in searchResultsTemp.items.indices)
{ " la-la-la "}
It is the same.