Update: This article provides a discussion of the problem I ran into when trying to generate random strings in Scala, but for the best Solution, see the Comments section below.
When it comes to generating random strings with the scala.util.Random class, I haven’t figured out yet how to properly use the nextString
method. I’ve tried using it in several different ways, but I always get a string of question marks as output, like this:
scala> val r = new scala.util.Random(31) r: scala.util.Random = scala.util.Random@7d49fa1e scala> r.nextString(10) res0: String = ??????????
This happens whether I give Random
a seed value or not, and whether I call nextString
as a static method or not:
scala> Random.nextString(5) res1: String = ?????
Update: The solution to this problem is shown in the Comments section below. I’ll update this post when I have more time, but for now I’ll just say that, as shown in that comment, a good answer is to use this code:
Random.alphanumeric.take(10).mkString
The Random alphanumeric method
Frankly I haven’t really needed this capability, so this unusual behavior hasn’t bothered me enough for me to take time to look into it. Plus, I could always use the alphanumeric
method as follows to get a random string:
scala> val x = Random.alphanumeric x: scala.collection.immutable.Stream[Char] = Stream(Q, ?) scala> x take 10 foreach println Q n m x S Q R e P B
(Note that the alphanumeric
method returns a Stream, so you need to coerce the Stream to give you some output, as shown in that example. I don’t think I have any tutorials out here about Scala Streams just yet, but I do cover them in detail in the Scala Cookbook.)
Many ways to create a Scala random string
On one particularly cold night in Alaska back in December (it’s January now as I write this) I got bored and decided to write my own random string method. As I started to write the code, I realized there were several different ways to tackle the problem. For me this is a cool thing about Scala; it’s like the old Perl slogan, “There’s more than one way to do it.”
Without any further discussion, I’ll just say that the following Scala code demonstrates several different ways to generate a random string. I start with a “Java-esque” approach, and then show several other ways to create a random string, including some recursive and tail recursive approaches.
import scala.annotation.tailrec /** * Examples of different ways to write "random string" methods in Scala. * See the main method for examples of how each method is called. * Created by Alvin Alexander, https://alvinalexander.com */ object RandomStringExamples { def main(args: Array[String]) { println("1: " + randomString(10)) println("2: " + randomStringArray(10)) println("3: " + randomStringRecursive(10).mkString) println("3: " + randomStringRecursive2(10).mkString) println("4: " + randomStringTailRecursive(10, Nil).mkString) println("5: " + randomStringRecursive2Wrapper(10)) println("6: " + randomAlphaNumericString(10)) println("6: " + randomAlphaNumericString(10)) println("6: " + randomAlphaNumericString(10)) println("x2: " + x2(10, ('a' to 'z') ++ ('A' to 'Z'))) } // 1 - a java-esque approach def randomString(length: Int) = { val r = new scala.util.Random val sb = new StringBuilder for (i <- 1 to length) { sb.append(r.nextPrintableChar) } sb.toString } // 2 - similar to #1, but using an array def randomStringUsingArray(length: Int): String = { val r = new scala.util.Random val a = new Array[Char](length) val sb = new StringBuilder for (i <- 0 to length-1) { a(i) = r.nextPrintableChar } a.mkString } // 3 - recursive, but not tail-recursive def randomStringRecursive(n: Int): List[Char] = { n match { case 1 => List(util.Random.nextPrintableChar) case _ => List(util.Random.nextPrintableChar) ++ randomStringRecursive(n-1) } } // 3b - recursive, but not tail-recursive def randomStringRecursive2(n: Int): String = { n match { case 1 => util.Random.nextPrintableChar.toString case _ => util.Random.nextPrintableChar.toString ++ randomStringRecursive2(n-1).toString } } // 4 - tail recursive, no wrapper @tailrec def randomStringTailRecursive(n: Int, list: List[Char]):List[Char] = { if (n == 1) util.Random.nextPrintableChar :: list else randomStringTailRecursive(n-1, util.Random.nextPrintableChar :: list) } // 5 - a wrapper around the tail-recursive approach def randomStringRecursive2Wrapper(n: Int): String = { randomStringTailRecursive(n, Nil).mkString } // 6 - random alphanumeric def randomAlphaNumericString(length: Int): String = { val chars = ('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9') randomStringFromCharList(length, chars) } // 7 - random alpha def randomAlpha(length: Int): String = { val chars = ('a' to 'z') ++ ('A' to 'Z') randomStringFromCharList(length, chars) } // used by #6 and #7 def randomStringFromCharList(length: Int, chars: Seq[Char]): String = { val sb = new StringBuilder for (i <- 1 to length) { val randomNum = util.Random.nextInt(chars.length) sb.append(chars(randomNum)) } sb.toString } def x(length: Int, chars: Seq[Char]): String = { val list = List.range(1, length) val arr = new Array[Char](length) list.foreach{ e => arr(e) = chars(util.Random.nextInt(chars.length)) } list.mkString } // create a fake list so i can use map (or flatMap) def x2(length: Int, chars: Seq[Char]): String = { val tmpList = List.range(0, length) val charList = tmpList.map{ e => chars(util.Random.nextInt(chars.length)) } return charList.mkString } }
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
I’m sure there are many other ways to generate a random string in Scala, but if nothing else, rather than solve a specific problem, I thought I’d just share all these different potential approaches.