Search code examples
elixircomparison

Calculating parent value based on children values


Apologies if the title doesn't make sense, I'm struggling to describe what it is precisely but hopefully, the following will enlighten you.

This is part of an AoC challenge but I believe it's a specific enough question that one might run into it anyway. For the challenge, I don't think I'm going at it the right way, building a tree would be better but now I just want to know how to solve this issue.


Where I'm at

My data looks like the following:

iex(249)> Day7.part1
[
  %{path: ["/", "a", "e"], size: 584},
  %{path: ["/", "a"], size: 94269},
  %{path: ["/", "d"], size: 24933642},
  %{path: ["/"], size: 23352670}
]

What I want to do is iterate through where I compare the :path values, if they match then the shorter list must be the parent and the parent :size will be updated such that size: parent.size + child.size. Or something like that at least.


Desired output

iex(249)> Day7.part1
[
  %{path: ["/", "a", "e"], size: 584},
  %{path: ["/", "a"], size: 94853},
  %{path: ["/", "d"], size: 24933642},
  %{path: ["/"], size: 48381165}
]

What I've attempted so far

  def child_dirs([], system), do: system
  def child_dirs([head | tail] = dirs, system) do
      Enum.map(dirs, fn x ->
        if x.path == List.delete_at(head.path, -1) do
          system = system ++ [%{path: x.path, size: head.size + x.size}]
        end
      end)
      child_dirs(tail, system)
  end

Which results in the following

iex(281)> Day7.part1 |> Day7.child_dirs([])
[]

But if I change system = system ++ ... to inspect it, like so IO.inspect(system = system ++ [%{path: x.path, size: head.size + x.size}]). I get the following:

iex(284)> Day7.part1 |> Day7.child_dirs([])
[%{path: ["/", "a"], size: 94853}]
[%{path: ["/"], size: 23446939}]
[%{path: ["/"], size: 48286312}]
[]

So it's clearly working in some regard but just not adding the updated values to the ongoing accumulator named system.

Any help or advice would be much appreciated :)


Solution

  • That’s most likely not the optimal way, but it’s the most clean and idiomatic one, AFACT.

    input
    [
      %{path: ["/", "a", "e"], size: 584},
      %{path: ["/", "a"], size: 94269},
      %{path: ["/", "d"], size: 24933642},
      %{path: ["/"], size: 23352670}
    ]
    

    Walk through the list, select from this list elements which start with the current one, and sum them all.

    for %{path: path} <- input do
      size =
        input
        |> Enum.filter(&Enum.slice(&1.path, 0..length(path)-1) == path)
        |> Enum.map(& &1.size)
        |> Enum.sum()
      %{path: path, size: size}
    end
    #⇒ [
    #   %{path: ["/", "a", "e"], size: 584},
    #   %{path: ["/", "a"], size: 94853},
    #   %{path: ["/", "d"], size: 24933642},
    #   %{path: ["/"], size: 48381165}
    # ]