I can't figure out how to get the largest element in loop - kotlin

I don't understand the loop in the following content.
There is a sequence of integer numbers (which ends with the number 0). Find the largest element of this sequence.
The number 0 itself is not included in the sequence. It serves only as a sign that there are no more numbers.
I tried various methods, but they did not solve the problem.
The way to exit the loop at 0 is to use
While(i ! = 0){}
Here is the code we created.
fun main(args: Array<String>) {
val scanner = Scanner(System.`in`)
var count = scanner.nextInt()
do {
var input = scanner.nextInt()
while(input != 0 || count != 0) {
if (count < input){
count = input
}
}
}while(input != 0 || count != 0)
println(count)
}

You can use maxOf() for example:
fun readLn() = readLine()!!
fun readIntOrStopLoopWithZero() = readLn().toIntOrNull() ?: 0
fun readMaxInteger() {
var maxInput: Int = 0
//getting first user input
var userInput = readIntOrStopLoopWithZero()
while (userInput!= 0) {
//calc max
maxInput = maxOf(userInput, maxInput)
//getting user input again.
userInput= readIntOrStopLoopWithZero()
}
if (maxInput == 0)
//In case we get 0 as first input.
print("max value not found")
else
print("Max input is : $maxInput")
}
readMaxInteger()
Input:
1
2
0
output:
Max input is 2
Input:
1
0
output:
Max input is 1
Input:
0
output:
max value not found
Input:
1
2
3
Word
output:
Max input is : 3

Problems:
You've over-complicated it by creating a nested loop.
You're checking input != 0 to exit either of the loops, so it will exit immediately on the first element.
So fixing these problems, you have:
fun main(args: Array<String>) {
val scanner = Scanner(System.`in`)
var count = 0 // start with 0 for simplicity
do {
val input = scanner.nextInt()
if (count < input) {
count = input
}
} while (input != 0)
println(count)
}
And here's an alternate solution. Make a Sequence so it becomes an Iterable and you can use maxOrNull() on it:
fun main(args: Array<String>) {
val sequence = sequence {
val scanner = Scanner(System.`in`)
do {
val input = scanner.nextInt()
yield(input)
} while (input != 0)
}
println(sequence.maxOrNull() ?: 0)
}

I wanted to finish within a single function, so I chose the following.
fun main(args: Array<String>) {
val scanner = Scanner(System.`in`)
var count = 0 // start with 0 for simplicity
do {
val input = scanner.nextInt()
if (count < input) {
count = input
}
} while (input != 0)
println(count)
}
Thanks to everyone who responded!

Related

Find any 2 objects in a list matching criteria in one iteration

