Lambda expression inside Kotlin String - kotlin

My code:
class Team (val name: String, val aggressive: Boolean = true) {
val attendance: Int
init {
if (aggressive){
attendance = 25000
} else {
attendance = 20000
}
}
}
...
fun chooseTeam() {
val homeTeam = Team(name = "Everton")
println("the home team is $homeTeam.aggressive so they are ${if ($homeTeam == "aggressive") "angry" else "timid" }")
}
I'm trying to assign the value of the lambda string based on if $homeTeam.aggressive is true or not.
However I'm getting red lines all over the lambda so obviously the syntax seems off. Can someone tell me whats wrong with the code?

What you actually mean is a string template and not a lambda. You must have confused it because both use curly brackets {...}.
You can either do it like this:
fun chooseTeam() {
val homeTeam = Team(name = "Everton")
// 1) no dollar sign before homeTeam in the comparison
// 2) you need to compare to homeTeam.aggressive and not homeTeam
println("the home team is ${homeTeam.name} so they are ${if (homeTeam.aggressive) "angry" else "timid" }")
}
Or better, you assign the mapping (Boolean to String) to a variable first which increases the readability.
fun chooseTeam() {
val homeTeam = Team(name = "Everton")
val adjective = if (homeTeam.aggressive) "angry" else "timid"
println("the home team is ${homeTeam.name} so they are $adjective")
}

The correct way
fun chooseTeam() {
val homeTeam = Team(name = "Everton")
// 1) no dollar sign before homeTeam in the comparison
// 2) in kotlin if you declare constructor parameter as val they also becomes property of class which you can access like this homeTeam.aggressive
// 2) if you want the name of team, just use ${homeTeam.name} instead $homeTeam.aggression
println("the home team is $homeTeam.aggression so they are ${if(homeTeam.aggressive) "angry" else "timid"}")
}
Hope this helps.
Edit :
More detailed explanation is given by #Willi Mentzel

Related

Kotlin (if else)

I'm new to kotlin. I need to make a calculator using enam. At the end of the code, there is a function that reads the action and returns the enam. The function works but, "if else" doesn't. Need console calculator, not in android studio.Please help!
All code from file
import java.lang.Exception
fun main() {
print("first number: ")
val first : Int = readNumber()
print("second number: ")
val second : Int = readNumber()
println("Enter action: +, -, *, /")
val sum : Int = first + second
println("Answer: $sum ")
}
fun readNumber() :Int{
return try {
readLine()!!.toInt()
}
catch (e:Exception){
0
}
}
enum class Action(val symbol : String){
PLUS("+"),
MINUS("-"),
MULTIPLY("*"),
DIVIDE("/");
}
fun defineAct(){
val pls = Action.PLUS
val mns = Action.MINUS
val mlt = Action.MULTIPLY
val dvd = Action.DIVIDE
if (val = pls){
sum = pls + mns
println("Ответ: ${sum}")
else if (val - mns){
sum = pls - mns
}
}
Your are using val keyword inside if-else. You could change it accordingly your requirements. Hope it will solve your problem.

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
}

How can I change this to use "for loop" instead of `forEach`

