在第一次出现之后断开flatMap

时间:2015-03-15 14:49:44

标签: scala collections

abstract class Node(id: Long, name: String) {
  def find(id: Long) = if (this.id == id) Some(this) else None
}
case class Contact(id: Long, name: String, phone: String) extends Node(id, name)
case class Group(id: Long, name: String, entries: Node*) extends Node(id, name) {
  override def find(id: Long): Option[Node] = {
    super.find(id) orElse entries.flatMap(e => e.find(id)).headOption
  }
}
val tree =
  Group(0, "Root"
      , Group(10, "Shop", Contact(11, "Alice", "312"))
      , Group(20, "Workshop"
          , Group(30, "Tyres", Contact(31, "Bob", "315"), Contact(32, "Greg", "319"))
          , Contact(33, "Mary", "302"))
      , Contact(1, "John", "317"))
println(tree.find(32))

树数据是从联系人(具有子组和联系人)构建的。我想找到一个具有特定 id 的节点。目前,我使用以下方式遍历 Group 的成员

entries.flatMap(e => e.find(id)).headOption

但它不是最佳的,因为我检查所有子条目而不是在第一次查找时中断。

我很感激你的魔法帮助 Scala Wold 。感谢。

2 个答案:

答案 0 :(得分:3)

您需要collectFirst,它会选择第一个匹配的元素并将其包装在Some中,或None如果找不到它。您也可以将entries转换为视图,以使评估变得懒惰。

entries.view.map(_.find(id)).collectFirst { case Some(node) => node }

它也适用于原始代码:

entries.view.flatMap(_.find(id)).headOption

答案 1 :(得分:0)

解决此问题的另一种方法是为数据结构提供遍历支持。总的来说它是一棵树,因此可以轻松遍历。请检查以下代码:

sealed abstract class Node(val id: Long, val name: String) extends Traversable[Node] {
  def foreach[U](f:Node => U) = this match {
    case x: Contact => f(x)
    case x: Group => f(x); x.entries.foreach(_ foreach f)
  }
}
case class Contact(override val id: Long, override val name: String, phone: String) extends Node(id, name) {
  override def toString = (id, name, phone).toString
}
case class Group(override val id: Long, override val name: String, entries: Node*) extends Node(id, name) {
  override def toString = (id, name).toString + entries.map(_.toString).mkString
}

val tree =
  Group(0, "Root"
    , Group(10, "Shop", Contact(11, "Alice", "312"))
    , Group(20, "Workshop"
      , Group(30, "Tyres", Contact(31, "Bob", "315"), Contact(32, "Greg", "319"))
      , Contact(33, "Mary", "302"))
    , Contact(1, "John", "317"))

println(tree) // (0,Root)(10,Shop)(11,Alice,312)(20,Workshop)(30,Tyres)(31,Bob,315)(32,Greg,319)(33,Mary,302)(1,John,317)
println(tree find {_.id == 32}) //Some((32,Greg,319))
println(tree map {_.name }) //List(Root, Shop, Alice, Workshop, Tyres, Bob, Greg, Mary, John)

好消息是,现在您可以使用Traversable[Node]特性的所有好处。另一方面,问题是我必须在case类中重写toString方法。所以我猜还有改进的余地。