Here you go
defmodule X do
import Pathex
import Pathex.Combinator
import Pathex.Lenses
def post_lens(post_id) do
combine(fn recursive ->
matching(%{id: ^post_id}) ||| path(:children) ~> some() ~> recursive
end)
end
def set_show_reply_form_to_true(tree, post_id) do
Pathex.set!(tree, post_lens(post_id) ~> path(:show_reply_form), true)
end
end
And try it out with this
%{
id: 1,
show_reply_form: false,
children: [
%{
id: 2,
show_reply_form: false,
children: [
%{
id: 4,
show_reply_form: false,
children: []
}
]
},
%{
id: 3,
show_reply_form: false,
children: [
%{
id: 5,
show_reply_form: false,
children: []
}
]
}
]
}
|> X.set_show_reply_form_to_true(4)
|> IO.inspect()
A few words about this cryptic thing
combine(fn recursive ->
matching(%{id: ^post_id}) ||| path(:children) ~> some() ~> recursive
end)
I know that combine is hard to get correctly from the first try, especially when combined with logical operators (like |||), but my advise here is to always think of it as of a recursive tree traversal.
Once you get into the node, it might be the node you were looking for (hence the matching(%{id: ^post_id}), but if it is not (hence the |||), you need to check it’s children. First you need to find where the children are (under children key, hense the path(:children)), then you need to traverse all children or just some (here I assume we can encounter post with id=4 only once, so we need to find the single one, hence we do ~> some()), and then you need to treat them just like you did (hence the ~> recursive)


















