【问题标题】:Returning multiple collections from flatMap从 flatMap 返回多个集合
【发布时间】:2012-05-30 15:03:35
【问题描述】:

我正在研究一种方法,该方法对多个项目有 3 种可能的结果:错误、无效和成功。对于其中的每一个,我需要返回一个 json 列表,标识哪些项目是错误的、无效的和成功的。

我目前的尝试如下。我已经使用Object 来表示我的对象所使用的类,因为完全解释需要太长时间。 Object 类有一个方法 process,它返回一个布尔值来指示成功或错误,并在对象无效时抛出异常:

def process(list: List[Objects]) = {
    val successIds = new ListBuffer[Int]();
    val errorIds = new ListBuffer[Int]();
    val invalidIds = new ListBuffer[Int]();

    list.foreach( item => {
        try {
            if (item.process) {
                successIds ++ item.id
            } else {
                errorIds ++ item.id
            }
        } catch {
            case e: Exception => invalidIds ++ item.id
        }
    })

    JsonResult(
        Map("success" -> successIds, 
            "failed" -> errorIds, 
            "invalid" -> invalidIds)
    ) 
}

使用可变数据结构的问题不是很“Scala-y”。我更愿意以更实用的方式构建这些列表,但我对 scala 还是很陌生。关于如何做到这一点的任何想法或提示?

我的想法是使用类似 flatMap 方法的方法,该方法采用集合元组并以与 flatMap 方法对单个集合相同的方式对它们进行整理:

def process(list: List[Objects]) = {

    val (success, error, invalid) = list.flatMap( item => {
        try {
            if (item.process) {
                (List(item.id), List.empty, List.empty)
            } else {
                (List.empty, List(item.id), List.empty)
            }
        } catch {
            case e: Exception => 
                (List.empty, List.empty, List(item.id))
        }
    })

    JsonResult(
        Map("success" -> success, 
            "failed" -> error, 
            "invalid" -> invalid)
    ) 
}

【问题讨论】:

  • 查看这个 Gist,它解释了使用应用函子进行非快速验证,由 ScalaZ 提供:gist.github.com/970717

标签: scala collections iteration


【解决方案1】:

flatMap 不是你需要的——你需要groupBy

def process(list: List[Objects]) = {

  def result(x: Objects) =  
    try if (x.process) "success" else "failed"
    catch {case _ => "invalid"}     

  JsonResult(list groupBy result mapValues (_ map (_.id)))
}

【讨论】:

  • +1 -- 对于这个特定问题,肯定比我的回答更好!
  • +1, "try if ... else ...",有什么语言不能做的吗?
  • 我花了一段时间才真正实现这一点,抱歉。做得很好,简单明了 +1
【解决方案2】:

总是有递归:

class Ob(val id: Int) { def okay: Boolean = id < 5 }


@annotation.tailrec def process(
  xs: List[Ob], 
  succ: List[Int] = Nil,
  fail: List[Int] = Nil,
  invalid: List[Int] = Nil
): (List[Int], List[Int], List[Int]) = xs match {
  case Nil => (succ.reverse, fail.reverse, invalid.reverse)
  case x :: more =>
    val maybeOkay = try { Some(x.okay) } catch { case e: Exception => None }
    if (!maybeOkay.isDefined) process(more, succ, fail, x.id :: invalid)
    else if (maybeOkay.get)   process(more, x.id :: succ, fail, invalid)
    else                      process(more, succ, x.id :: fail, invalid)
}

正如人们所希望的那样(如果您不关心顺序,请跳过相反的步骤):

scala> process(List(new Ob(1), new Ob(7), new Ob(2), 
   new Ob(4) { override def okay = throw new Exception("Broken") }))

res2: (List[Int], List[Int], List[Int]) = (List(1,2),List(7),List(4))

【讨论】:

    【解决方案3】:

    适应使其在没有“对象”的情况下编译

    def procex (item: String): Boolean = ((9 / item.toInt) < 1)
    
    def process (list: List[String]) = {
        val li: List[(Option[String], Option[String], Option[String])] = list.map (item => {
            try {
                if (procex (item)) {
                    (Some (item), None, None)
                } else {
                    (None, Some (item), None)
                }
            } catch {
                case e: Exception => 
                    (None, None, Some (item))
            }
        })
        li
    }
    // below 10 => failure
    val in = (5 to 15).map (""+_).toList
    // 0 to throw a little exception
    val ps = process ("0" :: in)
    
    val succeeders = ps.filter (p=> p._1 != None).map (p=>p._1)
    val errors     = ps.filter (p=> p._2 != None).map (p=>p._2)
    val invalides  = ps.filter (p=> p._3 != None).map (p=>p._3)
    

    什么不起作用:

    (1 to 3).map (i=> ps.filter (p=> p._i != None).map (p=>p._i))
    

    _i 不起作用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-27
      • 2021-10-25
      • 2019-06-28
      相关资源
      最近更新 更多