beatscode

beatscode

Recursively accumulate nested maps

Having an issue similar to https://forum.elixirforum.com/t/whats-the-best-way-to-access-and-retrieve-data-from-deeply-nested-maps-and-lists/613/4.

I have a data structure with a nested map I’d like to accumulate into a list. The problem is I don’t know how to recursively get the maps of nested elements.
Here is a contrived version. I’m trying to get a list of all the c and nested c values. Is there a way to do this?

test "Recursive Maps" do
    #I just want all the c's in a list
    rMap = %{:a => 1, :b => 2, :c => %{ :a => 111, :b => 2222, 
        :c => %{ :a => 212, :b => 323, 
            :c => %{:a => 45, :b => 524, 
                :c => %{}}}}}

    rMap2 = %{:a => 3, :b => 4, 
        :c => %{ :a => 5, :b => 6, 
            :c => %{ :a => 7, :b => 8, 
                :c => %{:a => 9, :b => 10, 
                    :c => %{a: 11, b: 12, c: 13}}}}}
                    
    rMaps = [rMap,rMap2]
    IO.puts "rMaps List of Justcs"
    IO.inspect rMaps
    justCs = justc rMaps
    IO.inspect justCs
    
    justCs = justc3 rMaps
    IO.inspect justCs

end
# Loop through list
def justc3(rMaps) do
    Enum.reduce_while(rMaps, [], fn rMap, acc ->
        if Map.has_key?(rMap,:c) == false do
            {:halt, acc}
        else
            c = Map.get(rMap,:c)
            {:cont, [c] ++ acc} 
        end
    end)
end

def justc(rMaps) do
    justCs = for rMap <- rMaps do
        c = Map.get(rMap,:c)
      #  IO.inspect Enum.count(c)
        if Enum.count(c) == 0 do
            []
        else
            Map.get(rMap,:c) 
        end
    end
end

First Post!

amnu3387

amnu3387

Why not just divide it into two functions that match on having a :c atom (if you want to match on binary keys too, then adding another function that matches on it) and recur, returning the accumulator if no map with :c is passed?

defmodule Test do
  def get_c(acc, %{:c => c}), do: Test.get_c([c | acc], c)
  def get_c(acc, _), do: acc
end

iex(4)> defmodule Test do
...(4)> def get_c(acc, %{:c => c}), do: Test.get_c([c | acc], c)
...(4)> def get_c(acc, _), do: acc
...(4)> end

{:module, Test,
 <<70, 79, 82, 49, 0, 0, 3, 248, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 85,
   0, 0, 0, 9, 11, 69, 108, 105, 120, 105, 114, 46, 84, 101, 115, 116, 8, 95,
   95, 105, 110, 102, 111, 95, 95, 9, 102, ...>>, {:get_c, 2}}

iex(5)> Test.get_c([], %{a: 'test', c: %{a: false, b: true, c: %{c: 1, b: 2, d: 3}}})

[1, %{b: 2, c: 1, d: 3}, %{a: false, b: true, c: %{b: 2, c: 1, d: 3}}]

In case you want to customise at runtime the key that you dig for you could use a case,

def get_arbitrary(acc, %{} = map, key) do
  case map[key] do
    nil -> acc
    value -> get_arbitrary([value | acc], value, key)
  end
end
def get_arbitrary(acc, _, _), do: acc

Last Post!

amnu3387

amnu3387

I just had a little brainfart before, of course that won’t get you 'c’s inside other maps…

defmodule Test do
  def get_cees(map, acc, key) do
    Enum.reduce(map, acc, fn 
      ({k, %{} = v}, acc) when k == key -> Test.get_cees(v, [ v | acc], key)
      ({k, v}, acc) when k == key -> [v | acc]
      ({k, %{} = v}, acc) -> Test.get_cees(v, acc, key)
      (_ , acc) -> acc
      end)
   end
 end

This will though, you match on the arguments that get passed to the reduce function. When you reduce a map you get a {k,v} tuple, so when you get a tuple where v is a map, and the key matches the key you’re searching for, you recur the function, passing v (which is a map), and appending it to the accumulator. When you get a tuple where the value isn’t a map, but the key of the reduction is the key you’re looking for, you just append the value to the accumulator. When you get a tuple where v is a map, but the key isn’t the one you want, you just recur to dig another level, but without appending to the accumulator. Otherwise you just return the accumulator as is, because it means the v isn’t a map (so no need to recur) and the key isn’t the key you want, so no need to append.

Basically [ h | t ] means, give me a list where you append h to another list t (even if empty) and can be used as well to get the head of a list (1 elem), and the tail (n elems) of it.

Where Next?

Popular in Questions Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40165 209
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New

We're in Beta

About us Mission Statement