简体   繁体   English

如何检查List [Option [_]]中是否没有None并返回元素的名称?

[英]How to check if there's None in List[Option[_]] and return the element's name?

I have multiple Option 's. 我有多个Option I want to check if they hold a value. 我要检查它们是否具有值。 If an Option is None , I want to reply to user about this. 如果OptionNone ,那么我想就此回复用户。 Else proceed. 否则继续。

This is what I have done: 这是我所做的:

val name:Option[String]
val email:Option[String]
val pass:Option[String]
val i = List(name,email,pass).find(x => x match{
  case None => true
  case _ => false
})
i match{
  case Some(x) => Ok("Bad Request")
  case None => {
    //move forward
  }
}

Above I can replace find with contains , but this is a very dirty way. 在上面我可以用contains代替find ,但这是一种非常肮脏的方式。 How can I make it elegant and monadic? 如何使它优雅而单调?

Edit: I would also like to know what element was None . 编辑:我也想知道什么元素是None

Another way is as a for-comprehension: 另一种理解方式是:

val outcome = for {
  nm <- name
  em <- email
  pwd <- pass
  result = doSomething(nm, em, pwd) // where def doSomething(name: String, email: String, password: String): ResultType = ???
} yield (result)

This will generate outcome as a Some(result) , which you can interrogate in various ways (all the methods available to the collections classes: map, filter, foreach, etc.). 这将以Some(result)生成outcome ,您可以用各种方式(所有可用于collections类的方法:map,filter,foreach等)进行查询。 Eg: 例如:

outcome.map(Ok(result)).orElse(Ok("Bad Request"))
  val ok = Seq(name, email, pass).forall(_.isDefined)
