Idiomatic way to convert A => Seq[B] - scala

I'd like to convert a single value into a collection of multiple "characteristics", without using a mutable data structure to collect the values. I'd like something like this fantasy construct which uses pattern matching, but does not stop after first match:
scala> 2 multimatch {
case i if i > 0 => "Positive"
case i if i < 0 => "Negative"
case i if (i % 2 == 0) => "Even"
//yadda yadda
}
res0: Seq[java.lang.String] = List(Positive, Even)

Using the pimp pattern, partial functions and repeated parameters, this is how close I can get:
class MultiMatcher[A](a: A) {
def multiMatch[B](pfs: PartialFunction[A, B]*): Seq[B] = {
pfs.map(_.lift(a)).collect{ case Some(v) => v }
}
}
implicit def toMultiMatcher[A](a:A): MultiMatcher[A] = new MultiMatcher(a)
2 multiMatch (
{ case i if i > 0 => "Positive" },
{ case i if i < 0 => "Negative" },
{ case i if i % 2 == 0 => "Even" }
)
// returns Seq[java.lang.String] = ArrayBuffer(Positive, Even)

First you define your characteristics as functions from Int to Option[String]
val pos = (i:Int) => if (i > 0) Some("Positive") else None
val neg = (i:Int) => if (i < 0) Some("Negative") else None
val even = (i:Int) => if (i % 2 == 0) Some("Even") else None
Then you make a list of characteristics.
val characteristics = pos::neg::even::Nil
And then you use flatmap to get a list of those characteristics that apply to a certain object.
scala> characteristics.flatMap(f=>f(2))
res6: List[java.lang.String] = List(Positive, Even)

First, define a multimatch function as follows:
scala> def multimatch[A,B]( value : A,ps: ( A => Boolean, B)*) =
| for ( p <- ps
| if (p._1(value))) yield p._2
multimatch: [A,B](value: A,ps: ((A) => Boolean, B)*)Seq[B]
Then, here we go:
scala> multimatch(2,
| ( ((x :Int) => x > 0) -> "POSITIVE"),
| ( ((x :Int) => x < 0) -> "NEGATIVE"),
| ( ((x :Int) => x % 2 == 0) -> "EVEN")
| )
res4: Seq[java.lang.String] = ArrayBuffer(POSITIVE, EVEN)
Or, less cluttered:
scala> multimatch(2,
| ( (x :Int) => x > 0 , "POSITIVE"),
| ( (x :Int) => x < 0, "NEGATIVE"),
| ( (x :Int) => x % 2 == 0, "EVEN")
| )
res5: Seq[java.lang.String] = ArrayBuffer(POSITIVE, EVEN)

Related

Optimal way to find neighbors of element of collection in circular manner

