haskellrecursiontree

Building a forest from a list of "tree paths"


Starting from a list of "tree paths", for example:

[["a", "b", "c"], ["a", "b", "e"], ["f", "g"]]

I would like to build a forest ([Tree]):

a
|_ b
  |_ c
  |_ e
f
|_ g

This is my attempt so far. I have been constructing the building blocks in order to assemble the final solution:

module Main where

import Data.Tree

insertPath :: Eq a => [a] -> [Tree a] -> [Tree a]
insertPath [] ts = ts
insertPath (y:ys) [] = Node y [] : []
insertPath (y:ys) ((Node x ts):ts')
    | y /= x = Node x ts : insertPath ys ts'
    | otherwise = Node x (insertSubTrees ys ts) : insertPath ys ts'

insertSubTrees :: Eq a => [a] -> [Tree a] -> [Tree a]
insertSubTrees [] ts = ts
insertSubTrees (y:ys) [] = insertSubTrees ys [Node y []]
insertSubTrees (y:ys) (t:ts) = undefined

My questions are:


Solution

  • I think you overcomplicate things: if you reach the end of the path, we simply are done. So we can do this in one function:

    insertPath :: Eq a => [a] -> [Tree a] -> [Tree a]
    insertPath [] = id
    insertPath (p : ps) = go
      where
        go [] = [Node p (insertPath ps [])]
        go (n@(Node x xs) : ns)
          | p == x = Node x (insertPath ps xs) : ns
          | otherwise = n : go ns

    You can then generate the entire forest with a list of paths with:

    insertPaths :: Eq a => [Tree a] -> [[a]] -> [Tree a]
    insertPaths = foldl (flip insertPath)

    With the sample data, we thus generate a forest with:

    ghci> insertPaths [] [["a", "b", "c"], ["a", "b", "e"], ["f", "g"]]
    [Node {rootLabel = "a", subForest = [Node {rootLabel = "b", subForest = [Node {rootLabel = "c", subForest = []},Node {rootLabel = "e", subForest = []}]}]},Node {rootLabel = "f", subForest = [Node {rootLabel = "g", subForest = []}]}]