val response = for {
  n <- name
  e <- email
  p <- pass
} yield {
  /* do something with n, e, p */
}
response getOrElse { /* bad request /* }

Or, with Scalaz: 或者,使用Scalaz:

val response = (name |@| email |@| pass) { (n, e, p) =>
  /* do something with n, e, p */
}
response getOrElse { /* bad request /* }

If you want to reuse the code, you can do 如果您想重复使用代码,则可以

def allFieldValueProvided(fields: Option[_]*): Boolean = fields.forall(_.isDefined)

If you want to know all the missing values then you can find all missing values and if there is none, then you are good to go. 如果您想知道所有缺少的值,那么可以找到所有缺少的值,如果没有,那么您就很好了。

def findMissingValues(v: (String, Option[_])*) = v.collect { 
  case (name, None) => name
}

val missingValues = findMissingValues(("name1", option1), ("name2", option2), ...)

if(missingValues.isEmpty) {
  Ok(...)
} else {
  BadRequest("Missing values for " + missingValues.mkString(", ")))
}
if ((name :: email :: pass :: Nil) forall(!_.isEmpty)) {
} else {
   // bad request
}

I think the most straightforward way would be this: 我认为最直接的方法是这样的:

(name,email,pass) match {
  case ((Some(name), Some(email), Some(pass)) => // proceed
  case _ => // Bad request
}

A version with stone knives and bear skins: 有石刀和熊皮的版本:

import util._

object Test extends App {

  val zero: Either[List[Int], Tuple3[String,String,String]] = Right((null,null,null))
  def verify(fields: List[Option[String]]) = {
    (zero /: fields.zipWithIndex) { (acc, v) => v match {
        case (Some(s), i) => acc match {
          case Left(_)  => acc
          case Right(t) =>
            val u = i match {
              case 0 => t copy (_1 = s)
              case 1 => t copy (_2 = s)
              case 2 => t copy (_3 = s)
            }
            Right(u)
        }
        case (None, i) =>
          val fails = acc match {
            case Left(f)  => f
            case Right(_) => Nil
          }
          Left(i :: fails)
      }
    }
  }
  def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
  def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)

  val name:Option[String] = Some("Bob")
  val email:Option[String]= None
  val pass:Option[String] = Some("boB")

  val res = verify(List(name,email,pass))
  res.fold(fail, (consume _).tupled)
  val res2 = verify(List(name, Some("bob@bob.org"),pass))
  res2.fold(fail, (consume _).tupled)
}

The same thing, using reflection to generalize the tuple copy. 同样,使用反射来概括元组副本。

The downside is that you must tell it what tuple to expect back. 缺点是您必须告诉它期望返回的元组。 In this form, reflection is like one of those Stone Age advances that were so magical they trended on twitter for ten thousand years. 在这种形式下,反思就像石器时代的进步之一,它们是如此神奇,以至于在推特上发展了一万年。

  def verify[A <: Product](fields: List[Option[String]]) = {
    import scala.reflect.runtime._
    import universe._
    val MaxTupleArity = 22
    def tuple = {
      require (fields.length <= MaxTupleArity)
      val n = fields.length
      val tupleN = typeOf[Tuple2[_,_]].typeSymbol.owner.typeSignature member TypeName(s"Tuple$n")
      val init = tupleN.typeSignature member nme.CONSTRUCTOR
      val ctor = currentMirror reflectClass tupleN.asClass reflectConstructor init.asMethod
      val vs = Seq.fill(n)(null.asInstanceOf[String])
      ctor(vs: _*).asInstanceOf[Product]
    }
    def zero: Either[List[Int], Product] = Right(tuple)
    def nextProduct(p: Product, i: Int, s: String) = {
      val im = currentMirror reflect p
      val ts = im.symbol.typeSignature
      val copy = (ts member TermName("copy")).asMethod
      val args = copy.paramss.flatten map { x =>
        val name = TermName(s"_$i")
        if (x.name == name) s
        else (im reflectMethod (ts member x.name).asMethod)()
      }
      (im reflectMethod copy)(args: _*).asInstanceOf[Product]
    }
    (zero /: fields.zipWithIndex) { (acc, v) => v match {
        case (Some(s), i) => acc match {
          case Left(_)  => acc
          case Right(t) => Right(nextProduct(t, i + 1, s))
        }
        case (None, i) =>
          val fails = acc match {
            case Left(f)  => f
            case Right(_) => Nil
          }
          Left(i :: fails)
      }
    }.asInstanceOf[Either[List[Int], A]]
  }

  def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
  def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)

  val name:Option[String] = Some("Bob")
  val email:Option[String]= None
  val pass:Option[String] = Some("boB")

  type T3 = Tuple3[String,String,String]
  val res = verify[T3](List(name,email,pass))
  res.fold(fail, (consume _).tupled)
  val res2 = verify[T3](List(name, Some("bob@bob.org"),pass))
  res2.fold(fail, (consume _).tupled)

I know this doesn't scale well, but would this suffice? 我知道这不能很好地扩展,但是足够了吗?

(name, email, pass) match {
  case (None, _, _) => "name"
  case (_, None, _) => "email"
  case (_, _, None) => "pass"
  case _ => "Nothing to see here"
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从 List[Option] 中过滤掉 None? - How to filter None's out of List[Option]? 如何检查Future列表中是否不存在[Option] - How to check if a None exists in a List of Future[Option] 如何解决json4s错误,以便Option [Seq [_]]将反序列化为None而不是Some(List()) - How do I workaround a json4s bug so that an Option[Seq[_]] will deserialize into a None instead of Some(List()) 如果找不到元素,为什么Scala的索引方法返回-1而不是None? - Why do Scala's index methods return -1 instead of None if the element is not found? 如何返回选项[列表]? - How to return Option[List]? 如何按字母顺序对Option [String]的case类进行排序,忽略None? - How to sort case classes with Option[String] in alphabetical order ignoring None's? 如何模拟没有返回值的方法是Option [SomeCaseClassDefinedInsideThisClass]的方法的返回 - How to mock return of None of method which return type is Option[SomeCaseClassDefinedInsideThisClass] 为什么“无”中断包含“选项”的“理解”? - Why does 'None' interrupt 'for' comprehension containing 'Option's? 反序列化选项返回 None 而不是 json4s 中的异常 - Deserializing Option returns None instead of an exception in json4s 有没有在未初始化/无值时将值分配给scala选项的最佳实践? - Is there a best practice to assign a value to a scala Option when it's not initialized/None?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM