guilhebl
guilhebl

Reputation: 8990

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

Upvotes: 3

Views: 420

Answers (4)

Nazarii Bardiuk
Nazarii Bardiuk

Reputation: 4342

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)
}

Upvotes: 2

jwvh
jwvh

Reputation: 51271

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)

Upvotes: 0

Akavall
Akavall

Reputation: 86128

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

Upvotes: 0

Dat Nguyen
Dat Nguyen

Reputation: 1646

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

Upvotes: 0

Related Questions