Reputation: 2750
I have a list of paths:
val paths = List(List("foo"), List("bar", "a"), List("bar", "b"))
I want to represent that in a scalaz Tree:
def pathsToTree(root: String, paths: List[List[String]]): Tree[String] = ???
Result:
pathsToTree("paths", paths)
=> "paths".node("foo".leaf, "bar".node("a".leaf, "b".leaf))
I've read some about TreeLoc
from http://eed3si9n.com/learning-scalaz/Tree.html but it seems pretty tedious using left/right or child indices. I imagine doing something like:
paths.foldLeft(root.node()) { case (acc: Tree[String], path: List[String]) =>
acc // how to add all the items from `path` to the tree?
}
It looks like I could use find
and setTree
or modifyTree
but that seems quite inefficient.
Upvotes: 1
Views: 357
Reputation: 16308
This is general builder:
def pathTree[E](root: E, paths: Seq[Seq[E]]): Tree[E] =
root.node(paths groupBy (_.head) map {
case (parent, subpaths) => pathTree(parent, subpaths collect {
case parent +: rest if rest.nonEmpty => rest
})
} toSeq: _*)
for online modifing we can define:
def addPath[E](path: Seq[E], tree: Tree[E]): Tree[E] = if (path.isEmpty) tree
else
tree match {
case Tree.Node(root, children) if children.exists(_.rootLabel == path.head) => root.node(
children map (subtree => if (subtree.rootLabel == path.head) addPath(path.tail, subtree) else subtree): _*
)
case Tree.Node(root, children) => root.node(children :+ path.init.foldRight(path.last.leaf)((root, sub) => root.node(sub)): _*)
}
so
val tree = pathTree("root", List(List("foo"), List("bar", "a"), List("bar", "b", "c")))
println(tree.drawTree)
yields
"root"
|
+- "foo"
|
`- "bar"
|
+- "b"
| |
| `- "c"
|
`- "a"
and println(addPath(Seq("foo","baz"), tree).drawTree)
prints
"root"
|
+- "foo"
| |
| `- "baz"
|
`- "bar"
|
+- "b"
| |
| `- "c"
|
`- "a"
Upvotes: 3