I am refactoring some older Java code over to Kotlin. There is a function that returns the index of the minimum value held by an element in a Kotlin IntArray within the range [a, b]. The range values default to 0 and the size of the array - 1.
I would like to do something along the lines of...
return data.minOf().indexOf()
...but while only iterating between the a and b indices of data.
Here is the function:
// data is the IntArray property that I'm looping through.
fun absMinIndex(a: Int = 0, b: Int = (data.size - 1)) : Int {
var minVal = data[a]
var minIndex = 0
for (i in (a + 1)..b) {
val e = data[i]
if (e < minVal) {
minVal = e
minIndex = i
}
}
return maxIndex
}
This [for loop] solves the issue nicely by never visiting indices out of range, and by not generating a copied array/sub-array. I'm wondering if it could be done 'prettier'.
Question
Is there a more idiomatic Kotlin approach to iterating through an array within a range that would not negatively impact the performance of my current solution?
Edited some code for clarity.
I believe this approach would be more idiomatic:
Use IntRange as an input parameter
Define extension method for IntArray providing custom iterator to traverse the list in the desired range wrapping values into IndexedValue:
fun IntArray.withIndexInRange(range: IntRange = 0..lastIndex) = Iterable {
require(range.first >= 0 && range.last <= lastIndex)
object : Iterator<IndexedValue<Int>> {
private var index = range.first
override fun hasNext() = index <= range.last
override fun next() = IndexedValue(index, this#withIndexInRange[index++])
}
}
Use minByOrNull method from stdlib to find minimal value or wrap it into another extension method for convenience:
fun <T : Comparable<T>> Iterable<IndexedValue<T>>.indexOfMinOrNull() = minByOrNull { it.value }?.index
Usage:
data.withIndexInRange(a..b).indexOfMinOrNull()
Note, that this will have some performance penalties (creation and GC of N extra objects), but as Donald Knuth says:
Premature optimization is the root of all evil
So, I believe better readability worth it.
As Tenfour04 suggested, not much you can do without loosing performance. But as per your idea with changing the way you call it, you can make it an extension function.
fun IntArray.findIndexOfMinInRange(a: Int = 0, b: Int = this.size - 1): Int {
var maxVal = get(a)
var maxIndex = 0
for (i in (a + 1)..b) {
if (get(i) < maxVal) {
maxVal = get(i)
maxIndex = i
}
}
return maxIndex
}
//and call it like this:
data.findIndexOfMinInRange(0, 15) //or without anything in the parentheses for the default values
One thing that I would change is the variable names inside the function, we are searching for the minimum value, not max, and also for the index of the minimum, not the maximum index. Also maybe (big maybe) creating a val of data[it] instead of accessing it twice might be better (honestly not sure, we would be trading a .get for a few bytes of memory).
So all in all, I'd probably leave it at this:
fun IntArray.findIndexOfMinInRange(fromIndex: Int = 0, toIndex: Int = this.size - 1): Int {
var min = get(fromIndex)
var indexOfMin = fromIndex
for(i in (fromIndex + 1)..toIndex){
val current = get(i)
if (current < min) {
min = current
indexOfMin = i
}
}
return indexOfMin
}
//would be called in the same way the one above
Also, a note of caution, if you create an IntArray of a certain size, and do not populate it in full, the default values it will hold for the non populated ones is 0. If you do:
val data = IntArray(6)
data[0] = 10
data[1] = 11
data[2] = 100
data[3] = 9
data[4] = 50
Then the actual array is [10, 11, 100, 9, 50, 0].
The below code is written in scala,
val Array(f, t) = readLine().trim().split(" +").map(_.toInt)
I am not able to comprehend val Array(f, t).
To me, Array is class. Due to that, We can only create the object and with that object, we can access the function of the class. Or else We can access the static methods of the Array class without creating an object for it.
-- scala
def main(args: Array[String]): Unit = {
val n = readInt
val m = readInt
val f = Array.ofDim[Int](100000)
Arrays.fill(f, -1)
for (e <- 1 to m) {
val Array(f, t) = readLine().trim().split(" +").map(_.toInt)
// Code continues
}
}
That is called pattern matching (for example you can check this at Extractors). The code you mentioned means that please assign the first (index 0) value in the array resulting to f, assign the second (index 1) element to t and there should not be more or less values in the array. Both f and t are fresh variables.
You also mentioned the confusion with the val Array(...) syntax. It translates to the following method: scala.Array.unapplySeq[T](x:Array[T])
I tried below code but it ultimately prints 5 zeros after giving the user defined values to array .
the below code takes array of size 5 and gives user defined values
object printarray {
def main(args:Array[String]) {
val arr = new Array[Int](5)
println("the values of array is ")
for(i<-0 to 4) {
val arr = scala.io.StdIn.readInt()
}
arr.foreach(println)
}
}
There are a couple of things that need improvement in the code.
You allocate an array of 5 elements named arr in the scope of the main method, but you also declare an additional value with the same name, arr, inside the for comprehension scope and read an Int into it, which you discard once you exit the for scope. Then, you print the array in the outer scope, which hasn't changed at all.
The first thing you need to make this work, is index into the array instead of creating a new value named arr in the inner scope:
object printarray {
def main(args:Array[String]) {
val arr = new Array[Int](5)
println("the values of array is ")
for (i <- 0 to 4) {
arr(i) = scala.io.StdIn.readInt()
}
arr.foreach(println)
}
}
To improve things further, you use the yield Scala synax to make this more concise:
val arr = for (i <- 0 to 4) yield StdIn.readInt()
This will not return an Array[Int], but an IndexedSeq[Int] with an underlying type of Vector[Int]. If you still want an Array[Int], you'll have to explicitly call toArray:
val arr: Array[Int] = (for (i <- 0 to 4) yield scala.io.StdIn.readInt()).toArray
In your for-loop you are reassigning the variable in every iteration. You can change it to:
for(i <- 0 to 4) {
arr(i) = scala.io.StdIn.readInt()
}
As a side-note, instead of declaring the arr before the loop you can simply do:
val arr = for(i <- 0 to 4) yield scala.io.StdIn.readInt()
I'm trying to read a csv file and return it as an array of arrays of doubles (Array[Array[Double]]). It's pretty clear how to read in a file line by line and immediately print it out, but not how to store it in a two-dimensional array.
def readCSV() : Array[Array[Double]] = {
val bufferedSource = io.Source.fromFile("/testData.csv")
var matrix :Array[Array[Double]] = null
for (line <- bufferedSource.getLines) {
val cols = line.split(",").map(_.trim)
matrix = matrix :+ cols
}
bufferedSource.close
return matrix
}
Had some type issues and then realized I'm not doing what I thought I was doing. Any help pointing me on the right track would be very appreciated.
It seems your question was already answered. So just as a side note, you could write your code in a more scalaish way like this:
def readCSV() : Array[Array[Double]] = {
io.Source.fromFile("/testData.csv")
.getLines()
.map(_.split(",").map(_.trim.toDouble))
.toArray
}
1) You should start with an empty Array unstead of null.
2) You append items to an Array with the operator :+
3) As your result type is Array[Array[Double]] you need to convert the strings of the csv to Double.
def readCSV() : Array[Array[Double]] = {
val bufferedSource = io.Source.fromFile("/testData.csv")
var matrix :Array[Array[Double]] = Array.empty
for (line <- bufferedSource.getLines) {
val cols = line.split(",").map(_.trim.toDouble)
matrix = matrix :+ cols
}
bufferedSource.close
return matrix
}
I'm trying to search a nested array of characters for a specific character, and then return the indices of the character from the array.
Code Snippet
def search(target: Char, arr:Array[Array[Char]]): List[Int] = {
for (i <- 0 until arr.length) { //search through first layer of array
for (j <- 0 until arr(i).length) { //search through second layer of array
if (arr(i)(j) == target) {
val x = List(i,j)
return x
} }}}
However, I'm getting an error from compilation, that says this function is returning the two type signature. Error message:
error: type mismatch;
found : Unit
required: List[Int]
for (i <- 0 until arr.length) { //search through first layer of array
^
I've found two similar threads here: found Unit: required Int. Why is the error not obvious?
and found: Unit required: Int - How to correct this?
but they don't solve the problem i'm facing: I am trying to return the List, but the compiler is getting stuck at the for loop..
Seems like there are a lot of answers already, but I think that this is the most idiomatic way to approach the problem:
The Code
def search(target: Char, arr: Array[Array[Char]]): List[(Int, Int)] = {
val indices = for{
(a, i) <- arr.iterator.zipWithIndex
(c, j) <- a.iterator.zipWithIndex
if( c == target )
} yield i -> j
indices.toList
}
The Explanation
In scala, for-comprehensions are nestable, so you can take care of any degree of nested arrays by simply adding another x <- y line. You can introduce filtering with an if statement inside of the for{...}.
In the comprehension, a is the ith array inside arr, where i is the first index. c is the jth character inside a, where j is the second index. I use iterator so that indices can be evaluated on the fly, without needing to copy the arrays behind the scenes because of the for-comprehension. At the end, I call toList to evaluate the results of the indices iterator into a list.
The return type, List[(Int, Int)] is a list of pairs. It makes more sense to return List( (1,2), (3,4) ) if you found your target at i=1, j=2 and i=3, j=4 than to return List(1,2,3,4).
General Thoughts
Try to avoid using return in scala. You can usually handle your collections with an iterator-like approach, using yield, and then evaluate the result by calling a toList or toMap or toWhatever.
The scala collections API is very helpful for many of these cases, too. In the case where you just want the first item that matches a condition, you can use myCollection.find(...). Explore the scala docs to see the huge variety of convenient functions that are already available to you.
I would suggest using a less Java-like approach altogether. I'm not entirely sure what your function is supposed to do, but if you want a list of all (x, y) indices of the match within the nested array, you could do something like this:
def search(target: Char, array: Array[Array[Char]]): Seq[(Int, Int)] = {
array.zipWithIndex.filter(_._1.contains(target)).map { xa =>
xa._1.zipWithIndex.filter(_._1 == target).map(xb => (xa._2, xb._2)).toSeq
}.flatten.toSeq
}
which behaves like this:
val t = Array(
Array('a', 'b', 'c'),
Array('b'), Array('c', 'a'),
Array('a', 'a', 'x', 'a')
)
println(search('a', t))
=> ((0,0), (2,1), (3,0), (3,1), (3,3))
Here is my solution to find the first index of a element in a two-dimension array:
(replace collectFirt to collect, if you wan to find all indexes)
def search[T](target: T, arr: Array[Array[T]]): List[Int] =
arr.indices.collectFirst{
case k if arr(k).contains(target) =>
List(k, arr(k).indexWhere(_ == target))
}.getOrElse(Nil)
Test:
scala> val t = Array(
| Array('a', 'b', 'c'),
| Array('b'),
| Array('c', 'a'),
| Array('a', 'a', 'x', 'a')
| )
scala> println(search('a', t))
List(0, 0)
scala> println(search('x', t))
List(3, 2)
scala> println(search('e', t))
List()
You only return a List if a particular condition is met (arr(i)(j) == target). You have to define a return value for the case that the for comprehensions run through. E.g.
def search(target: Char, arr:Array[Array[Char]]): List[Int] = {
for (i <- 0 until arr.length) { //search through first layer of array
for (j <- 0 until arr(i).length) { //search through second layer of array
if (arr(i)(j) == target) {
val x = List(i,j)
return x
}
}
}
Nil // not found
}
The for loop itself would return Unit if the second array is empty or the if expression evaluates to false. You could rewrite it so it returns null if it nevers gets to the code after the if-expression.
I would leave out the assignment of x too, there's really no point.
def search(target: Char, arr:Array[Array[Char]]): List[Int] =
{
for (i <- 0 until arr.length)
{ //search through first layer of array
for (j <- 0 until arr(i).length)
{ //search through second layer of array
if (arr(i)(j) == target)
{
return List(i,j)
}
}
}
Nil
}
Btw this could probably be rewritten with a more functional approach but that goes beyond the scope of this question.