I have a list of custom objects. I would like to retrieve any 2 that meet 2 different conditions.
E.g.
Assume
class Person {
val age: Int
//etc
}
And I have a List<Person>. I would like to get 2 Person that have age == 21 or age == 31
I know I can do a loop for that and break using some flag once I find both, but I was wondering if there was some more Kotlin idiomatic way for this instead of:
var p1: Person?
var p2: Person?
list.forEach{
if(it.age == 21) {
p1 = it
}
else if(it.age == 31) {
p2 = it
}
if(p1 != null && p2 != null) break
}
I can only think of 2 possible solutions:
An inefficient but simple way (2 partial iterations)
val list = listOf(Person(21), Person(23), Person(31), Person(20))
var p1: Person? = list.find{ it.age == 21 }
var p2: Person? = list.find{ it.age == 31 }
This will iterate twice, but will stop the iteration as soon as a result is found.
Based on your own solution, only iterates once
val list = listOf(Person(21), Person(23), Person(31), Person(20))
var p1: Person? = null
var p2: Person? = null
//forEach is a method, not an actual loop, you can't break out of it, this is a bypass (you can also enclose it into a fun and return from there to act as a break)
run loop#{
list.forEach {
when {
p1 == null && it.age == 21 -> p1 = it
p2 == null && it.age == 31 -> p2 = it
}
if (p1 != null && p2 != null) return#loop
}
}
The p1 == null check will make is so that we don't reasign the same value multiple times, thus getting the first result. It is better even if we aren't necessarily looking for the first result, since it won't make multiple assignments.
Ps: both solutions will leave you with nullable p1 and p2, you must remember to deal with that accordingly afterwards.
No, you are trying to do quite a specific operation, so I don't think there is a built-in function and you will need to implement it yourself. Since you want to only iterate the list once, I think it would be best to:
Implement it with traditional list iteration
Wrap it up in a generic extension function for idiomatic Kotlin use
data class Person(val age: Int)
fun <T> Iterable<T>.findFirstTwo(
predicateA: (T) -> Boolean,
predicateB: (T) -> Boolean
): Pair<T, T> {
var first: T? = null
var second: T? = null
val iterator = iterator()
var remainingPredicate: (T) -> Boolean = { false }
while (first == null && iterator.hasNext()) {
val item = iterator.next()
when {
predicateA(item) -> {
first = item
remainingPredicate = predicateB
}
predicateB(item) -> {
first = item
remainingPredicate = predicateA
}
}
}
while (second == null && iterator.hasNext()) {
val item = iterator.next()
if (remainingPredicate(item)) {
second = item
}
}
require(first != null && second != null) { "Input does not satisfy predicates" }
return Pair(first, second)
}
fun main() {
val people = (1..20).map { Person(it) }
val (seven, twelve) = people.findFirstTwo({ it.age == 12 }, { it.age == 7 })
val (one, nineteen) = people.findFirstTwo({ it.age == 1 }, { it.age == 19 })
val error = try {
people.findFirstTwo({ it.age == 100 }, { it.age == 3 })
} catch (e: Exception) {
e.message
}
println(one)
println(seven)
println(twelve)
println(nineteen)
print(error)
}
Output:
Person(age=1)
Person(age=7)
Person(age=12)
Person(age=19)
Input does not satisfy predicates
As a lot of people mentioned that find will probably be the best solution for this kind of problem but we will have to use it twice, once for 21 and other time for 31.
even though find will return a single object: <T> Iterable<T>.find(predicate: (T) -> Boolean): T?
it doesn't mean we can't find another one in the same iteration.
for example:
var ageParam = -1
var p1: Person? = null
var p2: Person? = list.find { person ->
p1?.let {
//p1 - the first person found, now we will iterate until age == ageParam(21/31)
person.age == ageParam
} ?: run {
//p1 - the first person hasn't found yet.
if (person.age == 21) {
p1 = person // saving person
ageParam = 31 // setting next condition to test
} else if (person.age == 31) {
p1 = person
ageParam = 21
}
false // it's false in order to keep iterate after P1 found
}
}
Your solution is close to optimal. But it doesn't take first objects meeting criteria. For listOf(Person(id=1, age=21), Person(id=2, age=21), Person(id=3, age=31)) it will return p1 = Person(id=2, age=21).
To fix this you need to have additional comparsions with null in your conditional expressions (after implementing this, your second if-statement could be merged into branches of the first one to avoid repeating checks). Also, you can't use break inside forEach - it should be substituted with simple loop.
All together:
var p1: Person? = null
var p2: Person? = null
for (it in list) {
if (p1 == null && it.age == 21) {
p1 = it
if (p2 != null) break
} else if (p2 == null && it.age == 31) {
p2 = it
if (p1 != null) break
}
}
To generalize this you may create extension function:
fun <T> Iterable<T>.takeFirstTwo(predicate1: (T) -> Boolean, predicate2: (T) -> Boolean): Pair<T?, T?> {
var p1: T? = null
var p2: T? = null
for (it in this) {
if (p1 == null && predicate1(it)) {
p1 = it
if (p2 != null) break
} else if (p2 == null && predicate2(it)) {
p2 = it
if (p1 != null) break
}
}
return p1 to p2
}
//Usage:
val is21 = { it: Person -> it.age == 21}
val is31 = { it: Person -> it.age == 31}
val (p1, p2) = list.takeFirstTwo(is21, is31)
You can use the filter keyword to filter any collection
val matches = list.filter { if (it.age == 21 || it.age == 31) }
matches will be a list where all Person objects have an age of 21 or 31.
I do not think there is such a method in List, the closest statement would be get first match using list.first{it.age == 21 || it.age == 31 }, which will get the first item matching given predicate then breaks the loop, may be you can write your own extension to filter first n numbers
fun <T> Iterable<T>.firstN(n: Int, predicate: (T) -> Boolean): List<T> {
val output = ArrayList<T>()
var count = 0
for (element in this){
if(count == n) break
if (predicate(element)){
count++
output.add(element)
}
}
return output
}
You can do below to get first two elements
with(list.firstN(2){ it.age == 21 || it.age == 31}){
if(size == 2){
val (p1, p2) = this
}
}

