A multiway tree or rosetree is a labeled tree where each node can have zero, one or more children, each of which represents a tree in its own right.
The root of the tree is always labeled, so a tree always has at least one label.
As an example, such a structure could represent a directory structure:
tree "root"
[ tree "home"
[ tree "user1" []
, tree "user2" []
]
, tree "etc" []
, tree "var"
[ tree "log" []
]
]
In a sense, Html msg
is pretty similar to how such trees look, but they can be
used to represent other things. A nested menu structure, or a sitemap, or any
other structure where a single root is connected to children which can each have
children of their own, and so on.
Represents a multiway tree. Each node in the tree holds a piece of
information (the label
) and a list of children, each of which is a tree.
singleton : a -> Tree a
Creates a singleton tree. This corresponds to tree v []
.
singleton 5
|> label
--> 5
singleton "foo"
|> children
--> []
tree : a -> List (Tree a) -> Tree a
Construct a tree from a label and a list of children.
tree 5 []
--> singleton 5
tree 5
[ singleton 1
, singleton 2
, tree 3
[ singleton 4
, singleton 5
]
]
|> count
--> 6
label : Tree a -> a
Gives you the label of a tree.
tree "hello" [ singleton "world", singleton "etc" ]
|> label
--> "hello"
children : Tree a -> List (Tree a)
Returns the children of a tree as a list.
singleton "heh"
|> children
--> []
tree "hello" [ singleton "world", singleton "etc" ]
|> children
--> [ singleton "world", singleton "etc" ]
mapLabel : (a -> a) -> Tree a -> Tree a
Execute a function on the label of this tree.
tree "hello" [ singleton "world", singleton "etc" ]
|> mapLabel String.toUpper
--> tree "HELLO" [ singleton "world", singleton "etc" ]
replaceLabel : a -> Tree a -> Tree a
Replace the label of this tree.
singleton "foo"
|> replaceLabel "bar"
--> singleton "bar"
mapChildren : (List (Tree a) -> List (Tree a)) -> Tree a -> Tree a
Execute a function on the children of a tree.
tree "lower1"
[ singleton "upper1"
, tree "upper2" [ singleton "lower2"]
, singleton "upper3"
]
|> mapChildren (List.map (mapLabel String.toUpper))
--> tree "lower1"
--> [ singleton "UPPER1"
--> , tree "UPPER2" [ singleton "lower2"]
--> , singleton "UPPER3"
--> ]
replaceChildren : List (Tree a) -> Tree a -> Tree a
Replace the children of a tree.
tree "hello" [ singleton "world" ]
|> replaceChildren [ singleton "everyone" ]
--> tree "hello" [ singleton "everyone" ]
prependChild : Tree a -> Tree a -> Tree a
Prepend a single child to a tree.
tree "hello" [ singleton "everyone" ]
|> prependChild (singleton "dear")
--> tree "hello" [ singleton "dear", singleton "everyone" ]
appendChild : Tree a -> Tree a -> Tree a
Append a child to a tree. Note that this uses children ++ [ newChild ]
under the hood so use sparingly.
tree "hello" [ singleton "you" ]
|> appendChild (singleton "and you!")
--> tree "hello" [ singleton "you", singleton "and you!" ]
foldl : (a -> b -> b) -> b -> Tree a -> b
Fold over all the labels in a tree, left to right, depth first.
tree "Hello "
[ singleton "world "
, tree "and "
[ singleton "you "
, singleton "and "
, singleton "you"
]
, singleton "!"
]
|> foldl (\label acc -> acc ++ label) ""
--> "Hello world and you and you!"
foldr : (a -> b -> b) -> b -> Tree a -> b
Fold over all the labels in a tree, right to left, depth first.
tree 1
[ singleton 2
, tree 3
[ singleton 4
, singleton 5
]
, singleton 6
]
|> foldr (::) []
--> [ 1, 2, 3, 4, 5, 6 ]
count : Tree a -> Basics.Int
Count the labels in a tree.
singleton "foo"
|> count
--> 1
tree "foo" [ singleton "bar", singleton "baz" ]
|> count
--> 3
flatten : Tree a -> List a
Flattens the tree into a list. This is equivalent to foldr (::) []
map : (a -> b) -> Tree a -> Tree b
Run a function on every label in the tree.
tree 1
[ singleton 2
, tree 3 [ singleton 4 ]
, singleton 5
]
|> map (\x -> String.fromInt (x * 2))
--> tree "2"
--> [ singleton "4"
--> , tree "6" [ singleton "8" ]
--> , singleton "10"
--> ]
indexedMap : (Basics.Int -> a -> b) -> Tree a -> Tree b
Run a function on every label in the tree while getting access to the
"index" of the label. This looks at thing in the same order as foldl
.
tree "foo"
[ singleton "bar"
, tree "baz" [ singleton "hello", singleton "world" ]
, singleton "qlux"
]
|> indexedMap (\idx val -> String.fromInt idx ++ " - " ++ val)
--> tree "0 - foo"
--> [ singleton "1 - bar"
--> , tree "2 - baz"
--> [ singleton "3 - hello"
--> , singleton "4 - world"
--> ]
--> , singleton "5 - qlux"
--> ]
mapAccumulate : (s -> a -> ( s, b )) -> s -> Tree a -> ( s, Tree b )
Map a function over every note while accumulating some value.
tree 1
[ singleton 2
, tree 3 [ singleton 4 ]
]
|> mapAccumulate (\acc label -> ( acc + label, String.fromInt label)) 0
--> ( 10
--> , tree "1"
--> [ singleton "2"
--> , tree "3" [ singleton "4" ]
--> ]
--> )
map2 : (a -> b -> c) -> Tree a -> Tree b -> Tree c
Map over 2 trees. Much like List.map2
, the result will be truncated to the shorter result.
left : Tree Int
left =
tree 3
[ singleton 5
, tree 6 [ singleton 12 ]
, singleton 4
]
right : Tree Int
right =
tree 8
[ tree 5 [ singleton 9 ]
, singleton 3
]
map2 (\x y -> x + y) left right
--> tree 11
--> [ singleton 10
--> , singleton 9
--> ]
indexedMap2 : (Basics.Int -> a -> b -> c) -> Tree a -> Tree b -> Tree c
Like map2
, but with the "index" added as the first argument.
mapAccumulate2 : (s -> a -> b -> ( s, c )) -> s -> Tree a -> Tree b -> ( s, Tree c )
Allows mapping over 2 trees while also accumulating a value.
left : Tree Int
left =
tree 3
[ singleton 5
, tree 6 [ singleton 12 ]
, singleton 4
]
right : Tree Int
right =
tree 8
[ tree 5 [ singleton 9 ]
, singleton 3
]
mapAccumulate2 (\sum x y -> ( sum + x + y, x + y )) 0 left right
--> ( 30
--> , tree 11
--> [ singleton 10
--> , singleton 9
--> ]
--> )
andMap : Tree (a -> b) -> Tree a -> Tree b
Given a tree of functions and a tree of values, applies the functions to the matching labels in the tree of values, truncating branches to match the common shape of the trees.
unfold : (b -> ( a, List b )) -> b -> Tree a
Create a tree from a seed.
Running the function on the seed should return a label and a list of seeds to use for the children.
For example, this function takes and int, and uses the string representation of that int as the label, with its children representing the integers from 0 up to but not including the value. The expected result is a tree in which each label has the number of children mentioned in the label, recursively.
unfolder : Int -> (String, List Int)
unfolder x =
( String.fromInt x, List.range 0 (x - 1) )
unfold unfolder 3
--> tree "3"
--> [ singleton "0"
--> , tree "1" [ singleton "0" ]
--> , tree "2"
--> [ singleton "0"
--> , tree "1" [ singleton "0" ]
--> ]
--> ]
restructure : (a -> b) -> (b -> List c -> c) -> Tree a -> c
Restructure a Tree
into another type of structure.
Imagine you have a Tree String
and you can to turn it into nested <ul>
s.
This function can help!
import Html exposing (Html)
labelToHtml : String -> Html msg
labelToHtml l =
Html.text l
toListItems : Html msg -> List (Html msg) -> Html msg
toListItems label children =
case children of
[] ->
Html.li [] [ label ]
_ ->
Html.li []
[ label
, Html.ul [] children
]
tree "root"
[ tree "folder"
[ singleton "foo"
, singleton "bar"
]
, singleton "yeah"
]
|> restructure labelToHtml toListItems
|> \root -> Html.ul [] [ root ]
--> Html.ul []
--> [ Html.li []
--> [ Html.text "root"
--> , Html.ul []
--> [ Html.li []
--> [ Html.text "folder"
--> , Html.ul []
--> [ Html.li [] [ Html.text "foo" ]
--> , Html.li [] [ Html.text "bar" ]
--> ]
--> ]
--> , Html.li [] [ Html.text "yeah" ]
--> ]
--> ]
--> ]
Or perhaps you have your own tree datastructure and you want to convert to it:
type MyTree a = MyTree a (List (MyTree a))
tree "root"
[ tree "folder"
[ singleton "foo"
, singleton "bar"
]
, singleton "yeah"
]
|> restructure identity MyTree
--> MyTree "root"
--> [ MyTree "folder"
--> [ MyTree "foo" []
--> , MyTree "bar" []
--> ]
--> , MyTree "yeah" []
--> ]