How and when does kotlin let run? - kotlin

for all the examples on the internet i cant figure out when and how is kotlins let ran?
if(phones.size == 0){
phones.add("")
}
return phones[0]
so if phones list size is 0, we add empty string and return that instead.
Now how would one do same with let ?
phones.let {
return ""
}
does this work with size 0, or do i have to have null list?
do i need return keyword, if yes, where?
is the above fun always going to return empty string? or just when phones is null?
when is this let code block even ran?

Update:
val cakes = listOf("carrot", "cheese", "chocolate")
fun main(args: Array<String>) {
var cakesEaten = 0
while (cakesEaten < 3) { // 1
cakesEaten ++
val result = cakes?.let{
if(cakesEaten == 2) {
"HeyLo"
} else {
2
}
}
println("result value = $result")
when(result) {
is String -> println(" result variable is a String")
is Int -> println(" result variable is Integer")
}
}
}
result value = 2
result variable is Integer
result value = HeyLo
result variable is a String
result value = 2
result variable is Integer
Original post
If your 'phones' Object is a Nullable type,
val result = phones?.let{
// this block runs only if phones object is not null
// items can be accessed like it.size
// expression result will be returned. no need to mention return.
if(it.size == 0) {
it.add("")
it[0]
} else it.size
}
result value will be either it[0] or it.size and its type will be Any.
But if this the functionality you need you can check Markos solution.

If you're interested in how to write your logic in Kotlin's FP idiom, it doesn't involve let at all:
phones.takeIf { it.isEmpty() }?.add("")
return phones[0]
However, I don't find this idiom better than what you started out with.

Related

Mapping array elements using a function in Kotlin

