The reason you get a stack overflow is because you are returning rootItem instead of [] in the case of Item.ChildItems [] .
I will change my code a bit because there are some common patterns that we can pull out. First of all, letβs take the basic structure of the tree and make it more general so that it can correspond to any type of thing, and not just Item :
type Node a = Node a (List (Node a))
This gives us a structure that always has a root node and can have any number of children, each of which can also have any number of children.
If we think about the algorithm you were looking for, we can extrapolate the familiar pattern. You have a structure with several elements, and you need an algorithm that visits each element and possibly changes it. This is very similar to List.map . This is such a common idiom that itβs nice to call our generalized map function:
map : (a -> b) -> Node a -> Node b map f (Node item children) = Node (f item) (List.map (map f) children)
(Side note: we just stumbled upon functors !)
Since I accepted the idea of ββchildren and put it in a Node type, we need to change the Item alias as follows:
type alias Item = { id: String , text: String }
Now that we have Item , it will be useful to have a function that can update it if id matches a specific value. Since in the future you may have more update functions that you want to perform, it is best to separate the search and identifier area from the function that you really want to perform:
updateByID : String -> (Item -> Item) -> Item -> Item updateByID id f item = if item.id == id then f item else item
Now, to update the element corresponding to the identifier anywhere in the tree, you can simply do this:
map (updateByID "someID" (\x -> { x | text = "Woohoo!" })) rootNode