我有一张类似
的地图val m = Map("foo" -> "bar", "faz" -> "baz")
我需要编写一个自定义get
方法,这样键就可以成为地图中最后带数字的键。
例如:
m.get("foo1") should return "bar"
我正在寻找一个好的scala模式来解决这个问题。
我也是使用yield从for循环生成上面的地图,所以我不能做这样的事情
val m = CustomMap("foo" -> "bar")
任何解决方案都将受到赞赏。
谢谢
答案 0 :(得分:0)
首先,您可以从for
理解生成地图,然后将其转换为CustomMap
。你只需要定义一个
def apply(map: Map[String, String]) = CustomMap(map.toSeq :_*)
中的CustomMap
- 然后您可以执行val m = CustomMap( for { ... } yield ... )
其次,如果它不必被命名为get
(它可能不应该是这样),你可以用隐含的方式做这种事情:
object PimpMyMap {
val pref = ".*?(\\d+)".r
implicit class Pimped[V](val map: Map[String,V]) extends AnyVal {
def getPrefix(key: String): Option[V] = map.get(key).orElse { key match {
case pref(k) => map.get(k)
case _ => None
}
}
现在你可以写下这样的内容:
import PimpMyMap._
val map = Map("foo" -> 1)
val one = map.getPrefix("foo123") // Some(1)
val anotherOne = map.getPrefix("foo") // also Some(1);
答案 1 :(得分:0)
您可以使用implicit class
和implicit conversion
:
import scala.language.implicitConversions
object MapHelpers {
implicit def optionStringToString(maybeS: Option[String]): String = maybeS.getOrElse("")
implicit class MapWithIntKey(val m: Map[String, String]) extends Map[String, String] {
override def get(key: String): Option[String] = {
val intRegex = """(\d+)""".r
val keyWithoutInt = intRegex
.findFirstMatchIn(key)
.map(int => {
val idx = key.indexOf(int.toString)
key.slice(0, idx)
})
.getOrElse(key)
m.get(keyWithoutInt)
}
def +[V1 >: String](
kv: (String, V1)): scala.collection.immutable.Map[String, V1] = m + kv
def -(key: String): scala.collection.immutable.Map[String, String] = m - key
def iterator: Iterator[(String, String)] = m.iterator
}
}
object App {
import MapHelpers._
def testMapImplicit(): Unit = {
val myMap: MapWithIntKey = Map("foo" -> "bar", "faz" -> "baz")
val result: String = myMap.get("foo1")
println("result", result) // bar
}
}
答案 2 :(得分:0)
如果您有可靠的方法从假密钥中获取真实密钥,则可以使用Map.withDefault
执行此操作:
class CustomMap[K, +V] private (underlying: Map[K, Option[V]]) {
def get(k: K): Option[V] = underlying(k)
}
object CustomMap {
def apply[K, V](original: Map[K, V], keyReducer: K => K) = new CustomMap(originalMap.
mapValues(Some(_)).
withDefault(k => originalMap.get(keyReducer(k))
)
}
在您的情况下,您可以将其与
一起使用val stringKeyReducer: String => String = k.reverse.dropWhile(_.isDigit).reverse
删除字符串末尾的数字,所以
CustomMap(Map("foo" -> "bar"), stringKeyReducer).get("foo1") = Some("bar")
答案 3 :(得分:0)
这是结合了两个答案的解决方案。
import scala.language.implicitConversions
object MapHelpers {
implicit def optionStringToString(maybeS: Option[String]): String = maybeS.getOrElse("")
implicit class MapWithIntKey(val m: Map[String, String]) extends Map[String, String] {
override def get(key: String): Option[String] = {
val prefix = "(.*?)\\d+".r
m.get(key).orElse{
key match {
case prefix(p) => m.get(p)
case _ => None
}
}
}
def +[V1 >: String](kv: (String, V1)): scala.collection.immutable.Map[String, V1] = m + kv
def -(key: String): scala.collection.immutable.Map[String, String] = m - key
def iterator: Iterator[(String, String)] = m.iterator
}
}
object App {
import MapHelpers._
def testMapImplicit(): Unit = {
val myMap: MapWithIntKey = Map("foo" -> "bar", "faz" -> "baz")
println("result - number match ", myMap.get("foo1"))
println("result - exact match ", myMap.get("foo"))
}
}
App.testMapImplicit()