New to Kotlin from Python. In Python, I can simply use the code below to pass each element of a List to a multiline function and return an iterator of the result.
countArr = list(map(countReps, arr))
In Kotlin, I found that I had to do the following. Am I missing something?
fun LetterCountI(str: String): String {
val arr = str.split(" ")
var transform:(String) -> Int = {countReps(it)}
val countArr = arr.map(transform)
val mxIndex:Int
var ans:String
if (countArr.max()!=1){
mxIndex = countArr.indexOf(countArr.max())
ans = arr[mxIndex]
} else {
ans = "-1"
}
return ans;
}
fun countReps(str: String): Int {
var m = mutableMapOf<Char, Int>()
var v:Int
for (c in str){
if (c in m.keys){
v = m[c]?:0
m.put(c,v+1)
} else {
m.put(c,1)
}
}
return m.values.max() ?: 0
}```
I'm having a bit of a hard time understanding your code, but one thing I can tell you is that you can replace
var transform:(String) -> Int = {countReps(it)}
val countArr = arr.map(transform)
with
val countArr = arr.map(::countReps)
In addition to the line you ask about, just about all of that code could be rewritten more concisely and idiomatically in Kotlin. For example:
fun String.wordWithMostRepeatedLetters()
= split(" ")
.associateWith{ it.maxRepeatedLetters() }
.filter{ it.value > 1 }
.maxByOrNull{ it.value }?.key ?: "-1"
fun String.maxRepeatedLetters()
= groupBy{ it }.map{ it.value.size }.maxOrNull() ?: 0
I've renamed the functions to try to explain what they give; replaced the countArr list with a map from each word to its count, so that you don't need to re-scan it to find the word resulting; and changed both functions to take a String receiver instead of a parameter. Then, because each variable was only used once, I removed them and made it all in-line, using an expression body for each function.
Some of those things don't always improve clarity, of course, especially for long functions — but I hope it demonstrates how concise Kotlin can be. (Hopefully without sacrificing maintainability. Which version would be easier to read? Which would be more likely to harbour subtle bugs?)
It's still not clear what the hard-coded "-1" return value indicates, though… If no word has any repeated letters, a null return would be more idiomatic. (Or it would be simpler just to return the first word, removing the filter() call, and returning null only if the string is blank.)

How to find last node that satisfies where predicate in singly linked list?

write a method "lastWhere" that accepts a function called "where" of type (T) -> Boolean. The method returns the last element of type T to which the "where" function applies. If no matching element is found, null is returned.
call the method "lastwhere" on the linked list below. Find the last game that is more than 10 euros.
So far I've got this Code going for me.
I assume the only important piece of Code I need to edit is the "fun lastWhere" for task number 1)
the second task wants me to implement a way on the main function to find the last Game that is cheaper than 10 Euros.
class LinkedList<T> {
data class Node<T>(val data: T, var next: Node<T>?)
private var first: Node<T>? = null
override fun toString(): String = first?.toString() ?: "-"
fun isEmpty() = first == null
fun addLast(data: T) {
if (first == null) {
first = Node(data, first)
return
}
var runPointer = first
while (runPointer?.next != null) {
runPointer = runPointer.next
}
runPointer?.next = Node(data, null)
}
fun lastWhere (where: (T) -> Boolean): T? { // "where" function needs to be implemented
if (isEmpty()) return null
else {
var runPointer = first
while (runPointer?.next != null ) {
runPointer = runPointer.next
}
return runPointer?.data
}
}
}
data class Game(val title: String, val price: Double)
fun main() {
val list = LinkedList<Game>()
list.addLast(Game("Minecraft", 9.99))
list.addLast(Game("Overwatch", 29.99))
list.addLast(Game("Mario Kart", 59.99))
list.addLast(Game("World of Warcraft", 19.99))
var test = list.lastWhere ({it.price >= 10.00}) // This is probably wrong too, since I haven't got task 1) working
println (test)
}
Would appreciate any help!
Since you only store a reference to first node, you don't have any choice but to start at first and iterate. you will also have to keep a reference to last item that satisfied the where predicate, and keep updating this reference with every iteration.
fun lastWhere (where: (T) -> Boolean): T? {
var runPointer = first
var item: T? = null // init item to null, if nothing is found we return null
while (runPointer != null ) {
// For every node, execute the where function and if it returns true
// then update the return value
if(where(runPointer.data)) { item = runPointer.data }
runPointer = runPointer.next
}
return item
}

incrementing hash map count in Kotlin

I have the function below. However, when I pass a string to it, I get the following error:
error: operator call corresponds to a dot-qualified call 'charCountMap.get(c).plus(1)' which is not allowed on a nullable receiver 'charCountMap.get(c)'. charCountMap.put(c, charCountMap.get(c) + 1)
private fun characterCount(inputString:String) {
val charCountMap = HashMap<Char, Int>()
val strArray = inputString.toCharArray()
for (c in strArray)
{
if (charCountMap.containsKey(c))
{
charCountMap.put(c, charCountMap.get(c) + 1)
}
else
{
charCountMap.put(c, 1)
}
}
}
The Kotlin Standard Library has groupingBy and eachCount for this purpose, you don't need to do any of this manually:
private fun characterCount(inputString:String) {
val charCountMap : Map<Char, Int> = inputString.groupingBy { it }.eachCount()
}
Note that I put the type on charCountMap for clarity, but it can be left off and inferred.
There is nice compute method in HashMap for this:
private fun characterCount(inputString:String) = hashMapOf<Char, Int>().also { charCountMap ->
inputString.forEach { charCountMap.compute(it) { _, v -> if (v == null) 1 else v + 1 } }
}
Both the other answers are correct. Todd's answer is right, you don't need to write a function for this. Just use the standard library. And if you are going to write a function that updates maps, Михаил Нафталь's suggestion to use compute() to handle updating existing values is also good.
However, if you're just doing this an an exercise, here are three suggestions to fix/improve your algorithm:
Instead of get(), use getValue(), which does not return null. It will raise an exception if the element does not exist, but you already checked for that.
Use the [] operator instead of put() (no need to, it's just nicer syntax).
You don't need to call toCharArray() because Strings are already iterable.
if (charCountMap.containsKey(c))
{
charCountMap[c] = charCountMap.getValue(c) + 1
}
else
{
charCountMap[c] = 1
}
Rewriting the whole thing using standard formatting:
fun characterCount(inputString: String): Map<Char, Int> {
val charCountMap = mutableMapOf<Char, Int>()
for (c in inputString) {
if (charCountMap.containsKey(c)) {
charCountMap[c] = charCountMap.getValue(c) + 1
} else {
charCountMap[c] = 1
}
}
return charCountMap
}

Multi-type ArrayList as function argument in Kotlin

I want to send an array list containing multiple types to a function (I know it is not a good practice, it is on purpose).
I don't know what type should I use for the 'numbers' argument of the function. And then how to iterate over it. I tried List but that needs a .
Thanks.
fun sum(numbers : ArrayList) : Double
{
var sum:Double = 0.0
for(i in 0 until numbers.itemCount)
{
var temp:Double = numbers.getItem(i).toDouble()
sum = sum + temp
}
return sum
}
fun main()
{
var ar = listOf("99", 1, 3.1)
println(sum(ar))
}
You can't do this without checking specific types. String.toDouble() is not the same function as Number.toDouble() even though they look the same. Your ArrayList type has to be Any to be able to accept both Strings and Numbers. Then you have to explicitly check the type. You will have to handle the case where something is not a String or a Number by throwing an exception.
You might as well make the type List<Any> instead of ArrayList<Any> to avoid the unnecessary restriction on input.
fun sum(numbers : List<Any>) : Double
{
var sum: Double = 0.0
for(item in numbers) {
val temp = when (item) {
is String -> item.toDouble()
is Number -> item.toDouble()
else -> error("Unsupported type")
}
sum += temp
}
return sum
}
There is an existing sumBy() function for lists, so you can simplify this code:
fun sum(numbers : List<Any>) : Double = numbers.sumBy {
when (it) {
is String -> it.toDouble()
is Number -> it.toDouble()
else -> error("Unsupported type")
}
}

Short circuiting operation in Kotlin sequences

I've been wondering if there is any chance of implementing sequence which executes some operation n times using map operator, but can terminate that execution during processing on the 'go'? Here is an imperative code which I'm trying to implement using Kotlin sequences:
val offers = mutableListOf<String>()
for (pageNumber in FIRST_PAGE_NUMBER until numberOfPages) {
val offersInPage = findByPage(query, pageSize, pageNumber)
offers.addAll(offersInPage)
if(offersInPage.size == 5)
break
}
The main thing is that I would like to hang up processing any further requests to external service when response from the previous one meets some conditions.
When trying to implement it in more declarative way I ended up with something like this:
IntArray(numberOfPages)
.asSequence()
.map { findByPage(query, pageSize, it) }
.takeWhile { it.size == 5 }
.flatten()
.toList()
But the findByQuery method is invoked n times and then the result is filtered. Is there any operator which help me implement something like terminating that lazy operation once given condition is met?
The sequence works exactly as you expected. It performs on map checks the takeWhile condition and does only continue if it is true.
One problem could be IntArray(numberOfPages). This creates an array of 0 of the size numberOfPages. So you iterate over a sequence of 0 and not the page numbers. You can simply change this to the for loop condition.
(FIRST_PAGE_NUMBER until numberOfPages)
.asSequence()
Another problem could be the takeWhile. In the for loop you stop after the first element with a size == 5. But in the sequence you stop before the first element with a size != 5. The simplest solution for this problem is to find another condition that would break the loop before offers.addAll(offersInPage) will be executed. If this is not possible you can use something like this:
fun <T> Sequence<T>.takeWhileEndInclusive(predicate: (T) -> Boolean) = object : Sequence<T> {
val sequence = this#takeWhileEndInclusive
override fun iterator() = object : Iterator<T> {
val iterator = sequence.iterator()
var nextState: Int = -1
var nextItem: T? = null
var found = false
private fun calcNext() {
if (!found && iterator.hasNext()) {
val item = iterator.next()
if (!predicate(item)) {
found = true
}
nextState = 1
nextItem = item
return
}
nextState = 0
}
override fun next(): T {
if (nextState == -1)
calcNext()
if (nextState == 0)
throw NoSuchElementException()
#Suppress("UNCHECKED_CAST")
val result = nextItem as T
nextItem = null
nextState = -1
return result
}
override fun hasNext(): Boolean {
if (nextState == -1)
calcNext()
return nextState == 1
}
}
}
This is a slightly adjusted version of the default takeWhile implementation.
Usage:
(FIRST_PAGE_NUMBER until numberOfPages)
.asSequence()
.map { findByPage(query, pageSize, it) }
.takeWhileEndInclusive { it.size != 5 }
.flatten()
.toList()