Drop a given number of positive items of a given list - scala

Suppose I need a function List[Int] => Option[List[Int]] to drop exactly n elements of a given list if and only if all of them > 0. If the list size <= n the function should return None.
For instance:
def posn(n: Int): List[Int] => Option[List[Int]] = ???
val pos4: List[Int] => Option[List[Int]] = posn(4)
scala> pos4(Nil)
res18: Option[List[Int]] = None
scala> pos4(List(-1))
res19: Option[List[Int]] = None
scala> pos4(List(-1, 2, 3))
res20: Option[List[Int]] = None
scala> pos4(List(1, 2, 3))
res21: Option[List[Int]] = None
scala> pos4(List(1, 2, 3, 4, 5))
res22: Option[List[Int]] = Some(List(5))
scala> pos4(List(1, 2, 3, -4, 5))
res23: Option[List[Int]] = None
I am writing posn like that:
def posn(n: Int): List[Int] => Option[List[Int]] = xs =>
if (xs.size >= n && xs.take(n).forall(_ > 0)) Some(xs.drop(n)) else None
This function seems working bit it doesn't seem elegant and idiomatic. How would you re-write it ?

Here's an (arguably) more idiomatic implementation using Pattern Matching and a recursive call to posn - but I'm not sure it's preferable to your suggested implementation:
def posn(n: Int): List[Int] => Option[List[Int]] = xs => (n, xs) match {
case (0, _) => Some(xs) // stop if enough objects dropped
case (_, head :: tail) if head > 0 => posn(n - 1)(tail) // drop positive and move on
case _ => None // found a negative item or end of xs => "fail"
}

I don't know if there is an idiomatic or elegant way to do this. There seems to be no generic pattern that can be extracted from your logic, except what you have already done (using drop and take), so I don't believe you will find some more useful predefined method
However, you are traversing your list a few times, and this could be avoided:
def posn(n: Int): List[Int] => Option[List[Int]] = xs => {
val (head, tail) = xs.splitAt(n) //does take and drop in one run
if (head.lengthCompare(n) == 0 && head.forall(_ > 0)) Some(tail) // lengthCompare does not compute the whole length if there is no need to
else None
}
This is still not perfect, and more verbose than your version.
You could also do all of it at once, with tail recursion (here assuming n>=0):
def posn(n: Int): List[Int] => Option[List[Int]] = xs =>
if (n == 0) Some(xs)
else if (xs.isEmpty || xs.head <= 0) None
else posn(n - 1)(xs.tail)
This would be more efficient if List was naively implemented, but I really doubt you will see any improvement.

I would write a generic version and use that to define posn:
def dropWhen[T](n: Int, p: T => Boolean, l: List[T]): Option[List[T]] = {
val (f, s) = l.splitAt(n)
if (f.length >= n && f.forall(p)) { Some(s) } else { None }
}
def posn(n: Int): List[Int] => Option[List[Int]] = l => dropWhen(n, (i : Int) => i > 0, l)
Note this method scans the prefix of length n twice

Another (non-recursive) alternative: use zipWithIndex and dropWhile to drop the first N positive numbers, and then check head to see whether the first remaining item is exactly at position n: if it is, we got what we want, otherwise we can return None:
def posn(n: Int): List[Int] => Option[List[Int]] = xs =>
Some(xs.zipWithIndex.dropWhile { case (v, i) => v > 0 && i < n })
.find(_.headOption.exists(_._2 == n)) // first index should be n
.map(_.map(_._1)) // remove indices

Related

A "Simple' Scala question but took me long time to debug

Please check the two pieces of script as above.
genComb4(lst) works since I put z <- genComb4(xs) before i <- 0 to x._2 in the for-comprehension; genComb(lst) does not work since I change the order of these two lines in for-comprehension.
It took me almost half day to find this bug, but I cannot explain it by myself. Could you tell me why this happened?
Thank you very much in advance.
// generate combinations
val nums = Vector(1, 2, 3)
val strs = Vector('a', 'b', 'c')
val lst: List[(Char, Int)] = strs.zip(nums).toList
def genComb4(lst: List[(Char, Int)]): List[List[(Char, Int)]] = lst match {
case Nil => List(List())
case x :: xs =>
for {
z <- genComb4(xs) // correct
i <- 0 to x._2 // correct
} yield ( (x._1, i) :: z)
}
genComb4(lst)
def genComb(lst: List[(Char, Int)]): List[List[(Char, Int)]] = lst match {
case Nil => List(List())
case x :: xs =>
for {
i <- (0 to x._2) // wrong
z <- genComb(xs) // wrong
} yield ( (x._1, i) :: z)
}
genComb(lst)
It's because of different types of container in for comprehension. When you start for-comprehension from line: i <- (0 to x._2) it's set type of result container as IndexedSeq but in case where first line is z <- genComb4(xs) the type of result container is List, take a look:
val x = 'a' -> 2
val indices: Seq[Int] = 0 to x._2
val combs: List[List[(Char, Int)]] = genComb4(List(x))
// indexed sequence
val indicesInFor: IndexedSeq[(Char, Int)] = for {
i <- 0 to x._2
} yield (x._1, i)
// list
val combsInFor: List[List[(Char, Int)]] = for {
z <- genComb4(List(x))
} yield z
so for make your second case is working, you should cast (0 to x._2).toList:
val indicesListInFor: List[(Char, Int)] = for {
i <- (0 to x._2).toList
} yield (x._1, i)
result code should be:
def genComb(lst: List[(Char, Int)]): List[List[(Char, Int)]] = lst match {
case Nil => List(List())
case x :: xs =>
for {
i <- (0 to x._2).toList
z <- genComb(xs)
} yield ( (x._1, i) :: z)
}
genComb(lst)
You should remember about type of starting line in for-comprehension and inheritance of scala collections. If next types in for-comprehension can't be converted by inheritance rules to the first expression line type you should take care about it by yourself.
Good practise is unwrap for-expression into flatMap, map and withFilter functions, then you will find miss-typing or something else faster.
useful links:
how does yield work, scala documentation
quick review of scala for-comprehensions
for-expressions, scala documentation

