Strange results of `list.indexWhere` when parameter `from` is negative - scala

Is this a bug in indexWhere method, or is there a meaningful explanation for the first four lines in the example bellow?
scala> List(1, 1, 4, 4, 4).indexWhere(_ > 3, -4)
res0: Int = -2
scala> List(1, 1, 4, 4, 4).indexWhere(_ > 3, -3)
res1: Int = -1
scala> List(1, 1, 4, 4, 4).indexWhere(_ > 3, -2)
res2: Int = 0
scala> List(1, 1, 4, 4, 4).indexWhere(_ > 3, -1)
res3: Int = 1
scala> List(1, 1, 4, 4, 4).indexWhere(_ > 3, 0)
res4: Int = 2
scala> List(1, 1, 4, 4, 4).indexWhere(_ > 3, 1)
res5: Int = 2
scala> List(1, 1, 4, 4, 4).indexWhere(_ > 3, 2)
res6: Int = 2
scala> List(1, 1, 4, 4, 4).indexWhere(_ > 3, 3)
res7: Int = 3
scala> List(1, 1, 4, 4, 4).indexWhere(_ > 3, 4)
res8: Int = 4
scala> List(1, 1, 4, 4, 4).indexWhere(_ > 3, 5)
res9: Int = -1

I think ideally you'd see an IllegalArgumentException if you passed a negative number as the start index (what's that supposed to mean anyway?), but maybe it was felt that the overhead of a bounds check wasn't worth it. I don't see any specified behaviour in the docs for this, so not sure I'd call it a bug; more a case of "rubbish in, rubbish out".

Source code looks like this:
override /*SeqLike*/
def indexWhere(p: A => Boolean, from: Int): Int = {
var i = from
var these = this drop from
while (these.nonEmpty) {
if (p(these.head))
return i
i += 1
these = these.tail
}
-1
}
So there is no input check, neither zeroing of i variable. I would say it is a bug. If you can, write it on https://groups.google.com/forum/#!forum/scala-language where devs can take a look at it.

As a future solution, you might want to implement your own version that does check. You might make it return an Option as well instead of -1, which is also unsafe:
implicit class WithIndexWhereSafe[T](seq: Seq[T]) {
def indexWhereSafe(p: T => Boolean, from: Int) = {
assert(from >= 0, "from must be >= 0")
val i = seq.indexWhere(p, from)
if (i != -1)
Some(i)
else
None
}
}
List(1, 1, 4, 4, 4).indexWhereSafe(_ > 3, 2) // Some(2)
List(1, 1, 4, 4, 4).indexWhereSafe(_ > 3, 5) // None
List(1, 1, 4, 4, 4).indexWhereSafe(_ > 3, -4) // AssertionError

Related

Scala .init method example

Can you please explain what init method performs with respect to below list
i can see the result of new list says that last sequence is being omitted from the existing list.
val numbers = List(1, 2, 3, 4, 5)
val result = numbers.init
println(result)
.init and .last are the compliments to the .head and .tail methods.
val nums = List(1,2,3,4)
nums.head //res0: Int = 1
nums.tail //res1: List[Int] = List(2, 3, 4)
nums.init //res2: List[Int] = List(1, 2, 3)
nums.last //res3: Int = 4
def init: List[A] which selects all elements except the last.
l: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8)
scala> l.last
res58: Int = 8
scala> l.init
res59: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
scala> val testList = List(1,2,3,4,5)
testList: List[Int] = List(1, 2, 3, 4, 5)
scala> testList.init
res0: List[Int] = List(1, 2, 3, 4)
scala> testList.last
res1: Int = 5
scala> testList.head
res2: Int = 1
scala> testList.tail
res3: List[Int] = List(2, 3, 4, 5)

Remove one element from Scala List

