使用选项值构造Map

时间:2012-07-10 15:45:30

标签: scala

我想建立一个这样的地图:

  def one = "one"
  def two = "two"
  def three = Some("three")

  Map[String, String]("one" -> one, "two" -> two, "three" -> three)

这将无法编译,因为方法三返回Option而不是String。 我可以这样做:

Map[String, String]("one" -> one, "two" -> two) ++ three.map(t => Map("three" -> t)).getOrElse(Map.empty[String, String])

现在它只会在列表中添加选项。

但必须有一种更优雅的方式。 (例如,lift-json知道如何在构造JValue时过滤掉Options)。

有什么建议吗? (P.S.我在这里简化了问题)

4 个答案:

答案 0 :(得分:7)

Map("one" -> one, "two" -> two) ++ three.map("three" -> _)也可以。

答案 1 :(得分:4)

您有两种地图,例如:

val map1 = Map("one" -> 1, "two" -> 2)
val map2 = Map("three" -> Some(3), "four" -> None)

你可以解包后者:

map2.collect { case (k,Some(v)) => (k,v) }

并合并地图:

map1 ++ map2.collect{ case (k,Some(v)) => (k,v) }

答案 2 :(得分:2)

如果你知道哪些值是选项而哪些不是,你只需在方法调用后直接调用getOrElse

Map[String, String]("one" -> one, "two" -> two, "three" -> three.getOrElse("empty"))

如果您不知道哪些方法会返回一个Option,您可以使用隐式转换从Option中提取值,或者如果它是None则将其设置为默认值:

implicit def optToStr(a : Option[String]) : String = a.getOrElse("empty")
Map[String, String]("one" -> one, "two" -> two, "three" -> three)

之后您也可以使用地图上的过滤器删除默认键值对,尽管这不是很优雅(在这种情况下可能有其他人知道更好的解决方案)。

答案 3 :(得分:0)

为了给您的客户提供一个很好的界面,您可以扩展其中一个Map来执行解包:

class MyMap[A, B](private val tuples: (A, Option[B])*)
    extends collection.DefaultMap[A, B] {

  private val container =
    new collection.mutable.HashMap[A, B]()

  container ++= tuples collect {case (k, Some(v)) => (k, v)}

  def iterator = container.iterator
  def get(id: A) = container.get(id)
  override def size = container.size
}

将此与隐式相结合,将成对(A, B)转换成对(A, Option[B])

implicit def pairToPairWithSomeValue[A, B](t: (A, B)): (A, Option[B]) =
  (t._1, Some(t._2))

并将其用作:

def one = "one"
def two = "two"
def three = Some("three")
def four = None


val mm = new MyMap("one" -> one, "two" -> two, "three" -> three,
                   "four" -> four)

mm foreach println
  /* (two,two), (one,one), (three,three) */