Adding a list to the end of another list

I am writing a piece of code in which I need to add a list to another list.
fun a(x:Int):List<List<Int>>{
var a = 1.rangeTo(x);
var b: List<List<Int>> = emptyList();
for (i in a){
var num1 = if(i<=3) i else 4;
var list_a= 0.rangeTo(num1 - 1);
b.add(list_a);
}
return b
}
I problem is b.add(list_a) is giving error(unresolved reference).
In this function if I input println(a(2))
The output should be [[0],[0,1]]
Please help.
The reason why you're getting the unresolved reference for b.add() is because you have initialized b as a List instead of MutableList.
Please note that once initialized, you can add elements to a MutableList but not a List in Kotlin. Hence change your initialization of b as var b: MutableList<List<Int>> = mutableListOf()
fun a(x: Int): List<List<Int>> {
var a = 1.rangeTo(x)
var b: MutableList<List<Int>> = mutableListOf()
for (i in a) {
var num1 = if (i <= 3) i else 4
var list_a = 0.rangeTo(num1 - 1).toList()
b.add(list_a)
}
return b
}
Refactoring it a bit further, you can use 1..x for the range in the for loop and (0 until num1) instead of 0.rangeTo(num1 - 1) as below
fun a(x: Int): List<List<Int>> {
val listB: MutableList<List<Int>> = mutableListOf()
for (i in 1..x) {
val num1 = if (i <= 3) i else 4
val listA = (0 until num1).toList()
listB.add(listA)
}
return listB
}

Convert collection of numbers to range formatted string with Kotlin

I have a very large list of numbers. I need to pass this list of numbers as a URL query parameter. Since these lists can get so large, it could potentially cause the request URL to exceed the allowed length of a URL; also, it's a bit difficult to debug a string of sequential numbers (E.G. 1,2,3,..,500,782). To remedy these issues, I would like to convert the sequential number list string to one that is formatted using a range notation (E.G. -5..-3,1..500,782). How do I create this range notation string using Kotlin and how do I parse the string back to a collection of numbers also using Kotlin?
This will convert a Collection<Int> to a string that uses the "range notation" specified:
fun Collection<Int>.toRangesString(): String {
if (this.isEmpty()) {
return ""
}
if (this.size <= 2) {
return this.toSortedSet().joinToString(",")
}
val rangeStrings = mutableListOf<String>()
var start: Int? = null
var prev: Int? = null
for (num in this.toSortedSet()) {
if (prev == null) {
start = num
prev = num
continue
}
if (num != (prev + 1)) {
_addRangeString(rangeStrings, start!!, prev)
start = num
prev = num
continue
}
prev = num
}
if (start != null) {
_addRangeString(rangeStrings, start, prev!!)
}
return rangeStrings.joinToString(",")
}
private fun _addRangeString(rangeStrings: MutableList<String>, start: Int, prev: Int) {
rangeStrings.add(
when {
(start == prev) -> start.toString()
((start + 1) == prev) -> "${start},${prev}"
else -> "${start}..${prev}"
}
)
}
...and this will parse those range notated strings into a Set<Int>:
fun parseRangesString(str: String): Set<Int> {
if (str.isBlank()) {
return setOf()
}
val ranges = str.trim().split(",")
val numbers = mutableListOf<Int>()
for (range in ranges) {
if (range.contains("..")) {
val (start, end) = range.split("..")
numbers.addAll(start.toInt()..end.toInt())
continue
}
numbers.add(range.toInt())
}
return numbers.toSet()
}
...and, finally, even better than using a huge collection of numbers, you can use Kotlin's IntRange (or LongRange) class:
fun toIntRanges(str: String): Collection<IntRange> = _toRanges(str, ::_createIntRange)
fun toLongRanges(str: String): Collection<LongRange> = _toRanges(str, ::_createLongRange)
private fun <T : ClosedRange<*>> _toRanges(str: String, createRange: (start: String, end: String) -> T): Collection<T> {
if (str.isBlank()) {
return listOf()
}
val rangeStrs = str.trim().split(",")
val ranges = mutableListOf<T>()
for (rangeStr in rangeStrs) {
if (rangeStr.contains("..")) {
val (start, end) = rangeStr.split("..")
ranges.add(createRange(start, end))
continue
}
ranges.add(createRange(rangeStr, rangeStr))
}
return ranges.toList()
}
private fun _createIntRange(start: String, end: String) = IntRange(start.toInt(), end.toInt())
private fun _createLongRange(start: String, end: String) = LongRange(start.toLong(), end.toLong())