For example, if I have a list of List(1,2,1,3,2), and I want to remove only one 1, so the I get List(2,1,3,2). If the other 1 was removed it would be fine.
My solution is:
scala> val myList = List(1,2,1,3,2)
myList: List[Int] = List(1, 2, 1, 3, 2)
scala> myList.patch(myList.indexOf(1), List(), 1)
res7: List[Int] = List(2, 1, 3, 2)
But I feel like I am missing a simpler solution, if so what am I missing?
surely not simpler:
def rm(xs: List[Int], value: Int): List[Int] = xs match {
case `value` :: tail => tail
case x :: tail => x :: rm(tail, value)
case _ => Nil
}
use:
scala> val xs = List(1, 2, 1, 3)
xs: List[Int] = List(1, 2, 1, 3)
scala> rm(xs, 1)
res21: List[Int] = List(2, 1, 3)
scala> rm(rm(xs, 1), 1)
res22: List[Int] = List(2, 3)
scala> rm(xs, 2)
res23: List[Int] = List(1, 1, 3)
scala> rm(xs, 3)
res24: List[Int] = List(1, 2, 1)
you can zipWithIndex and filter out the index you want to drop.
scala> val myList = List(1,2,1,3,2)
myList: List[Int] = List(1, 2, 1, 3, 2)
scala> myList.zipWithIndex.filter(_._2 != 0).map(_._1)
res1: List[Int] = List(2, 1, 3, 2)
The filter + map is collect,
scala> myList.zipWithIndex.collect { case (elem, index) if index != 0 => elem }
res2: List[Int] = List(2, 1, 3, 2)
To remove first occurrence of elem, you can split at first occurance, drop the element and merge back.
list.span(_ != 1) match { case (before, atAndAfter) => before ::: atAndAfter.drop(1) }
Following is expanded answer,
val list = List(1, 2, 1, 3, 2)
//split AT first occurance
val elementToRemove = 1
val (beforeFirstOccurance, atAndAfterFirstOccurance) = list.span(_ != elementToRemove)
beforeFirstOccurance ::: atAndAfterFirstOccurance.drop(1) // shouldBe List(2, 1, 3, 2)
Resource
How to remove an item from a list in Scala having only its index?
How should I remove the first occurrence of an object from a list in Scala?
List is immutable, so you can’t delete elements from it, but you can filter out the elements you don’t want while you assign the result to a new variable:
scala> val originalList = List(5, 1, 4, 3, 2)
originalList: List[Int] = List(5, 1, 4, 3, 2)
scala> val newList = originalList.filter(_ > 2)
newList: List[Int] = List(5, 4, 3)
Rather than continually assigning the result of operations like this to a new variable, you can declare your variable as a var and reassign the result of the operation back to itself:
scala> var x = List(5, 1, 4, 3, 2)
x: List[Int] = List(5, 1, 4, 3, 2)
scala> x = x.filter(_ > 2)
x: List[Int] = List(5, 4, 3)

Scala: Any predefined function to iterate over list(0), then list(0, 1), then list(0, 1, 2), etc.?

