【问题标题】:Elegant way to validate scala map验证 scala 地图的优雅方式
【发布时间】:2017-06-06 04:16:16
【问题描述】:

我的程序收到一个 scala 映射,要求是验证这个映射(键值对)。例如:验证键值,将其值更改为可接受的格式等。在极少数情况下,我们会在将映射传递到下层之前更新键。它并不总是需要更新此映射,但只有当我们检测到有任何不受支持的键或值时。但是,我们必须检查所有键/值对。我正在做这样的事情:

private def updateMap ( parameters: Map[String, String]): Map[String, String] = {

parameters.map{

  case(k,v) => k match { case "checkPool" =>


    (k, (if (k.contains("checkPool"))
      v match {
        case "1" => "true"
        case _ => "false"
      }
    else v))

  case "Newheader" => (k.replace("Newheader","header"),v)
  case _ =>(k,v)
  }


  case _ => ("","")
}

}

像这样,代码会增加以进行验证并将键/值转换为支持的键/值。有没有更简洁的方式在 Scala 中对地图进行验证?

谢谢

【问题讨论】:

  • 键值是等于“NewHeader”和“checkPool”还是它们是较长字符串的一部分(您使用包含函数)?如果它们是键弦的一部分,那么 Ramesh Maharjan 的独奏很好,否则存在更清洁的方式。

标签: scala apache-spark scala-collections


【解决方案1】:

如果你把所有的模式都放在一个上面会更清楚:

parameters.map{
  case (k@"checkPool", "1") => k -> "true"
  case (k@"checkPool", _") => k -> "false"
  case ("Newheader", v) => "header" -> v
  // put here all your other cases
  case (k, v) => k -> v  //last possible case, if nothing other matches
}

为清楚起见,您还可以将不同的验证器放在偏函数中:

type Validator = PartialFunction[(String, String), (String, String)
val checkPool: Validator = {
  case (k@"checkPool", "1") => k -> "true"
  case (k@"checkPool", _") => k -> "false"
}
val headers: Validator = {
  case ("Newheader", v) => "header" -> v
}

然后将所有验证器一个接一个地放入您的map

parameters.map(
  checkPool orElse
   headers orElse
   ... orElse
   PartialFunction(identity[(String, String)]) //this is the same as case (k, v) => k -> v
)

【讨论】:

    【解决方案2】:

    简单的if else条件匹配似乎是最好的选择。

    def updateMap(parameters: Map[String, String]): Map[String, String] = {
      parameters.map(kv => {
        var key = kv._1
        var value = kv._2
        if(key.contains("checkPool")){
          value = if(value.equals("1")) "true" else "false"
        }
        else if(key.contains("Newheader")){
          key = key.replace("Newheader", "header")
        }
        (key, value)
      })
    }
    

    您可以添加更多elseif条件

    【讨论】:

      【解决方案3】:

      如果我理解正确,您的目标是减少将补丁应用到地图所需的代码量。

      正如另一个答案中提出的那样,这个想法是在定义更新规则和应用它们之间分开,这样您就可以在需要新更新时增加第二个,而不必重新定义应用规则的方式。

      我们可以将规则的定义建模为从键到定义转换的函数的映射。转换本身可以在简单函数之上建模。

      然后可以将应用规则定义为应用这些函数并将它们应用于它们已定义的键。

      sealed trait Patch extends (((String, String)) => (String, String))
      
      final class PatchValue(update: String => String) extends Patch {
        override def apply(pair: (String, String)): (String, String) = pair.copy(_2 = update(pair._2))
      }
      
      final class PatchKey(update: String => String) extends Patch {
        override def apply(pair: (String, String)): (String, String) = pair.copy(_1 = update(pair._1))
      }
      
      final class MalleableMapPatching(rules: Map[String, Patch]) {
      
        def updatePair(pair: (String, String)): (String, String) = {
          rules.filterKeys(_ == pair._1).foldLeft(pair) {
            case (pair, (_, patch)) =>
              patch(pair)
          }
        }
      
        def update(parameters: Map[String, String]): Map[String, String] =
          parameters.map(updatePair)
      
      }
      

      现在您已经完成了规则定义部分,我们可以创建一个带有一组准备应用的规则的具体对象。

      val patchingRules =
        Map(
          "checkPool" -> new PatchValue(value => if (value == "1") "true" else "false"),
          "Newheader" -> new PatchKey(_ => "header")
        )
      
      val patcher = new MalleableMapPatching(patchingRules)
      

      如果您有兴趣更好地了解我的解决方案,here is the code on Github;我创建了一组测试来验证我对您的代码的重构。

      希望我的观点对您有所帮助。

      【讨论】:

        猜你喜欢
        • 2011-10-03
        • 2019-04-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-25
        • 2010-10-21
        • 1970-01-01
        • 2017-11-03
        • 1970-01-01
        相关资源
        最近更新 更多