I have a Vector and I'd like to find neighbors of given element.
Say if we have Vector(1, 2, 3, 4, 5) and then:
for element 2, result must be Some((1, 3))
for element 5, result must be Some((4, 1))
for element 1, result must be Some((5, 2))
for element 6, result must be None
and so on..
I have not found any solution in standard lib(please point me if there is one), so got the next one:
implicit class VectorOps[T](seq: Vector[T]) {
def findNeighbors(elem: T): Option[(T, T)] = {
val currentIdx = seq.indexOf(elem)
val firstIdx = 0
val lastIdx = seq.size - 1
seq match {
case _ if currentIdx == -1 || seq.size < 2 => None
case _ if seq.size == 2 => seq.find(_ != elem).map(elem => (elem, elem))
case _ if currentIdx == firstIdx => Some((seq(lastIdx), seq(currentIdx + 1)))
case _ if currentIdx == lastIdx => Some((seq(currentIdx - 1), seq(firstIdx)))
case _ => Some((seq(currentIdx - 1), seq(currentIdx + 1)))
}
}
}
The question is: how this can be simplified/optimized using stdlib?
def neighbours[T](v: Seq[T], x: T): Option[(T, T)] =
(v.last +: v :+ v.head)
.sliding(3, 1)
.find(_(1) == x)
.map(x => (x(0), x(2)))
This uses sliding to create a 3 element window in the data and uses find to match the middle value of the 3. Adding the last/first to the input deals with the wrap-around case.
This will fail if the Vector is too short so needs some error checking.
This version is safe for all input
def neighbours[T](v: Seq[T], x: T): Option[(T, T)] =
(v.takeRight(1) ++ v ++ v.take(1))
.sliding(3, 1)
.find(_(1) == x)
.map(x => (x(0), x(2)))
Optimal when number of calls with the same sequence is about or more than seq.toSet.size:
val elementToPair = seq.indicies.map(i => seq(i) ->
(seq((i - 1 + seq.length) % seq.length), seq((i + 1 + seq.length) % seq.length)
).toMap
elementToPair.get(elem)
// other calls
Optimal when number of calls with the same sequence less than seq.toSet.size:
Some(seq.indexOf(elem)).filterNot(_ == -1).map { i =>
(seq((i - 1 + seq.length) % seq.length), seq((i + 1 + seq.length) % seq.length) }

How to filter list which elements are in non decreasing order

How can I filter list of other list, e.g.
val l = List(List(1,2,3), List(1,2,4), List(9,8,7))
to return a List of elements that are not in descending order and sum of elements in the list is odd. So it should return List(1,2,4) (sum is 7 and elements are in not descending order).
I was thinking about something like l.filter( _ < _ ).filter( _ + _ % 2 != 0) but I don't really know how to do this.
You can check whether the sorted list is the same as the list itself:
val filtered = l.filter(x => x.sorted == x && x.sum % 2 != 0)
Tail recursive method as #Luis noted:
def isAscendingAndOddSum(xss: List[List[Int]]): List[List[Int]] = {
#scala.annotation.tailrec
def isAscendingAndOddSumR(xss: List[List[Int]], acc: List[List[Int]]): List[List[Int]] = xss match {
case Nil => acc
case h::t if(h.sorted == h && h.sum % 2 != 0) => isAscendingAndOddSumR(t, h :: acc)
case h::t => isAscendingAndOddSumR(t, acc)
}
isAscendingAndOddSumR(xss, List())
}
scala> isAscendingAndOddSum(List(List(1,2,3), List(1,2,4), List(9,8,7)))
val res11: List[List[Int]] = List(List(1, 2, 4))

scala how to reduce while n > 0

I am wondering if there's a way to deal with a while (n > 0) loop in a more functional way, I have a small Scala app that counts the number of digits equal to K from a range from 1 to N:
for example 30 and 3 would return 4 [3, 13, 23, 30]
object NumKCount {
def main(args: Array[String]): Unit = {
println(countK(30,3))
}
def countKDigit(n:Int, k:Int):Int = {
var num = n
var count = 0
while (num > 10) {
val digit = num % 10
if (digit == k) {count += 1}
num = num / 10
}
if (num == k) {count += 1}
count
}
def countK(n:Int, k:Int):Int = {
1.to(n).foldLeft(0)((acc, x) => acc + countKDigit(x, k))
}
}
I'm looking for a way to define the function countKDigit using a purely functional approach
First expand number n into a sequence of digits
def digits(n: Int): Seq[Int] = {
if (n < 10) Seq(n)
else digits(n / 10) :+ n % 10
}
Then reduce the sequence by counting occurrences of k
def countKDigit(n:Int, k:Int):Int = {
digits(n).count(_ == k)
}
Or you can avoid countKDigit entirely by using flatMap
def countK(n:Int, k:Int):Int = {
1.to(n).flatMap(digits).count(_ == k)
}
Assuming that K is always 1 digit, you can convert n to String and use collect or filter, like below (there's not much functional stuff you can do with Integer):
def countKDigit(n: Int, k: Int): Int = {
n.toString.collect({ case c if c.asDigit == k => true }).size
}
or
def countKDigit(n: Int, k: Int): Int = {
n.toString.filter(c => c.asDigit == 3).length
}
E.g.
scala> 343.toString.collect({ case c if c.asDigit == 3 => true }).size
res18: Int = 2
scala> 343.toString.filter(c => c.asDigit == 3).length
res22: Int = 2
What about the following approach:
scala> val myInt = 346763
myInt: Int = 346763
scala> val target = 3
target: Int = 3
scala> val temp = List.tabulate(math.log10(myInt).toInt + 1)(x => math.pow(10, x).toInt)
temp: List[Int] = List(1, 10, 100, 1000, 10000, 100000)
scala> temp.map(x => myInt / x % 10)
res17: List[Int] = List(3, 6, 7, 6, 4, 3)
scala> temp.count(x => myInt / x % 10 == target)
res18: Int = 2
Counting the occurrences of a single digit in a number sequence.
def countK(n:Int, k:Int):Int = {
assert(k >= 0 && k <= 9)
1.to(n).mkString.count(_ == '0' + k)
}
If you really only want to modify countKDigit() to a more functional design, there's always recursion.
def countKDigit(n:Int, k:Int, acc: Int = 0):Int =
if (n == 0) acc
else countKDigit(n/10, k, if (n%10 == k) acc+1 else acc)

Define recursive lambda expression in scala

I am trying to define a recursive lambda expression in scala and struggling a bit with sintax, if think that this is just a syntax:
Here is what I have so far (not compilable):
type rec = (Int => Int, Int)
val f = (x : Int) => x + x
val y : rec = (f:Int => Int, x:Int) => if (x > 0) y( f, 1) else 1
Error I am getting:
ScalaFiddle.scala:14: error: ScalaFiddle.rec does not take parameters
val y : rec = (f:Int => Int, x:Int) => if (x > 0) y( f, 1) else 1
^
Original code sample I am trying to optimize (which works fine):
case class Rec[I, O](fn : (I => O, I) => O) extends (I => O) {
def apply(v : I) = fn(this, v)
}
val sum = Rec[Int, Int]((f, v) => if (v == 0) 0 else v + f(v - 1))
Any help would be appreciated.
Thanks
rec is a tuple of type Tuple2[Int => Int,Int].
in your statement val y : rec = (f:Int => Int, x:Int) => if (x > 0) y( f, 1) else 1, you are trying to call a apply function on the tuple y (y(f,1)). Since the tuple doesnt have a apply function defined you are getting the error rec does not take parameters
as per your sample code your type rec (Int => Int, Int) => Int.
so the code sample would be
type rec = (Int => Int, Int) => Int
val f = (x : Int) => x + x
val y : rec = (f:Int => Int, x:Int) => if (x > 0) y( f, 1) else 1
Check This link for this answer explanation
var sumIt:(Int => Int) = (x: Int) => {if(x<=1) 1 else sumIt(x-1)+x

Scala Using Case Statements with +

Still working on hw1 of the Coursera Scala course, I'm having trouble with case statements and tuples.
object Pascal {
def main(args: List[String]) = {
require((args length) == 2, "args length must equal 2")
println("calling pascal on " + (args head) + " and " + (args last))
pascal((args head) toInt, (args last) toInt)
}
def pascal(c: Int, r: Int): Int = {
require(((r >= 0) && (c >= 0))
&& (c > r + 1), "r and c must be >= 0 and c must be <= r + 1")
(c, r) match {
case (_, 0) => 1
case (0, _) => 1
case (1 + r, _) => 1 // error: value not found "+"
case (_, _) => pascal(r-1,c-1) + pascal(r-1,c-1)
}
}
}
Please tell me how to code this in Scala:
case (1 + r, _) => 1
I believe you are looking for something like this:
case (v, _) if(v == r + 1) => 1
which will match any value of c (except for c == 0 which was matched previously) and then apply a test to check if v is equal to r + 1
You can also
val R = r+1
(c, r) match {
case (0, _) => 1
case (R, _) => 1
case (_, _) => pascal(r-1, c-1) + pascal(r-1,c)
}
(Note that the recursion you have is wrong--you add the same entry twice!--and that case (_, 0) is unnecessary since if r is 0, either c is 0 (which is caught by its case), or c is r+1, which is caught by that case.)