Scala Map-使用地图功能替换键->值

时间:2019-01-28 23:32:01

标签: scala

我只想更改键key1key2的键和值(当它们的值分别为val1val2时)进行变革)。我可以使用以下代码来做到这一点,但我认为这不是非常优雅或有效。

是否有更好的方法来做同样的事情,也许只使用一个应用在 map 上的.map函数?

代码:

val map = Map(
  "key1" -> "val1",
  "key2" -> "val2",
  "otherkey1" -> "otherval1"
)

val requiredKeys = List("key1", "key2")

val interestingMap = map.filterKeys(requiredKeys.contains) // will give ("key1" -> "val1", "key2" -> "val2").

val changedIfMatched =
  if (interestingMap.get("key1").get.equalsIgnoreCase("val1") && interestingMap.get("key2").get.equalsIgnoreCase("val2"))
    Map("key1" -> "newval1", "key2" -> "newval2")
  else
    interestingMap

print(map ++ changedIfMatched) // to replace the old key->values with the new ones, if any.

还能通过++操作来更新旧的key->value映射吗?

5 个答案:

答案 0 :(得分:2)

这是一种检查两个键值对是否匹配的方法。

编辑:向mapValues类添加了Map方法。此技术可用于进一步检查地图的值。

val m = Map("key1" -> "val1", "key2" -> "VAL2", "otherkey1" -> "otherval1")
val oldKVs = Map("key1" -> "val1", "key2" -> "val2")
val newKVs = Map("newkey1" -> "newval1", "newkey2" -> "newval2")

implicit class MapImp[T,S](m: Map[T,S]) {
  def mapValues[R](f: S => R) = m.map { case (k,v) => (k, f(v)) }
  def subsetOf(m2: Map[T,S]) = m.toSet subsetOf m2.toSet
}

def containsKVs[T](m: Map[T,String], sub: Map[T,String]) =
  sub.mapValues(_.toLowerCase) subsetOf m.mapValues(_.toLowerCase)

val m2 = if (containsKVs(m, oldKVs)) m -- oldKVs.keys ++ newKVs else m

println(m2)
// Map(otherkey1 -> otherval1, newkey1 -> newval1, newkey2 -> newval2)

利用了您可以将Map转换为Set的{​​{1}}的事实。

答案 1 :(得分:2)

只需提前进行检查:

 map
   .get("k1").filter(_.equalsIgnoreCase("v1"))
   .zip(map.get("k2").filter(_.equalsIgnoreCase("v2")))
   .headOption
   .fold(map) { _ =>
      map ++ Map("key1" -> "newVal1", "key2" -> "newVal2")
   }

答案 2 :(得分:0)

您可以使用以下代码:

val interestingMap = 
    if(map.getOrElse("key1", "") == "val1" && map.getOrElse("key2", "") == "val2")
      map - "key1" - "key2" + ("key1New" -> "val1New") + ("key2New" -> "val2New")
    else map

可以根据您的特定需求调整检查部分(if语句)。

如果映射中不存在任何这些键值对,则将返回原始映射,否则,您将获得一个新的映射,并在请求的密钥处进行了两次更新。

关于效率,只要只更新两个键,我认为使用+直接添加元素与使用++运算符覆盖钥匙批发。如果您的地图很大,从长远来看,也许使用可变地图被证明是更好的选择。

答案 3 :(得分:0)

修改后的问题的答案

val map = Map(
  "key1" -> "val1",
  "key2" -> "val2",
  "otherkey1" -> "otherval1"
)

val requiredVals = List("key1"->"val1", "key2"->"val2")
val newVals = List("newval1", "newval2")

val result =
  if (requiredVals.forall{ case (k, v) => map.get(k).exists(_.equalsIgnoreCase(v)) }) {
    map ++ requiredVals.map(_._1).zip(newVals)
  } else {
    map
  }

此解决方案使用forall通过依次测试每一对来检查是否在地图中找到requiredKeys中的所有键/值对。

对于每个键/值对(k, v),它使用键在地图上执行get,以将当前值检索为Option[String]。如果找不到密钥,则为None;如果找到密钥,则为Some(s)

然后,代码在exists上调用Option[String]。如果值为false(找不到键),则此方法将返回None,否则它将返回传递给它的测试结果。测试是_.equalsIgnoreCase(v),它对Option_)的内容和requireKeys列表(v)中的值进行不区分大小写的比较。

如果该测试失败,则返回map的原始值。

如果此测试成功,则返回映射的修改版本。表达式requiredVals.map(_._1)返回requireVals列表中的键,而zip(newVals)将新值与原始键相关联。生成的值列表将使用++添加到地图中,这将用新值替换现有值。

原始答案

val map = Map(
  "key1" -> "val1",
  "key2" -> "val2",
  "otherkey1" -> "otherval1"
)

val requiredVals = Map("key1"->"val1", "key2"->"val2")
val newVals = Map("newkey1" -> "newval1", "newkey2" -> "newval2")

val result =
  if (requiredVals.forall{ case (k, v) => map.get(k).exists(_.equalsIgnoreCase(v)) }) {
    map -- requiredVals.keys ++ newVals
  } else {
    map
  }

请注意,这用新的密钥替换了旧的密钥,这似乎就是所描述的。如果要保留原始键和值,只需删除“-requiredVals.keys”,它将添加新键而不删除旧键。

答案 4 :(得分:0)

我认为这将是解决该问题的最通用,最可行的解决方案。

object Solution1 extends App {

  val map = Map(
    "key1" -> "val1",
    "key2" -> "val2",
    "otherkey1" -> "otherval1"
  )

  implicit class MapUpdate[T](map: Map[T, T]) {
    def updateMapForGivenKeyValues: (Iterable[(T, T)], Iterable[(T, T)]) => Map[T, T] =
      (fromKV: Iterable[(T, T)], toKV: Iterable[(T, T)]) => {

        val isKeyValueExist: Boolean = fromKV.toIterator.forall {
          (oldKV: (T, T)) =>
            map.toIterator.contains(oldKV)
        }

        if (isKeyValueExist) map -- fromKV.map(_._1) ++ toKV else map
      }
  }


  val updatedMap = map.updateMapForGivenKeyValues(List("key1" -> "val1", "key2" -> "val2"),
    List("newKey1" -> "newVal1", "newVal2" -> "newKey2"))

  println(updatedMap)

}

因此,方法updateMapForGivenKeyValues采用旧键值和新键值元组的列表。如果该方法的第一个参数中提到的所有键值对都存在于映射中,则只有我们将使用该方法的第二个参数中提到的新键值对来更新映射。由于该方法是通用方法,因此可用于任何数据类型,例如String,Int,某些case类等。

我们可以轻松地将该方法用于不同类型的地图,而无需更改任何代码。