Scala Recursion Over Multiple Lists

I have this function that takes two lists and returns the sum of the two lists.
Example:
def sumOfSums(a: List[Int], b: List[Int]): Int = {
var sum = 0
for(elem <- a) sum += elem
for(elem <- b) sum += elem
sum
}
Simple enough, however now I'm trying to do it recursively and the second list parameter is throwing me off.
What I have so far:
def sumOfSumsRec(a: List[Int], b: List[Int], acc: Int): Int = a match {
case Nil => acc
case h :: t => sumOfSumsRec(t, acc + h)
}
There's 2 problems here:
I'm only matching on the 'a' List
I'm getting an error when I try to do acc + h, im not sure why.
Question: How can I recursively iterate over two lists to get their sum?
Pattern match both lists:
import scala.annotation.tailrec
def recSum(a: List[Int], b: List[Int]): Int = {
#tailrec
def recSumInternal(a: List[Int], b: List[Int], acc: Int): Int = {
(a, b) match {
case (x :: xs, y :: ys) => recSumInternal(xs, ys, x + y + acc)
case (x :: xs, Nil) => recSumInternal(xs, Nil, x + acc)
case (Nil, y :: ys) => recSumInternal(Nil, ys, y + acc)
case _ => acc
}
}
recSumInternal(a, b, 0)
}
Test:
recSum(List(1,2), List(3,4,5))
Yields:
15
Side note:
For any future readers of this post, I assumed this question was prinarly asked for educational purposes mostly, hence showing how recursion can work on multiple lists, but this is by no mean an idiomatic way to take. For any other purposes, by all means:
scala> val a = List(1,2)
a: List[Int] = List(1, 2)
scala> val b = List(3,4,5)
b: List[Int] = List(3, 4, 5)
scala> a.sum + b.sum
res0: Int = 15
Or consider using mechanisms such as foldLeft, foldMap, etc.

Scala find an Int between a List of Int

I have an Int and I need to find in a List of Ints the upper and lower bounds for this Int.
For example:
In a List(1,3,6,9), when I ask for 2, I should get 1 and 3. Is there any pre-built function in the Scala collection API that I can use? I know that I can achieve this using the filter function, but I'm looking for an already existing API if any?
So, not built in, but here you go. Since you want return nothing for (e.g.) 0 and 10, we need to return an option.
var rs = List(1, 3, 6, 9) //> rs : List[Int] = List(1, 3, 6, 9)
def bracket(n: Int, rs: List[Int]) = {
val (l, r) = rs.span(_ < n)
if (l == Nil || r == Nil)
None
else if (r.head == n)
Some((n, n))
else
Some((l.last, r.head))
}
bracket(0, rs) //> res0: Option[(Int, Int)] = None
bracket(2, rs) //> res1: Option[(Int, Int)] = Some((1,3))
bracket(6, rs) //> res2: Option[(Int, Int)] = Some((6,6))
bracket(10, rs) //> res3: Option[(Int, Int)] = None
Alternative if you know the edge cases can't happen:
def bracket(n: Int, rs: List[Int]) = {
val (l, r) = rs.span(_ < n)
if (r.head == n)
(n, n)
else
(l.last, r.head)
}
bracket(2, rs) //> res0: (Int, Int) = (1,3)
bracket(6, rs) //> res1: (Int, Int) = (6,6)
(will throw an exception if there is no lower and upper bound for n)
If you can't have edge cases and you are OK with a tuple that is (<=, >) then simply
def bracket(n: Int, rs: List[Int]) = {
val (l, r) = rs.span(_ <= n)
(l.last, r.head)
}
bracket(2, rs) //> res0: (Int, Int) = (1,3)
bracket(6, rs) //> res1: (Int, Int) = (6,9)
You can use grouped or slide iterators to take 2 elements at a time and test condition:
// you can also add 'numbers.sorted' if your list is not sorted
def findBoundries(x: Int, numbers: List[Int]): Option[List[Int]] =
numbers.grouped(2).find {
case a :: b :: Nil => a <= x && x <= b
}
findBoundries(2,List(1,3,6,9))
You can, as a workaround, also convert your List to a NavigableSet (Java class that has higher and lower methods, which do more or less what you require).

