将多个选项转换为列表的惯用方式

时间:2018-12-07 15:20:04

标签: scala

我经常遇到将大多数可选的设置类对象转换为字符串或列表(例如转换为用于启动某些外部程序的参数)的问题。通常,我最终会得到与此类似的命令性代码:

case class Options(host: Option[String], user: Option[String], password: Option[String]) {
  def argsString = {
    val args = new ArrayBuffer[String]()

    if (host.nonEmpty) {
      args+="--host"
      args+=host.get
    }

    if (user.nonEmpty) {
      args+="--user"
      args+=user.get
    }

    if(password.nonEmpty) {
      args+="--password"
      args+=password.get
    }

    args mkString " "
  }
}

是否有更类似于FP的方式?

7 个答案:

答案 0 :(得分:5)

有几种方法。这应该起作用:

case class Options(host: Option[String], user: Option[String], password: Option[String]) {
  def argsString: String = { 
    host.map("--host " + _) ++ 
    user.map("--user " + _) ++ 
    password.map("--password " + _) 
  }.mkString(" ")
}

答案 1 :(得分:1)

还有另一种可能性,基于通过模式匹配过滤(collect)的(参数名称,参数值)元组的列表:

case class Options(host: Option[String], user: Option[String], password: Option[String]) {

  def argsString(): String =
    Seq(("host", host), ("user", user), ("password", password))
      .collect { case (arg, Some(value)) => s"--$arg $value" }
      .mkString(" ")
}

答案 2 :(得分:1)

借助Scala 2.13,我们可以将概念推广到字段为Option[String]的任何案例类:

case class Options(host: Option[String], user: Option[String], password: Option[String]) {

  def argsString(): String =
    (productElementNames zip productIterator)
      .collect { case (arg, Some(value)) => s"--$arg $value" }
      .mkString(" ")
}

这是由于以下事实:使用Scala 2.13,我们可以使用productElementNames检索案例类的字段名称:

Options(Some("localhost"), None, Some("abcd")).productElementNames.toList
// List("host", "user", "password")

多亏了productIterator,为案例类字段提供了压缩,这为我们提供了参数名称和值的迭代器:

Options(Some("localhost"), None, Some("abcd")).productIterator.toList
// List(Some(localhost), None, Some(abcd))

(productElementNames zip productIterator).toList
// List((host, Some("localhost")), (user, None), (password, Some("abcd")))

基于此,我们然后可以进行模式匹配以过滤出None,并从过滤后的参数中得出一个字符串,以获得:

--host localhost --password abcd

答案 3 :(得分:0)

case class Options(host: Option[String], user: Option[String], password: Option[String]) {
  def argsString: String = {
     Seq(
       host.map("--host " + _),
       user.map("--user " + _),
       password.map("--password " + _)
     )
     .flatten
     .mkString(" ")

  }
}

答案 4 :(得分:0)

我会用类似的方法来处理它:

case class Options(host: Option[String], user: Option[String], password: Option[String]) {
  def args: Seq[String] =
    host.toList.flatMap(h => List("--host", h)) ++
    user.toList.flatMap(u => List("--user", u)) ++
    password.toList.flatMap(p => List("--password", p))

  def argsString: String = args.mkString(" ")
}

我倾向于将构建的字符串移动到最外边缘,因为这意味着如果其他层想要操纵args,则不必拆分(或更糟地解析)构建的字符串。

答案 5 :(得分:0)

def argsString0 =
    Array(s"${host.fold("")(e => s"--host ${e}")}",
          s"${user.fold("")(e => s"--user ${e}")}",
          s"${password.fold("")(e => s"--password ${e}")}").mkString(" ")

答案 6 :(得分:-1)

怎么样?

case class Options(host: Option[String], user: Option[String], password: Option[String]) {
  def argsString =
    List(
      host.map(List("--host", _)),
      user.map(List("--user", _)),
      password.map(List("--password", _))
    ).flatten.flatten.mkString(" ")
}