Первые часть импорта,
import qualified Data.Map as M
import qualified Data.Tree as T
import Data.List (foldl')
import Control.Arrow ((&&&))
import Data.Maybe (fromMaybe)
Далее, давайте предположим, что мы есть записи, которые имеют идентификатор и необязательный родительский идентификатор (корневые узлы не имеют родителя), и несут какую-то ценность:
data Rec a = Rec { recId :: Int
, recParentId :: Maybe Int
, recValue :: a
}
Нет ничего, что помешало бы нескольким узлам иметь Nothing
родительский идентификатор, поэтому мы могли бы найти более одного дерева, поэтому наша функция преобразования списка в дерево может выглядеть так:
toTree :: [Rec a] -> [T.Tree a]
toTree rs = ts where
Во-первых, давайте создадим карту от дополнительного родительского идентификатора в списке записей, которые имеют что родительский 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
функции передается значение и идентификатор узла мы хотим строить. Она возвращает значение, и список пар значение ребенка/Ид с помощью Map
мы строили раньше:
-- 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
В какой базе данных это? Postgres? Mysql? Oracle? Я ошибаюсь, полагая, что он использует SQL? – dave4420
База данных - это mysql. Если ничего не изменилось с mysql, я не думаю, что это может дать мне иерарический результат, т. Е. дерево и запрос рекурсивной базы данных будут работать, но это будет довольно дорого. – Guenni