Scala idiom to find first Some of Option from iterator

I have an iterator of Options, and would like to find the first member that is:
Some
and meets a predicate
What's the best idiomatic way to do this?
Also: If an exception is thrown along the way, I'd like to ignore it and move on to the next member
optionIterator find { case Some(x) if predicate(x) => true case _ => false }
As for ignoring exceptions… Is it the predicate that could throw? 'Cause that's not really wise. Nonetheless…
optionIterator find {
case Some(x) => Try(predicate(x)) getOrElse false
case _ => false
}
Adding a coat of best and idiomatic to the paint job:
scala> val vs = (0 to 10) map { case 3 => None case i => Some(i) }
vs: scala.collection.immutable.IndexedSeq[Option[Int]] = Vector(Some(0), Some(1), Some(2), None, Some(4), Some(5), Some(6), Some(7), Some(8), Some(9), Some(10))
scala> def p(i: Int) = if (i % 2 == 0) i > 5 else ???
p: (i: Int)Boolean
scala> import util._
import util._
scala> val it = vs.iterator
it: Iterator[Option[Int]] = non-empty iterator
scala> it collectFirst { case Some(i) if Try(p(i)) getOrElse false => i }
res2: Option[Int] = Some(6)
Getting the first even number over five that doesn't blow up the test.
Assuming that you can wrap your predicate so that any error returns false:
iterator.flatMap(x => x).find(yourSafePredicate)
flatMap takes a collection of collections (which an iterable of Option is as Option and Either are considered collections with a max size of one) and transforms it into a single collection:
scala> for { x <- 1 to 3; y <- 1 to x } yield x :: y :: Nil
res30: IndexedSeq[List[Int]] = Vector(List(1, 1), List(2, 1), List(2, 2), List(3, 1), List(3, 2), List(3, 3))
scala> res30.flatMap(x => x)
res31: IndexedSeq[Int] = Vector(1, 1, 2, 1, 2, 2, 3, 1, 3, 2, 3, 3)
find returns the first entry in your iterable that matches a predicate as an Option or None if there is no match:
scala> (1 to 10).find(_ > 3)
res0: Option[Int] = Some(4)
scala> (1 to 10).find(_ == 11)
res1: Option[Int] = None
Some sample data
scala> val l = Seq(Some(1),None,Some(-7),Some(8))
l: Seq[Option[Int]] = List(Some(1), None, Some(-7), Some(8))
Using flatMap on a Seq of Options will produce a Seq of defined values, all the None's will be discarded
scala> l.flatMap(a => a)
res0: Seq[Int] = List(1, -7, 8)
Then use find on the sequence - you will get the first value, that satisfies the predicate. Pay attention, that found value is wrapped as Option, cause find should be able to return valid value (None) value in case of "not found" situation.
scala> l.flatMap(a => a).find(_ < 0)
res1: Option[Int] = Some(-7)
As far as I know it is "OK" way for the Scala.
Might be more idiomatic way is to use collect / collectFirst on the Seq ...
scala> l.collectFirst { case a#Some(x) if x < 0 => a }
res2: Option[Some[Int]] = Some(Some(-7))
Pay attention that here we have Some(Some(-7)) because the collectFind should have chance to produce "not found" value, so here 1st Some - from collectFirst, the 2nd Some - from the source elements of Seq of Option's.
You can flatten the Some(Some(-7)) if you need the values in your hand.
scala> l.collectFirst({ case a#Some(x) if x < 0 => a }).flatten
res3: Option[Int] = Some(-7)
If nothing found - you will have the None
scala> l.collectFirst({ case a#Some(x) if x < -10 => a }).flatten
res9: Option[Int] = None

Find Indexes *Where*

There's a indexWhere function in Vector that finds the index of a match.
def indexWhere(p: (A) ⇒ Boolean, from: Int): Int
> Finds index of the first element satisfying some predicate after or
> at some start index.
http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Vector
I wrote this function to find all indexes where such a match occurs.
def getAllIndexesWhere[A,B](as: List[A])(f: (B => Boolean))(g: A => B): Vector[B] = {
def go(y: List[A], acc: List[Option[B]]): Vector[B] = as match {
case x :: xs => val result = if (f(g(x))) Some(g(x)) else None
go(xs, acc :+ result)
case Nil => acc.flatten.toVector
}
go(as, Nil)
}
However, is there already a built-in function of a collection?
zipWithIndex, filter, and map are built-ins that can be combined to get all the indices of some predicate.
Get the indices of the even values in the list.
scala> List(1,2,3,4,5,6,7,8,9,10).zipWithIndex.filter(_._1 % 2 == 0).map(_._2)
res0: List[Int] = List(1, 3, 5, 7, 9)
You can also use collect as #0__ notes.
scala> List(1,2,3,4,5,6,7,8,9,10).zipWithIndex.collect{ case(a,b) if a % 2 == 0 => b}
res1: List[Int] = List(1, 3, 5, 7, 9)