I'm struggling to change it to use a for loop and still do the same thing.
The program is supposed to read a file with some flights and this specific part of the program needs to read the file using two different days that the user inputs then it needs to show how many passengers there are per flight and each day.
And how it's done now works but I'm trying to change it to use a for loop as I said before but doesn't work because I don't know how to do the same thing as map does but only in the fun interval.
fun interval(reservas: List<Reservas>, dayInferior: Int, daySuperior: Int) {
val map = mapReservas(reservas)
for(day in dayInferior..daySuperior) {
map.forEach {
val reservasNum = it.key.first
val reservasDay = it.key.second
val reservasCount = it.value.count()
if (reservasDay == day) {
println("$reservasNum has $reservasCount passengers on day $day")
}
}
}
println()
println("Press Enter")
readLine()
}
fun mapReservas(reservas: List<Reservas>): Map<Pair<String, Int>, List<Reservas>> {
val map = mutableMapOf<Pair<String, Int>, MutableList<Reservas>>()
for (reserva in reservas) {
val key = reserva.numFlight to reserva.day
val list = map[key] ?: mutableListOf()
list.add(reserva)
map[key] = list
}
return map
}
All your code can be replaced only with one function.
fun interval(reservas: List<Reservas>, dayInferior: Int, daySuperior: Int) {
reservas.groupBy { reserva -> reserva.day to reserva.numFlight }
.filter { (key, _) -> key.first in dayInferior..daySuperior }
.forEach { (key, reservas) ->
val (reservasNum, reservasDay) = key
val reservasCount = reservas.count()
println("$reservasNum has $reservasCount passengers on day $reservasDay")
}
println()
println("Press Enter")
readLine()
}
Explaining:
As I undestand, at first you trying to group all your Reservas by day and numFlight. It can be done via one function groupBy where you pass pair of day and numFlight.
Filter all Reservas by day. It can be done by checking if day belongs to range dayInferior..daySuperior (operator in).
Print all reservas by using forEach.
Other things
Destructing declarations
val reservasNum = it.key.first
val reservasDay = it.key.second
same as
val (reservasNum, reservasDa) = it.key
Omitting one unused parameter in lamda:
.filter { (key, _) -> ... }
If you iterate with a for loop over the Map each element is a Pair. If you write (pair, list) you destructure each Pair which itself consists of a Pair and a List.
fun interval(reservas: List<Reservas>, dayInferior: Int, daySuperior: Int) {
val map = mapReservas(reservas)
for(day in dayInferior..daySuperior) {
for((pair, list) in map) {
val reservasNum = pair.first
val reservasDay = pair.second
val reservasCount = list.count()
// ...
}
}
// ...
}
Maybe this makes it more clear:
for(outerPair in map){
val (innerPair, list) = outerPair
val reservasNum = innerPair.first
val reservasDay = innerPair.second
val reservasCount = list.count()
// ...
}
I left this function (mapReservas) untouched intentionally, because maybe you are using it somewhere else. But you can improve it right away by using Type aliases (since Kotlin 1.1).
typealias FlightNum = String
typealias Day = Int
fun mapReservas(reservas: List<Reservas>):
Map<Pair<FlightNum, Day>, List<Reservas>> {
// ...
}
As you can see the code becomes much more readable if you use the destructure syntax and Type aliases.

Access to external value inside apply

Short example:
class MyClass {
val someName = "want this value"
val someOther = SomeOther().apply{ someName = someName }
// other stuff below
}
SomeOther will apply the value of its own someName to someName, so value application makes no difference (x=x).
Q: How can I access external someName ("want this value") being inside apply?
UPDATE
I have further doubts related to suggestion to use this.someName=someName, below 2 code snippets, the first one works as expected, surprisingly the second fails with similar behavior as described.
First
fun main(args: Array<String>) {
class SomeOther {
var someName: String? = null
}
val someName = "want this value"
print(SomeOther().apply { this.someName = someName }.someName) // works!
}
Second
class SomeOther {
var someName: String? = null
}
class MyClass {
val someName = "want this value"
val someOther = SomeOther().apply { this.someName = someName }
fun go() = print(someOther.someName)
}
fun main(args: Array<String>) = MyClass().go() // prints null
Q: Where's the difference?
You could use the also-function instead. It is equivalent to apply, except it will bind your object to it instead of this:
val someName = "want this value"
val someOther = SomeOther().also { it.someName = someName }
The also-function was added to Kotlin 1.1 specifially for when you do not want to shadow this from the outer scope.
using this reference expression as below:
val someOther = SomeOther().apply { someName = this#MyClass.someName }
// reference to the outer class ---^
AND the T.apply function is a convenient way to applying Builder Design Pattern, in this way you never need using this or additional parameter at all, for example:
val foo = Foo().apply {
//v--- no need using `this` or any addition parameters
foo = "bar"
fuzz = "buzz"
}
class Foo {
lateinit var foo: String;
lateinit var fuzz: String
}
Edit
you can assuming apply(lambda) which will apply an anonymous class of Function2<T,ARG,T> instance, then you know why immediately?
in your first approach it looks like as below:
val lambda: Function2<SomeOther, String, SomeOther> = { thisRef, arg ->
thisRef.someName = arg;
// ^--- parameter is used in lambda
thisRef
}
val someName = lambda(SomeOther(), "want this value").someName
println(someName)
in your second approach it looks like as below:
class MyClass {
val lambda: Function2<SomeOther, MyClass, SomeOther> = { thisRef, arg ->
// the parameter `arg` is never used in lambda ---^
thisRef.someName = thisRef.someName
// ^--- it use thisRef's someName rather than arg's
thisRef
}
val someOther = lambda(SomeOther(), this)
}
You can access out of apply like that
class SomeOther {
var someName: String? = null
}
class MyClass {
val someName = "want this value"
val someOther = SomeOther().apply { this.someName = this#SomeOther.someName }
fun go() = print(someOther.someName)
}
Try this:
val someName = "want this value"
val otherName = SomeOther().apply { this.someName = someName }
// internal someName ---^ ^
// external someName ---^
print(otherName.someName) // >>> want this name