I want to move branches from tree to tree in Elm.
For example:
Tree 1:
A-1
- A-1-1
- - A-1-1-1
- - A-1-1-2
- - - A-1-1-2-1
- - - A-1-1-2-2
Tree 2
B-1
- B-1-1
- - B-1-1-1
- - B-1-1-2
- - - B-1-1-2-1
- - - B-1-1-2-2
I'd like to move A-1-1 under B-1-1-2-1 which should give
B-1
- B-1-1
- - B-1-1-1
- - B-1-1-2
- - - B-1-1-2-1
- - - - A-1-1
- - - - - A-1-1-1
- - - - - A-1-1-2
- - - - - - A-1-1-2-1
- - - - - - A-1-1-2-2
- - - B-1-1-2-2
I'm new to functional programming. I can imagine how to do this with a recursive forloop in Python but I'm stuck with Elm.
I can move one node easily but I don't see how to add the children recursively:
module Main exposing (..)
import Canopy exposing (Node, append, children, leaf, mapChildren, node, value)
import Html exposing (Html, b, div, h1, h2, li, text, ul)
import List exposing (map)
tree1 : Node String
tree1 =
node "A-1"
[ node "A-1-1"
[ leaf "A-1-1-1"
, node "A-1-1-2"
[ leaf "A-1-1-2-1"
, leaf "A-1-1-2-2"
]
]
]
tree2 : Node String
tree2 =
node "B-1"
[ node "B-1-1"
[ leaf "B-1-1-1"
, node "B-1-1-2"
[ leaf "B-1-1-2-1"
, leaf "B-1-1-2-2"
]
]
]
tree3 : Node String
tree3 =
let
nodeToMove =
"A-1-1"
newParentNode =
"B-1-1-2-1"
-- append the node only but not its descendants
treeWithNewNode =
append newParentNode nodeToMove tree2
-- type mismatch
-- treeWithNewNodeAndNewNodeChildren =
-- nodeToMove |> mapChildren (\child -> append
-- does not do what I was hopping for
-- newTree =
-- mapChildrenAt
-- nodeToMove
-- (\child -> append newParentNode (value child) treeWithNewNode)
-- tree2
newParentNode child tree2)
in
treeWithNewNode
main =
div []
[ h1 [] [ text "Adding a branch to another tree" ]
, h2 [] [ text "Tree 1" ]
, viewNode tree1
, h2 [] [ text "Tree 2" ]
, viewNode tree2
, h2 [] [ text "Move A-1-1 under B-1-1-2-1" ]
, viewNode tree3
]
viewNode : Node String -> Html msg
viewNode node =
let
subNodes =
children node
in
li []
[ b [] [ text (value node) ]
, ul [] (List.map viewNode subNodes)
]
My trial is here: https://ellie-app.com/7842F8jCLpCa1
I'm using Canopy here but I could use another library if it's recommended.
In your code, it looks to me like you never actually extract A-1-1
's children from tree1
, so let's start with that:
subtreeToMove =
Maybe.withDefault (leaf <| "Failed to find node " ++ nodeToMove) <| get nodeToMove tree1
The get function finds a node in a tree by value. Since there might not be a node with the specified value, it returns a Maybe
, so we pass in a default value.
Next you find the target node in tree2
, and attach the node with its children. I'll use replaceChildrenAt here since the target node is a leaf:
treeWithNewNode =
tree2 |> replaceChildrenAt newParentNode [ subtreeToMove ]
Just mentioning this because you described the desired result as moving a node between trees: All data is immutable in Elm – so, after the move, tree1
and tree2
are still the same. So rather, a subtree from tree1
has been copied into a duplicate of tree2