String format with named values - string

I'm searching for idiomatic scala way to format string with named arguments. I'm aware of the String method format, but it does not allow to specify named arguments, only positional are available.
Simple example:
val bob = "Bob"
val alice = "Alice"
val message = "${b} < ${a} and ${a} > ${b}"
message.format(a = alice, b = bob)
Defining message as separate value is crucial, since I want to load it from resource file and not specify in the code directly. There are plenty of similar questions that was answered with the new scala feature called String Interpolation. But this does not address my case: I could not let the compiler do all the work, since resource file is loaded in the run time.

It's not clear whether by "positional args" you mean the usual sense or the sense of "argument index" as used by Formatter.
scala> val bob = "Bob"
bob: String = Bob
scala> val alice = "Alice"
alice: String = Alice
scala> val message = "%2$s likes %1$s and %1$s likes %2$s" format (bob, alice)
message: String = Alice likes Bob and Bob likes Alice
You'd like:
scala> def f(a: String, b: String) = s"$b likes $a and $a likes $b"
f: (a: String, b: String)String
scala> f(b = bob, a = alice)
res2: String = Bob likes Alice and Alice likes Bob
You can compile the interpolation with a reflective toolbox or the scripting engine.
scala> import scala.tools.reflect._
import scala.tools.reflect._
scala> val tb = reflect.runtime.currentMirror.mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl#162b3d47
scala> val defs = s"""val a = "$alice" ; val b = "$bob""""
defs: String = val a = "Alice" ; val b = "Bob"
scala> val message = "${b} < ${a} and ${a} > ${b}"
message: String = ${b} < ${a} and ${a} > ${b}
scala> val msg = s"""s"$message""""
msg: String = s"${b} < ${a} and ${a} > ${b}"
scala> tb eval (tb parse s"$defs ; $msg")
res3: Any = Bob < Alice and Alice > Bob
or
scala> def f(a: String, b: String) = tb eval (tb parse s"""val a = "$a"; val b = "$b"; s"$message"""")
f: (a: String, b: String)Any
scala> f(a = alice, b = bob)
res4: Any = Bob < Alice and Alice > Bob
It's a little overmuch.
But consider:
https://www.playframework.com/documentation/2.0/ScalaTemplates

I don't know whether it's possible to use the same parser the compiler uses.
But Regex can handle the example you've given.
val values = Map("a" -> "Alice", "b" -> "Bob")
val message = "${b} < ${a} and ${a} > ${b}"
def escapeReplacement(s: String): String =
s.replace("\\", "\\\\").replace("$", "\\$")
"\\$\\{([^\\}]*)\\}".r.replaceAllIn(message,
m => escapeReplacement(values(m.group(1))))

One approach might be to have a list of substitutions (no limit on how many variables) and then go with something like:
def substituteVar(s:String,subs:Seq[(String,String)]):String=
subs.foldLeft(s)((soFar,item) => soFar replaceAll("\\$\\{"+item._1+"}", item._2))
substituteVar(message,Seq(("a",alice),("b",bob)))
res12: String = Bob < Alice and Alice > Bob
Could be extended as well...

Related

scala fixed length string format not working for Chinese

I'm trying to pad strings to make them have same length using string format, which works well on English strings:
def main(args: Array[String]): Unit = {
val a = "this is first"
val b = "second"
val c = "third word"
println(f"$a%-30s" + "|")
println(f"$b%-30s" + "|")
println(f"$c%-30s" + "|")
}
// gives:
this is first |
second |
third word |
However, when I test it on Chinese strings, this method doesn't work anymore:
def main(args: Array[String]): Unit = {
val a = "小伙_6"
val b = "女网友_6"
val c = "有期徒刑_6"
println(f"$a%-30s" + "|")
println(f"$b%-30s" + "|")
println(f"$c%-30s" + "|")
// gives:
小伙_6 |
女网友_6 |
有期徒刑_6 |
How can I get this work for Chinese strings?

SML how to change String after isSubstring comparison

I am currently learning SML/NJ due to a program that uses mostly a gui for basic input, but SML input for advanced options.
I want to compare whether one string is a substring of another.
If the condition is true then the full string should "just" be returned or assigned to a new variable.
For Testing purposes I used an online compiler, because I get almost zero feedback from the other program.
Relevant Code Snippet:
fun SString(sub:string, str:string):string =
if isSubstring(sub, str) = TRUE then str
(* str should be returned , no errors*)
else val p2:string="nope";
(* no return or adjustable(fixed)return /without data*)
val p1 = "sender,time,data"
val p2 = "sender"
print(SString(p2,p1))
So far I am stuck now.
My main questions are:
Can I actually create a new variable in a function?
What is the best practice in this case?
In some online docs I read that it isn't possible to assign a new value to a variable once assigned.
Should my function rather have following form with inner bindings and let decl in expr end *var?
fun newstr:string(sub:string,str:string) =
let val n = isSubstring(sub,str)
in
end *sub
Thanks in advance
Relevant results of the compilation:
Standard ML of New Jersey v110.78 [built: Thu Aug 31 03:45:42 2017]
- stdIn:4.33-4.44 Error: syntax error: deleting ELSE VAL ID
- stdIn:4.52-4.61 Error: syntax error: deleting EQUALOP STRING SEMICOLON
P.S. I added fitting tags, feel free to remove/adjust them
A gentle Introduction to ML gave me a great understanding of SML, except for some cryptic codes and type conversions.
val p1:string = "weather,maps,translate";
val p2:string = "maps";
fun SString(sub:string, str:string) = let
in case (String.isSubstring sub str) of
(true) => str ^ ",pie"
| (_) => "nope"
end;
val a = SString(p2,p1);
val b = String(p1,p2);
The executed result of above code:
Standard ML of New Jersey v110.78 [built: Thu Aug 31 03:45:42 2017]
- val p1 = "weather,maps,translate" : string
val p2 = "maps" : string
[autoloading]
[library $SMLNJ-BASIS/basis.cm is stable]
[autoloading done]
val SString = fn : string * string -> string
val a = "weather,maps,translate,pie" : string
val b = "nope" : string

Merging overlapping strings

Suppose I need to merge two overlapping strings like that:
def mergeOverlap(s1: String, s2: String): String = ???
mergeOverlap("", "") // ""
mergeOverlap("", "abc") // abc
mergeOverlap("xyz", "abc") // xyzabc
mergeOverlap("xab", "abc") // xabc
I can write this function using the answer to one of my previous questions:
def mergeOverlap(s1: String, s2: String): String = {
val n = s1.tails.find(tail => s2.startsWith(tail)).map(_.size).getOrElse(0)
s1 ++ s2.drop(n)
}
Could you suggest either a simpler or maybe more efficient implementation of mergeOverlap?
You can find the overlap between two strings in time proportional to the total length of the strings O(n + k) using the algorithm to calculate the prefix function. Prefix function of a string at index i is defined as the size of the longest suffix at index i that is equal to the prefix of the whole string (excluding the trivial case).
See those links for more explanation of the definition and the algorithm to compute it:
https://cp-algorithms.com/string/prefix-function.html
https://hyperskill.org/learn/step/6413#a-definition-of-the-prefix-function
Here is an implementation of a modified algorithm that calculates the longest prefix of the second argument, equal to the suffix of the first argument:
import scala.collection.mutable.ArrayBuffer
def overlap(hasSuffix: String, hasPrefix: String): Int = {
val overlaps = ArrayBuffer(0)
for (suffixIndex <- hasSuffix.indices) {
val currentCharacter = hasSuffix(suffixIndex)
val currentOverlap = Iterator.iterate(overlaps.last)(overlap => overlaps(overlap - 1))
.find(overlap =>
overlap == 0 ||
hasPrefix.lift(overlap).contains(currentCharacter))
.getOrElse(0)
val updatedOverlap = currentOverlap +
(if (hasPrefix.lift(currentOverlap).contains(currentCharacter)) 1 else 0)
overlaps += updatedOverlap
}
overlaps.last
}
And with that mergeOverlap is just
def mergeOverlap(s1: String, s2: String) =
s1 ++ s2.drop(overlap(s1, s2))
And some tests of this implementation:
scala> mergeOverlap("", "")
res0: String = ""
scala> mergeOverlap("abc", "")
res1: String = abc
scala> mergeOverlap("", "abc")
res2: String = abc
scala> mergeOverlap("xyz", "abc")
res3: String = xyzabc
scala> mergeOverlap("xab", "abc")
res4: String = xabc
scala> mergeOverlap("aabaaab", "aab")
res5: String = aabaaab
scala> mergeOverlap("aabaaab", "aabc")
res6: String = aabaaabc
scala> mergeOverlap("aabaaab", "bc")
res7: String = aabaaabc
scala> mergeOverlap("aabaaab", "bbc")
res8: String = aabaaabbc
scala> mergeOverlap("ababab", "ababc")
res9: String = abababc
scala> mergeOverlap("ababab", "babc")
res10: String = abababc
scala> mergeOverlap("abab", "aab")
res11: String = ababaab
It's not tail recursive but it is a very simple algorithm.
def mergeOverlap(s1: String, s2: String): String =
if (s2 startsWith s1) s2
else s1.head +: mergeOverlap(s1.tail, s2)

Scala: Remove the last occurrence of a character

I am trying to remove the last occurrence of a character in a string. I can get its index:
str.lastIndexOf(',')
I have already tried to use split and the replace function on the string.
You could use patch.
scala> val s = "s;dfkj;w;erw"
s: String = s;dfkj;w;erw
scala> s.patch(s.lastIndexOf(';'), "", 1)
res6: String = s;dfkj;werw
Curious why Scala doesn't have a .replaceLast but there must be a reason...
Reverse the String and use str.replaceFirst then reverse again
I doubt this is terrible efficient but it is effective :)
scala> "abc.xyz.abc.xyz".reverse.replaceFirst("zyx.", "").reverse
res5: String = abc.xyz.abc
As a def it would look like this:
def replaceLast(input: String, matchOn: String, replaceWith: String) = {
input.reverse.replaceFirst(matchOn.reverse, replaceWith.reverse).reverse
}
scala> def removeLast(x: Char, xs: String): String = {
|
| val accumulator: (Option[Char], String) = (None, "")
|
| val (_, applied) = xs.foldRight(accumulator){(e: Char, acc: (Option[Char], String)) =>
| val (alreadyReplaced, runningAcc) = acc
| alreadyReplaced match {
| case some # Some(_) => (some, e + runningAcc)
| case None => if (e == x) (Some(e), runningAcc) else (None, e + runningAcc)
| }
| }
|
| applied
| }
removeLast: (x: Char, xs: String)String
scala> removeLast('f', "foobarf")
res7: String = foobar
scala> removeLast('f', "foobarfff")
res8: String = foobarff
You could try the following:
val input = "The quick brown fox jumps over the lazy dog"
val lastIndexOfU = input.lastIndexOf("u")
val splits = input.splitAt(lastIndexOfU)
val inputWithoutLastU = splits._1 + splits._2.drop(1) // "The quick brown fox jmps over the lazy dog"

why does scala toString give type Any?

I need to compose a String depending on the value of an Int - Scala is confusing me:
scala> val qqq: Int = -3
qqq: Int = -3
scala> qqq.toString
res17: String = -3
scala> if (qqq < 0)
| qqq.toString
res19: Any = -3
what is going on there? Why is qqq.toString a string in the first but not the last?
What happens if q >= 3 ? What type does if return then ?
e.g. if you write this:
if (qqq < 0)
"a"
else
"b"
you'll always get a String return type

Resources