I want to iterate over a scala list in an incremental way, i.e. the first pass should yield the head, the second the first 2 elements, the next the first 3, etc...
I can code this myself as a recursive function, but does a pre-existing function exist for this in the standard library?
You can use the .inits method to get there, albeit there may be performance issues for a large list (I haven't played around with making this lazy):
scala> val data = List(0,1,2,3,4)
data: List[Int] = List(0, 1, 2, 3, 4)
scala> data.inits.toList.reverse.flatten
res2: List[Int] = List(0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4)
You can use the take like so:
scala> val myList = 1 to 10 toList
myList: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> for(cnt <- myList.indices) yield myList.take(cnt+1)
res1: scala.collection.immutable.IndexedSeq[List[Int]] = Vector(List(1), List(1, 2), List(1, 2, 3), List(1, 2, 3, 4), List(1, 2, 3, 4, 5), List(1, 2, 3, 4, 5, 6), List(1, 2, 3, 4, 5, 6, 7), List(1, 2, 3, 4, 5, 6, 7, 8), List(1, 2, 3, 4, 5, 6, 7, 8, 9), List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
OK, since I've whined enough, here's an iterator version that tries reasonably hard to not waste space or compute more than is needed at at one point:
class stini[A](xs: List[A]) extends Iterator[List[A]] {
var ys: List[A] = Nil
var remaining = xs
def hasNext = remaining.nonEmpty
def next = {
val e = remaining.head
remaining = remaining.tail
ys = e :: ys
ys.reverse
}
}
val it = new stini(List(1, 2, 3, 4))
it.toList
//> List[List[Int]] =
// List(List(1), List(1, 2), List(1, 2, 3), List(1, 2, 3, 4))
Try: for((x, i) <- l.view.zipWithIndex) println(l.take(i + 1))
if you need something side-effected (I just did println to give you an example)

Removing an element from MutableList in Scala

I have a MutableList and I want to remove an element from it but I cannot find the appropriate method. There is a method to remove element from ListBuffer like this:
val x = ListBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9)
x -= 5
I am unable to find an equivalent method on MutableList.
MutableList lacks -= and --= because it does not extend the Shrinkable trait. Various motivations for this can be found here.
MutableList does have diff, filter, and other methods which can help you in case you are in a situation where reassigning a variable (or instantiating a new variable) might be an option, and performance concerns aren't paramount:
var mylist = MutableList(1, 2, 3)
mylist = mylist diff Seq(1)
val myNewList = mylist.filter(_ != 2)
val indexFiltered = mylist.zipWithIndex.collect { case (el, ind) if ind != 1 => el }
You can often use ListBuffer instead of MutableList, which will unlock the desired -= and --= methods:
val mylist = ListBuffer(1, 2, 3)
mylist -= 1 //mylist is now ListBuffer(2, 3)
mylist --= Seq(2, 3) //mylist is now empty
It's not the answer, just to warn you about problems (at least in 2.11.x):
//street magic
scala> val a = mutable.MutableList(1,2,3)
a: scala.collection.mutable.MutableList[Int] = MutableList(1, 2, 3)
scala> a += 4
res7: a.type = MutableList(1, 2, 3, 4)
scala> a
res8: scala.collection.mutable.MutableList[Int] = MutableList(1, 2, 3, 4)
scala> a ++= List(8,9,10)
res9: a.type = MutableList(1, 2, 3, 4, 8, 9, 10)
scala> val b = a.tail
b: scala.collection.mutable.MutableList[Int] = MutableList(2, 3, 4, 8, 9, 10)
scala> b.length
res10: Int = 6
scala> a.length
res11: Int = 7
scala> a ++= List(8,9,10)
res12: a.type = MutableList(1, 2, 3, 4, 8, 9, 10, 8, 9, 10)
scala> b += 7
res13: b.type = MutableList(2, 3, 4, 8, 9, 10, 7)
scala> a
res14: scala.collection.mutable.MutableList[Int] = MutableList(1, 2, 3, 4, 8, 9, 10, 7)
scala> b
res15: scala.collection.mutable.MutableList[Int] = MutableList(2, 3, 4, 8, 9, 10, 7)
scala> a ++= List(8,9,10)
res16: a.type = MutableList(1, 2, 3, 4, 8, 9, 10, 7)
This example is taken from some gist - I've posted it on facebook with #devid_blein #street_magic tags, but can't find original link on the internet.

takeWhile: also need first element failed the condition in scala

scala> List(1,2,3,4,5,6,7).takeWhile(i=>i<5)
res1: List[Int] = List(1, 2, 3, 4)
What if I also need to include 5 in the result?
Assuming that the function that you will be using is more complicated than the taking first 5 elements then,
You can do
scala> List(1,2,3,4,5,6,7)
res5: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
scala> res5.takeWhile(_<5) ++ res5.dropWhile(_<5).take(1)
res7: List[Int] = List(1, 2, 3, 4, 5)
Also
scala> res5.span(_<5)
res8: (List[Int], List[Int]) = (List(1, 2, 3, 4),List(5, 6, 7))
scala> res8._1 ++ res8._2.take(1)
res10: List[Int] = List(1, 2, 3, 4, 5)
Also
scala> res5.take(res5.segmentLength(_<5, 0) + 1)
res17: List[Int] = List(1, 2, 3, 4, 5)
scala> res5.take(res5.indexWhere(_>5))
res18: List[Int] = List(1, 2, 3, 4, 5)
Edit
If this is not a parallelized computation and you won't be using the parallel collections:
var last = myList.head
val rem = myList.takeWhile{ x=>
last = x
x < 5
}
last :: rem
anonymous function forming a closure around the solution you want.
Previous Answer
I'd settle for the far less complicated:
.takeWhile(_ <= 5)
wherein you're just using the less than or equal operator.
List(1,2,3,4,5,6,7,8).takeWhile(_ <= 5)
This is best optimal solution for it