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()
Related
I am relatively new to Kotlin and I try to overcome a special case.
I am filtering a books store and want to verify that the length of the obtained list is exactly one unit shorter than the original one. Further I need to verify that the discarded element is under a specific state. Here is my example:
fun BookStoreVerified(bookStore: BookStore): Boolean {
val specialChapter = bookStore.stores
.flatMap { it.books }
.flatMap { it.chapters }.filter { it != null && it.state == Chapter.SPECIAL }
val total = bookStore.stores
.flatMap { it.books }
.flatMap { it.chapters }
.filterNotNull()
val finalChapters = book.stores
.flatMap { it.books }
.flatMap { it.chapters }
.filter { it != null && it.state.isCorrect }
return (finalChapters.size + specialChapterFigure.size == total.size) && (specialChapter.size == 1)
}
My question is if there is a smarter way to compute the above operation. I would like to know if ander a scope like filter, map can we make reference to the previous object? ( get the length of the original list for instance ?)
You have Books where each Book contains a list of Chapters. You want to partition chapters from all the books according to some criteria.
With this in mind the partition function can be useful:
data class Chapter(val state: String)
data class Book(val chapters: List<Chapter>? = null)
fun main() {
val books = listOf(
Book(),
Book(chapters = listOf(Chapter("a"), Chapter("SPECIAL"))),
Book(chapters = listOf(Chapter("c"), Chapter("d")))
)
val (specialChs, regularChs) = books
.flatMap { it.chapters ?: emptyList() }
.partition { it.state == "SPECIAL" }
println(specialChs) // [Chapter(state=SPECIAL)]
println(regularChs) // [Chapter(state=a), Chapter(state=c), Chapter(state=d)]
}
Now that you have specialChs and regularChs, you can check whatever invariants you want.
For example:
check(specialChs.size == 1 && specialChs.first().state ==
"SPECIAL")
Edit: It is possible to abstract away the existence of null chapters inside a Book:
data class Book(val chapters: List<Chapter>? = null) {
val safeChapters: List<Chapter>
get() = chapters ?: emptyList()
}
then in your code you can flatMap { it.safeChapters } instead of .flatMap { it.chapters ?: emptyList() }
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 check in if statement that condition is true for elements in a collection
fun main() {
var control = 20
val list = mutableListOf<Int>()
for (i in 1..20)
list.add(i)
while (true) {
control++
if (control % list[0] == 0 && control % list[1] == 0)
}
}
i only write 2 conditions for convenience
Not sure if you want to check if the condition is true for ALL the elements, or you just want to know which ONE in particular is true, or if ANY are true.
In the first scenario, where we want to know if ALL of them
val list = (1..20).toList() //a bit easier to create the list this way
val result = list.all { control % it == 0 }
println(result) //will print true if all of the elements are true, and false if at least one is false
The second scenario we can do a simple .map, to know each one individually.
val list = (1..20).toList()
val result = list.map { control % it == 0 }
println(result) //will print a list of type (true, false, true, etc) depending on each element
And if we want to check if ANY are true, we can do:
val list = (1..20).toList()
val result = list.any { control % it == 0 }
println(result) //will print true if any of the elements are true, and false if all of the elements are false
Edit since Todd mentioned none in the comments, I'll add a few other similar functions in case it would help others with similar questions.
Firstly, we don't actually need a list, all of these funcitons can work on the range directly (the ones above and the ones below)
val result = (1..20).all { it % 2 == 0 }
Other similar functions:
none - the opposite of all.
filter - will keep all elements for which the predicate is true.
filterNot - will keep all elements for which the predicate is false.
For example you can use filter:
fun main() {
val list = mutableListOf<Int>()
for (i in 1..20)
list.add(i)
list.filter { it % 2 === 1 }
.forEach { println(it) }
}
Change my code to this if anyone interested
fun main() {
var count = 20
var flag = false
val list = (1..20).toList()
while (true){
count++
if (list.all { count % it == 0 }){
println(count)
break
}
}
}
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
}
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.