最初にいくつかのインポート、
import qualified Data.Map as M
import qualified Data.Tree as T
import Data.List (foldl')
import Control.Arrow ((&&&))
import Data.Maybe (fromMaybe)
次に、ID とオプションの親 ID (ルートノードには親がない) を持つレコードがあり、何らかの値を持っていると仮定しましょう。
data Rec a = Rec { recId :: Int
, recParentId :: Maybe Int
, recValue :: a
}
複数のノードがNothing
親 ID を持つことを妨げるものは何もないため、複数のツリーが見つかる可能性があるため、リストをツリーに変換する関数は次のようになります。
toTree :: [Rec a] -> [T.Tree a]
toTree rs = ts where
まず、オプションの親 ID から、その親 ID を持つレコードのリストへのマップを作成しましょう。
-- gs :: M.Map (Maybe Int) [Rec a]
gs = foldl' f M.empty rs where
f m r = M.insertWith (const (r:)) (recParentId r) [r] m
次に、ダミー ルート ノードから始まるツリーを展開してみましょう。その子ノードは、関心のあるツリーのルートになります。ダミー ルート ノードには値がないことに注意してください。したがって、以下を使用しますundefined
。
-- t :: T.Tree a
t = T.unfoldTree mkNode (undefined, Nothing)
このmkNode
関数には、構築したいノードの値と ID が渡されます。Map
値と、以前に作成した を使用して子の値/ID ペアのリストを返します。
-- mkNode :: (a, Maybe Int) -> (a, [(a, Maybe Int)])
mkNode (a, i) = (a, map (recValue &&& Just . recId)
. fromMaybe []
. M.lookup i $ gs)
最後に、ダミーのルート ノードを破棄し、関心のあるツリーのルートとして直接の子を返すことができます。
ts = T.subForest t
そして、ここにテストがあります:
main = mapM_ (putStrLn . T.drawTree)
$ toTree [ Rec 0 Nothing "rootA"
, Rec 1 (Just 0) "rootA.childA"
, Rec 2 (Just 0) "rootA.childB"
, Rec 3 (Just 1) "rootA.childA.childA"
, Rec 4 (Just 1) "rootA.childA.childB"
, Rec 5 (Just 2) "rootA.childB.childA"
, Rec 6 (Just 2) "rootA.childB.childB"
, Rec 7 Nothing "rootB"
, Rec 8 (Just 7) "rootB.childA"
, Rec 9 (Just 7) "rootB.childB"
, Rec 10 (Just 8) "rootB.childA.childA"
, Rec 11 (Just 8) "rootB.childA.childB"
, Rec 12 (Just 9) "rootB.childB.childA"
, Rec 13 (Just 9) "rootB.childB.childB"
]
生成するもの:
rootB
|
+- rootB.childB
| |
| +- rootB.childB.childB
| |
| `- rootB.childB.childA
|
`- rootB.childA
|
+- rootB.childA.childB
|
`- rootB.childA.childA
rootA
|
+- rootA.childB
| |
| +- rootA.childB.childB
| |
| `- rootA.childB.childA
|
`- rootA.childA
|
+- rootA.childA.childB
|
`- rootA.childA.childA