5
\$\begingroup\$

I wanted to create a FizzBuzz implementation that both showcases the strengths of Scala (Functional paradigm and collections) and is general enough to anticipate the typical interview follow-up questions. i.e. What if fizz was for factors of 4 instead? What if we wanted to replace factors of 7 with "foo"?

object FizzBuzz extends App {

  def fizzbuzz(factors: Map[Int, String])(currentNumber: Int): String = {
    val words = factors.filter(currentNumber % _._1 == 0).values
    if (words.isEmpty) currentNumber.toString else words.mkString("")
  }

  private final val Factors = Map(3 -> "fizz", 5 -> "buzz")
  private final val Range = 1 to 100

  Range.map(fizzbuzz(Factors)).foreach(println)
}

I would appreciate any feedback on how I can make my Scala more idiomatic.

\$\endgroup\$
5
\$\begingroup\$

Map is not guaranteed to impose or preserve any order for its entries. For example, if I change Factors to …

private final val Factors = Map(3 -> "a", 5 -> "b", 6 -> "c", 7 -> "d", 8 -> "e", 9 -> "f")

… I get:

⁞
89
bcfa
d
92
a
94
b
cae
97
d
fa
b

So, the fact that you get "fizzbuzz" rather than "buzzfizz" is based on luck, not skill. If you want to guarantee "fizzbuzz", you need a scala.collection.immutable.SortedMap.

\$\endgroup\$
0
\$\begingroup\$

Because you want to be able to change the mapping of fizz and buzz I would think to make a funcion that take a function as an argument that maps a number to a string:

def fizzbuzz(countUntil: Int)(mapping: Int => String) = {
  for (i <- 0 to countUntil) yield mapping(i)
}

Then you can call the function like this:

// Map Int to String using pattern matching with a guard
fizzbuzz(30) {
  case fizzbuzz if fizzbuzz % 3 == 0 && fizzbuzz % 5 == 0 => "fizzbuzz"
  case fizz if fizz % 3 == 0 => "fizz"
  case buzz if buzz % 5 == 0 => "buzz"
  case f => s"$f"
}.foreach(println)

The pattern matching code could be an if/else just as long as it takes an Int and returns a String for example:

// Map Int to String using if/else
fizzbuzz(30){ f =>
  if (f % 3 == 0 && f % 5 == 0) "fizzbuzz"
  else if (f % 3 == 0) "fizz"
  else if (f % 5 == 0) "buzz"
  else s"$f"
}.foreach(println)
\$\endgroup\$
  • \$\begingroup\$ That's a terrible use of pattern matching. \$\endgroup\$ – Nico Nov 1 '17 at 12:37
  • \$\begingroup\$ @Nico What makes it so terrible? \$\endgroup\$ – Soapy Nov 1 '17 at 13:36
  • 1
    \$\begingroup\$ you're just wrapping an if then else into a pattern matching for no added benefit \$\endgroup\$ – Nico Nov 1 '17 at 14:08
  • \$\begingroup\$ @Nico If you're going for performance or if you perfer the syntax then go for an if/else however I pefer the pattern matching in this case. But for a complete example I've added an if/else version to my answer. \$\endgroup\$ – Soapy Nov 1 '17 at 14:25
  • 2
    \$\begingroup\$ @Soapy The main drawback I see with your approach is that the number of cases grows drastically when there are more possible factors. For example, if I wanted factors of 3 -> "fizz", 4 -> "buzz", 5 -> "foo", 6 -> "bar", you would need an if case for each combination of those options. (case 3, case 3&4, case 3&5, case 3&4&5 ...) which would be difficult to maintain. Although I appreciate the suggestion! \$\endgroup\$ – Taylor Nov 1 '17 at 16:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.