What is .indices meaning in kotlin? - kotlin

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.

Related

Kotlin MutableList.take in place?

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

Why do Kotlin Sets have indices?

I'm curious as to why Kotlin Sets have indices. You can access elements by using mySet.elementAt(index). No other language I know has this feature. Is the feature any useful if sets aren't supposed to be ordered and yet they have indices? Also, doesn't this feature make Sets in Kotlin slower than other Sets in other languages?
Set has the elementAt method, not because it is based on indices (so it is not "slower than other languages" just because it has this method), but because it implements Iterable<T>. elementAt is an extension function on Iterable<T>:
fun <T> Iterable<T>.elementAt(index: Int): T
What a Set is based on depends on which concrete implementation of Set you use (Set is just an interface). HashSet is based on a hash table, for example.
So Set gets this elementAt method "for free" just because it implements Iterable<T>. The only way for it to not have elementAt is to not implement Iterable<T>, but that would mean you can't iterate over a Set. That's not very useful, is it? Also, as I'll talk about later, elementAt does have its uses.
Since elementAt is an extension function on Iterable<T>, all it can do really, is to ask to the iterator to give it n elements, and return the last element. This is how it is implemented.
public fun <T> Iterable<T>.elementAt(index: Int): T {
if (this is List)
return get(index)
return elementAtOrElse(index) { throw IndexOutOfBoundsException("Collection doesn't contain element at index $index.") }
}
...
public fun <T> Iterable<T>.elementAtOrElse(index: Int, defaultValue: (Int) -> T): T {
if (this is List)
return this.getOrElse(index, defaultValue)
if (index < 0)
return defaultValue(index)
val iterator = iterator()
var count = 0
while (iterator.hasNext()) {
val element = iterator.next()
if (index == count++)
return element
}
return defaultValue(index)
}
If your Set does not have a particular order (e.g. HashSet), then its iterator will return elements in no particular order either, so using elementAt(x) is not very meaningful. On the other hand, if you are using an ordered set, like a LinkedHashSet (this is what setOf and mutableSetOf creates), then using elementAt does make sense.
Also note that elementAt does have O(n) time, but that doesn't mean that accessing the set using the set's methods (e.g. contains) also has O(n) time. That also depends on which concrete implementation of Set you use. Both LinkedHashSet.contains and HashSet.contains are O(1) time.

How to use combinators with zip iterable

The zip that accepts iterable is turning my object to Object[] vs the merge. After the zip, I cannot perform other transformation because I lost my object type. Is this the same concept as the stream's reduce combiner? Just wondering how to properly use it. Thanks.
final List<Object[]> list = Flux
.zip(List.of(Mono.just("hello"), Mono.just("world")), objects -> objects)
.collectList().block();
final List<String> strings = Flux
.merge(List.of(Mono.just("hello"), Mono.just("world")))
.collectList().block();
It's an API limitation at present since the generic type of the Iterable's Publisher isn't captured, so that type information isn't available to you in the method. This means you'll unfortunately have to do something unsafe if you want to keep the type information here.
The most trivial change to your current code to get a List<String[]> would be the following:
final List<String[]> list = Flux
.zip(List.of(Mono.just("hello"), Mono.just("world")), objects -> Arrays.stream(objects).toArray(String[]::new))
.collectList().block();
...but of course, you do lose your type safety.
Depending on your use case (generally speaking, if you combinator can combine elements one at a time rather than all in one go), you may also be able to use Flux.zip() in a reducer:
List<Flux<String>> l = new ArrayList<>();
l.add(Flux.just("hello", "me"));
l.add(Flux.just("world", "hungry"));
final List<String> strings = Flux.fromIterable(l)
.reduce((a, b) -> Flux.zip(a, b, (x, y) -> x + ", " + y))
.flatMap(x -> x.collectList())
.block();
It's not equivalent, but may be a type-safe alternative depending on what you need.
Looks like the first argument to the zip function takes a Iterable<? extends Publisher<?>> the question marks mean it can take whatever object.
and its second argument Function<? super Object[],? extends O> is a function that the first argument is "something" that is an object in an array, and the second argument is "something" that extends a concrete type.
So sadly you will be getting a Object[] it's how it is written. You can cast your objects to the correct.
I have never used it before but i played around with it a bit.
final Flux<String> helloWorldString = Flux.zip(List.of(Mono.just("hello"), Mono.just(" "), Mono.just("world")), objects -> {
StringBuilder value = new StringBuilder();
for (var object : objects) {
value.append((String) object);
}
return value.toString();
});
As it is a combinator i think its purpose is to take any objects[] and build a concrete type out if it.

sortedBy() selector not sorting List

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.

Check type of ArrayList in Kotlin

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.