需要帮助以函数式编程风格重构此 Scala 方法

Need help to refactor this scala method in functional programming style

我有这个 scala 方法可以根据一些参数构建地图:

def foo(name: Option[String], age: Option[Int], hasChilds: Option[Boolean], 
    childs: Option[List[Map[String, Any]]]): Map[String,Any] = {

    var m = Map[String, Any]()

    if (!name.isEmpty) m += ("name" -> name.get)
    if (!age.isEmpty) m += ("age" -> age.get)
    if (!hasChilds.isEmpty) m += ("hasChilds" -> hasChilds.get)
    if (!childs.isEmpty) m += ("childs" -> childs.get)

    m
}

不知道有没有办法重构代码更函数式的风格?

在这种情况下是否可以取消使用 var

一种方法包括不可变 Map 的扁平化,像这样,

def foo(name: Option[String], 
        age: Option[Int], 
        hasChilds: Option[Boolean], 
        childs: Option[List[Map[String, Any]]]): Map[String,Any] = {

  Map( ("name" -> name), 
       ("age" -> age),
       ("hasChilds" -> hasChilds),  
       ("childs" -> childs)).collect { case(a,Some(b)) => (a,b) }
}

另一种方法可以是

def foo(....) = 
   Map("name" -> name, "age" -> age, "hasChilds" -> hasChilds, "childs" ->  childs)
.filter(_._2 != None).mapValues(_.get)

正如@Dider所指出的,这也可以做到,类似于@enzyme解决方案

Map("name" -> name, "age" -> age, "hasChilds" -> hasChilds, "childs" -> childs)
.collect {case (k, Some(v)) => (k,v) }

Scala 通常支持类型化数据和不变性,而您在这里反对这两者。我不知道这张地图的上下文是什么,但我认为使用带有可选参数的 case 类会更惯用。例如:

case class Person(name: String, age: Option[Int], children: Seq[Person]) {
  def hasChildren: Boolean = !children.isEmpty
}

现在您可以使用可选名称变量按如下方式调用它。

val name: Option[String] = Option("suud")
val age: Option[Int] = Option(25)
val children: Seq[Person] = Seq.empty
val suud: Option[Person] = name map {Person(_, age, children)}

按照 foo 的编写方式,可以传入一个空的子项列表,其中包含一个布尔参数,表示地图有子项。将 hasChildren 写成 case 的方法 class 可以防止这种情况,因为布尔方法取决于它要提供的信息的集合。

如果你真的坚持在这里使用地图,你可以使用 MapBuilder 来获取不可变地图,或者直接导入并使用可变地图。

import scala.collection.mutable.MapBuilder

val builder: MapBuilder[String, Any, Map[String, Any]] = new MapBuilder(Map.empty)

if (!name.isEmpty) builder += ("name" -> name.get)
if (!age.isEmpty) builder += ("age" -> age.get)
if (!hasChilds.isEmpty) builder += ("hasChilds" -> hasChilds.get)
if (!childs.isEmpty) builder += ("childs" -> childs.get)

builder.result

现在生成器的结果是一个不可变的映射。如果你真的需要一个可变的地图,你可以:

import scala.collection.mutable

val m = mutable.Map[String, Any]()

现在您已经有了一个可变映射,可以使用它的 toMap 方法将其转换为不可变映射。