How to get the item before the last item of an ArrayList in kotlin? - arraylist

How to get the item before the last item of an ArrayList in kotlin?
I have a list like
val myList = listOf("item1", "item2", "item3", "item4", "item5")
I want to get "item4" from myList

myList[myList.lastIndex - 1]
be sure to check if there are at least two items in the array before calling this
EDIT:
If you're using this often, you can define an extension function that acts similarly to last():
fun <T> List<T>.secondToLast(): T {
if (size < 2)
throw NoSuchElementException("List has less than two elements")
return this[size - 2]
}

You can do something like this:
myList.getOrNull(myList.lastIndex - 1)
This will not require additional checks, but will return null if the list is small or empty.

What about this:
myList.dropLast(1).lastOrNull()
This is not very efficient but it reads really nice.
Marko proposed a similar, more efficient way in the comments:
myList.takeLast(2).firstOrNull()

Related

MutableList of MutableLists in Kotlin: adding element error

Why I'm getting "java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0" while running next code??? :
val totalList = mutableListOf<MutableList<Int>>()
fun main() {
for (i in 0..15) {
for (j in 0..10) {
*some operations and calculations with **var element of type Int***
totalList[i].add(element)
}
}
}
I was thinking that in such case while iterating through 'j' it should add elements to mutableList[i], after this it should start adding elements to mutableList[i + 1] etc.... But instead I am recieving IndexOutOfBoundsException....
val totalList = mutableListOf<MutableList<Int>>()
All this does is create one list which is going to contain MutableList<Int> items. Right now, there's nothing in it (you've supplied no initial elements in the parentheses).
Skip forward a bit, and you do this:
totalList[0].add(element)
You're trying to get the first element of that empty list and add to it. But there is no first element (index 0) because the list is empty (length 0). That's what the error is telling you.
There's lots of ways to handle this - one thing you could do is create your lists up-front:
// create the 16 list items you want to access in the loop
// (the number is the item count, the lambda generates each item)
val totalList = MutableList(16) { mutableListOf<Int>() }
// then refer to that list's properties in your loop (no hardcoded 0..15)
for (i in totalList.indices) {
...
// guaranteed to exist since i is generated from the list's indices
totalList[i].add(element)
}
Or you could do it the way you are now, only using getOrElse to generate the empty list on-demand, when you try to get it but it doesn't exist:
for (i in 0..15) {
for (j in 0..10) {
// if the element at i doesn't exist, create a list instead, but also
// add it to the main list (see below)
totalList.getOrElse(i) {
mutableListOf<Int>().also { totalList.add(it) }
}.add(element)
}
}
Personally I don't really like this, you're using explicit indices but you're adding new list items to the end of the main list. That implicity requires that you're iterating over the list items in order - which you are here, but there's nothing enforcing that. If the order ever changed, it would break.
I'd prefer the first approach - create your structure of lists in advance, then iterate over those and fill them as necessary. Or you might want to consider arrays instead, since you have a fixed collection size you're "completing" by adding items to specific indices
Another approach (that I mentioned in the comments) is to create each list as a whole, complete thing, and then add that to your main list. This is generally how you do things in Kotlin - the standard library contains a lot of functional tools to allow you to chain operations together, transform things, and create immutable collections (which are safer and more explicit about whether they're meant to be changed or they're a fixed set of data).
for (i in 0..15) {
// map transforms each element of the range (each number) to an item,
// resulting in a list of items
val items = (0..10).map { j ->
// do whatever you're doing
// the last expression in the lambda is its resulting value,
// i.e. the item that ends up in the list
element
}
// now you have a complete list of items, add them to totalList
totalList.add(items)
}
(Or you could create the list directly with List(11) { j -> ... } but this is a more general example of transforming a bunch of things to a bunch of other things)
That example there is kinda half and half - you still have the imperative for loop going on as well. Writing it all using the same approach, you can get:
val totalList = (0..15).map { i ->
(0..10).map { j ->
// do stuff
element
}
}
I'd probably prefer the List(count) { i -> ... } approach for this, it's a better fit (this is a general example). That would also be better since you could use MutableList instead of List, if you really need them to be mutable (with the maps you could just chain .toMutableList() after the mapping function, as another step in the chain). Generally in Kotlin, collections are immutable by default, and this kind of approach is how you build them up without having to create a mutable list etc. and add items to it yourself

How do I make this Sequence lazy?