Error 10001st prime number in Kotlin

My code:
import java.util.*
fun checkPrime(n : Int): Boolean{
val z = n/2
var stop = false
if(n == 0 || n == 1){
stop = true
return false
}
for(i in 2..z){
if(n % i == 0){
stop = true
return false
break
}
}
return !stop
}
fun main(args : Array<String>){
var primes = ArrayList<Int>()
//The "500000" can be replaced with any number so you get at least 100001 primes
for(i in 2..500000){
if(checkPrime(i)){
primes.add(i)
}
}
println("Finished")
println("Size of Array: ${primes.size}")
println("10001st Prime: ${primes.get(index = 10001)}")
}
I'm new to Kotlin so this probably looks horrible to you. When I run it, I get 104759, which is wrong. Where is the error here? Is my prime checking function incorrect? Thanks.
Alternatively, you could use Java's built in Prime Number checker and Kotlin's infinite sequences generated by buildSequence.
Ensure that you use take() to select how many elements you want to extract.
import java.math.BigInteger
import kotlin.coroutines.experimental.buildSequence
fun primes() = buildSequence {
var n = 1
while (true){
if (BigInteger("$n").isProbablePrime(1)){
yield(n)
}
n++
}
}
fun main(args: Array<String>) {
println(primes().take(10001).last())
}
Prime numbers are of the form 6f ± 1, excluding 2 and 3 where f is any integer
fun isPrime(number: Int) : Boolean
{
if (number <= 1)
{
return false;
}
// The check for the number 2 and 3
if (number <= 3)
{
return true;
}
if (number%2 == 0 || number%3 == 0)
{
return false;
}
for (i in 5..number/2 step 6)
{
if (number%i == 0 || number%(i+2) == 0)
{
return false;
}
}
return true;
}
Time Complexity of the solution: O(sqrt(n))

Loop String from Console and sum odd numbers

fun main(args: Array<String>) {
println("Number: ")
val num = readLine()!!.toInt()
var sum = 0
for (digit in num) {
if (digit % 2 != 0) {
sum += digit
}
}
println("$sum")
}
I need this loop to go through every digit in the number and sums all the digits that are odd.
It gives me an error on num "For-loop range must have an 'iterator()' method"
You cannot iterate over an Int like num:
val num = readLine()!!.toInt()
You can fix it without a loop and by using standard functions map, filter and sum:
val sum = readLine()!!.toCharArray()
.map { it.toString().toInt() }
.filter { it % 2 != 0 }
.sum()
The filter-condition for even numbers would be it % 2 == 0
EDIT
For your homework, do this:
val num = readLine()!!.toCharArray()
var sum = 0
for (a in num) {
val intVal = a.toString().toInt()
if (intVal % 2 != 0) {
sum += intVal
}
}