Assign children to parent enumerable

Hi!

I have a list of structs and want to update the first record to include similar ids as children. Not sure if there is a good way to do it other than iterating over it. :scream:

I was trying out group_by to create a list where structs with identical keys are together but i have multiple ids so if id key is empty it should use uid instead. since it uses either uid or id as its key, there might be duplicates?

list1 = [%{"id": 1, "test": "parent"}, %{"id": 1, "test": "child1"}, %{"id": 1, "test": "child2"}, %{"uid": 1, "test": "parent", id: ""}]

list1 = [%{"id": 1, "test": "parent", "children": [%{"id": 1, "test": "child1"}, %{"id": 1, "test": "child2"}], {"uid": 1, "test": "parent", id: ""}

Thanks! Been stuck on this for a while. :sweat:

Hi, perhaps you could rephrase the question as it’s a bit confusing.

Are you saying that you need to transform data of the form:

[
  %{"id": 1, "test": "parent"},
  %{"id": 1, "test": "child1"},
  %{"id": 1, "test": "child2"},
  %{"uid": 1, "test": "parent", id: ""}
]

into data of the form:

[%{
  "id": 1,
  "test":
  "parent",
  "children": [%{
                  "id": 1,
                  "test": "child1"
               }, %{
                  "id": 1, "test": "child2"
               }],
  {"uid": 1, "test": "parent", id: ""}
 ]

If so you’ll need to be more explicit about the transformation rules, and it might help to have some context. E.g. why does one record have a uid and not id? Why is there a parent at the end but no children following it etc?

@demem123: I’m not 100% sure about your rules, but let me know if this example fits them:

defmodule Example do
  def collect_children(list), do: Enum.reduce(list, [], &do_collect_children/2)

  defp do_collect_children(element, acc) do
    cond do
      element.test == "parent" -> [element | acc]
      element.id == "" ->
        update_children(acc, element, :uid)
      true ->
        update_children(acc, element, :id)
    end
  end

  defp update_children(acc, element, key) do
    index = Enum.find_index(acc, &(&1[key] == element[key]))
    update_in(
      acc,
      [Access.at(index), :children],
      &do_update_children(&1, element)
    )
  end

  defp do_update_children(nil, element), do: [element]
  defp do_update_children(list, element), do: [element | list]
end

list = [
  %{"id": 1, "test": "parent"},
  %{"id": 1, "test": "child1"},
  %{"id": 1, "test": "child2"},
  %{"uid": 1, "test": "parent", id: ""}
]

IO.inspect Example.collect_children(list)

Note: Here parents and children are reversed. You can reverse them again using &Enum.reverse/1 or by replacing tail calls like: [element | list] to: list ++ [element].

1 Like

thanks! =) looks really clean! i will try it out.

hey! Thanks! yep trying to transform the data. I was using ecto to get the data from the table and the table has multiple foreign keys so there are times that other ids doesnt have any values in it. =)

@demem123: I suggest you to add has_many children and belongs_to parent relations for same model, so you do not need to group them.