I was trying to generate all permutations of a list in Kotlin. There are a zillion examples out there which return a List<List<T>>, but my input list breaks those as they try to fit all the results in the output list. So I thought I would try to make a version returning Sequence<List<T>>...
fun <T> List<T>.allPermutations(): Sequence<List<T>> {
println("Permutations of $this")
if (isEmpty()) return emptySequence()
val list = this
return indices
.asSequence()
.flatMap { i ->
val elem = list[i]
(list - elem).allPermutations().map { perm -> perm + elem }
}
}
// Then try to print the first permutation
println((0..15).toList().allPermutations().first())
Problem is, Kotlin just seems to give up and asks for the complete contents of one of the nested sequences - so it never (or at least not for a very long time) ends up getting to the first element. (It will probably run out of memory before it gets there.)
I tried the same using Flow<T>, with the same outcome.
As far as I can tell, at no point does my code ask it to convert the sequence into a list, but it seems like something internal is doing it to me anyway, so how do I stop that?
As mentioned in the comments, you have handled the empty base case incorrectly. You should return a sequence of one empty list.
// an empty list has a single permutation - "itself"
if (isEmpty()) return sequenceOf(emptyList())
If you return an empty sequence, first will never find anything - your sequence is always empty - so it will keep evaluating the sequence until it ends, and throw an exception. (Try this with a smaller input like 0..2!)

The least amount of letters in a list of Palindromes

So the question is giving a BIG string, break it up, find the palindromes and then find the shortest length within those sets of palindromes. Here's the code
Main Function
fun main(){
val bigArray = "Simple, given a string of words, return the length of acdca the " +
"shortest valav words String will never be empty and you do not need dad to account for different data types."
println(leastP(bigArray))
}
The Custom Function
fun leastP(s: String): Int {
val sSplit = listOf(s.split(""))
val newArray = listOf<String>()
for (i in sSplit){
for (j in i.indices){
if (isPalindrome3(i[j])) newArray.plus(j)
}
}
return newArray.minOf { it.length }
}
private fun isPalindrome3(s: String): Boolean {
var i = 0
var j = s.length -1
while (i < j){
if (s[i++].lowercaseChar() != s[j--].lowercaseChar()) return false
}
return true
}
}
I get this error
Not sure whats going on or where I messed up. Any help is appreciated.
In addition to the array problem identified in Tenfour04's answer, the code has an additional problem:
split("") splits the string into individual characters, not just individual words. 
If you debug it, you'll find that isPalindrome3() is being called first on an empty string, then on "S", then on "i", and so on.
That's because the empty string "" matches at every point in the input.
The easiest fix is to call split(" "), which will split it at space characters.
However, that might not do exactly what you want, for several reasons: it will include empty strings if the input has runs of multiple spaces; it won't split at other white space characters such as tabs, newlines, non-breaking spaces, en spaces, etc.; and it will include punctuation such as commas and full stops. Splitting to give only words is harder, but you might try something like split(Regex("\\W") to include only letters, digits, and/or underscores. (You'll probably want something more sophisticated to include hyphens and apostrophes, and ensure that accented letters etc. are included.)
There's a further issue that may or may not be a problem: you don't specify a minimum length for your palindromes, and so words like a match. (As do empty strings, if the split produces any.) If you don't want the result to be 0 or 1, then you'll also have to exclude those.
Also, the code is currently case-sensitive: it would not count "Abba" as a palindrome, because the first A is in upper case but the last a isn't. If you wanted to check case-insensitively, you'd have to handle that.
As mentioned in a comment, this is the sort of thing that should be easy to test and debug. Short, self-contained functions with no external dependencies are pretty easy to write unit tests for. For example:
#Test fun testIsPalindrome3() {
// These should all count as palindromes:
for (s in listOf("abcba", "abba", "a", "", "DDDDDD"))
assertTrue(isPalindrome3(s))
// But these shouldn't:
for (s in listOf("abcbb", "Abba", "a,", "abcdba"))
assertFalse(isPalindrome3(s))
}
A test like that should give you a lot of confidence that the code actually works. (Especially because I've tried to include corner cases that would spot all the ways it could fail.) And it's worth keeping unit tests around once written, as they can verify that the code doesn't get broken by future changes.
And if the test shows that the code doesn't work, then you have to debug it! There are many approaches, but I've found printing out intermediate values (whether using a logging framework or simply println() calls) to be the simplest and most flexible.
And for reference, all this can be rewritten much more simply:
fun String.leastP() = split(Regex("\\W"))
.filter{ it.length >= 2 && it.isPalindrome() }
.minOfOrNull{ it.length }
private fun String.isPalindrome() = this == reversed()
Here both functions are extension functions on String, which makes them a bit simpler to write and to call. I've added a restriction to 2+ characters. And if the input is empty, minOfOrNull() returns null instead of throwing a NoSuchElementException.
That version of isPalindrome() isn't quite as efficient as yours, because it creates a new temporary String each time it's called. In most programs, the greater simplicity will win out, but it's worth bearing in mind. Here's one that's longer but as efficient as in the question:
private fun String.isPalindrome()
= (0 until length / 2).all{ i -> this[i] == this[length - i - 1]}
Your newArray is a read-only list. When you call plus on it, the function does not modify the original list (after all, it is read-only). The List.plus() function returns a new list, which you are promptly discarding by not assigning it to any variable or property.
Then it crashes because it is unsafe to call minOf on an empty list.
Two different ways to fix this:
Make the newArray variable a var and replace newArray.plus(j) with newArray += j. The += operator, when used on a read-only list that is assigned to a mutable var variable, calls plus() on it and assigns the result back to the variable.
Initialize newArray as a MutableList using mutableListOf() and replace newArray.plus(j) with newArray += j. The += operator, when used with a MutableList, calls add() or addAll() on the MutableList, so it directly changes the original instance.
I didn’t check any of your logic. I’m only answering the question about why it’s crashing.
But as Gidds points out, the logic can be simplified a ton to achieve the same thing you’re trying to do using functions like filter(). A few odd things you’re doing:
Putting the result ofstring.split("") in a list for no reason
Using "" to split your string so it’s just a list of one-character Strings instead of a list of words. And you’re ignoring punctuation.
Filling newArray with indices so minOf will simply give you the first index that corresponded with being a palindrome, so it will always be 0.
Here’s how I might write this function (didn’t test it):
fun leastP(s: String): Int {
return s.split(" ")
.map { it.filter { c -> c.isLetter() } }
.filter { isPalindrome3(it) }
.minOfOrNull { it.length } ?: 0
}

Grouping and ordering the top 3 values in a list

I have a list similar in concept to the following:
val selectedValues = listOf("Apple", "Apple", "Apple", "Grape", "Grape", "Cherry")
I need a way to group and sort it so that I get something like this:
Apple: 3
Grape: 2
Cherry: 1
I got a little bit of headway with this answer but I want it to be ordered by the count (most to least) and I can't seem to figure out how to get there.
I feel like the answer I posted gets me very close to what I want but I just need to figure out how to get it to work the way I need to and I'm just hitting a wall and need a little assistance as I'm still fairly new to Kotlin.
You could try something like this:
fun main(args : Array<String>) {
val selectedValues = listOf("Apple", "Apple", "Apple", "Grape", "Grape", "Cherry")
val solution = selectedValues
.groupBy { it }
.mapValues { it.value.size }
.toList()
.sortedByDescending { (_, value) -> value }
.toMap()
println(solution)
}
This will returns you
{Apple=3, Grape=2, Cherry=1}
which I think is (more or less) what you were after.
If you want to get a bit clever, you can use a Grouping which allows you to do a group-and-fold operation, i.e. group a bunch of things and then turn each group into a result - there's an eachCount() operation which turns each group into a count:
selectedValues.groupingBy { it }.eachCount()
.entries
.sortedByDescending { it.value }
.take(3) // they're still Entry pairs here, so you can toMap() if you need one
.run(::println)
Stefano's approach is the most general (and definitely what I'd go for if you're still starting out), but there are some specific tools for some scenarios too (possibly with some performance optimisation going on under the hood)
There's also the "build your own map of counts" approach:
// this is basically the same as "val newMap, stuff.forEach { update count in newMap }"
selectedValues.fold(mutableMapOf<String, Int>()) { counts, item ->
counts.apply { put(item, getOrDefault(item, 0) + 1) }
}
which I think is how the groupingBy().eachCount() combo above works - instead of creating a map of lists ("Apple"=["Apple", "Apple", "Apple"] and so on) and then generating a map of Ints from that, you just generate the Map<String, Int> from the original source list.
Whatever's easiest honestly, you only need to worry about efficiency when you're dealing with a lot of stuff, and Kotlin lets you write nice readable operations with its basic functions. Just thought I'd give you an idea of some of the options you have since you were struggling a little, in case it helps some things click!

Kotlin: How to check fast if list2 contains one item of list1

I am trying to check if a given list2 contains at least one item of given list1. If so, I would like to return true. My current approach is:
val object1 = Object1(body = listOf(1,2,3))
val object2 = Object1(body = listOf(2,5,7)
val doesList2ContainList1: Boolean = object2.body.intersect(object1.body).any() ==> should return true
I doubt that this is the fastest possible way to check if object2.body contains one item of object1.body because it checks the whole list, creates a new one and then executes any() right? But I want to instantly return true if one item of list2 is in list1. I have to make sure that this is really fast because object1.body and or object2.body can become really large.
I appreciate every help. Thank you
intersect internally uses a set, so O(n). You can make it faster though (even though not with better time complexity), if performance is really a concern. For example:
fun <T> Collection<T>.containsAny(other: Collection<T>): Boolean {
// Use HashSet instead of #toSet which uses a LinkedHashSet
val set = if (this is Set) this else HashSet(this)
for (item in other)
if (set.contains(item)) // early return
return true
return false
}