Can't figure out how to nest Enum.map inside of other Enum.map

Updated question:
After reviewing my original problem I realized that I have made things harder on myself and now I have a different issue.

It might be best to explain what I want the code to do in the end.

It starts with an input that is a list of maps like so :

parsed_data = [...%{
    __meta__: #Ecto.Schema.Metadata<:loaded, "territories_to_scrape">,
    count: 1000000000,
    id: 23,
    inserted_at: ~N[2021-12-20 16:01:48],
    postal_code: ["92887", "92886", "92883", "92882", "92881", "92880", "92879",
     "92870", "92869", "92868", "92867", "92866", "92865", "92861", "92860",
     "92845", "92844", "92843", "92841", "92840", "92835", "92833", ...],
    state: "California",
    territory_id: 25,
    territory_name: "California25",
    updated_at: ~N[2021-12-20 16:01:48]
  },
  %{
    __meta__: #Ecto.Schema.Metadata<:loaded, "territories_to_scrape">,
    count: 1000000000,
    id: 24,
    inserted_at: ~N[2021-12-20 16:01:48],
    postal_code: ["97206", "97205", "97204", "97203", "97202", "97201", "97149",
     "97148", "97147", "97146", "97145", "97144", "97141", "97140", "97138",
     "97137", "97136", "97135", "97134", "97133", "97132", ...],
    state: "Texas",
    territory_id: 9,
    territory_name: "Texas9",
    updated_at: ~N[2021-12-20 16:01:48]
  }, ... ]

I want to iterate over each postal code so that it gives me a new list of maps where each postal code is mapped to each other element in the map:
example of desired output for this step I’m only interested in count, postal code and state):

[...%{count: 1000000000, postal_code: "92887", state: "California"},
    %{count: 1000000000, postal_code: "92886", state: "California"},
    %{count: 1000000000, postal_code: "92883", state: "California"}, ... ]

I then want to pass that to a function and have it insert the strikezone for each map

example desired output (notice nothing changes but the strikezone:

[...%{count: 1000000000, postal_code: "92887", state: "California", strikezone: "OEM Auto Dealer"},
    %{count: 1000000000, postal_code: "92887", state: "California", strikezone: "HVAC"},
    %{count: 1000000000, postal_code: "92887", state: "California", strikezone: "Jewelry"}...]

Then I want to use this list of maps to populate the arguments of the Helpers.pull_by_postal_code function which takes a count, a postal_code, a state and a strikezone.

So far this is what I have been able to figure out:
parsed_data is the original list of maps:

def enum_each_list(parsed_data) do
    parsed_data
    |> Enum.map(fn row ->
      pull_by_specific_postal_codes(row)
    end)
  end


# #   @doc """
# #   Specify list of postal codes and desired strikezones and count and will pull by postal code for each
# #   """
  def pull_by_specific_postal_codes(row) do
    strikezones = [
      "OEM Auto Dealer",
      "HVAC",
      "Jewelry",
      "Furniture",
      "Roofing",
      "Blinds",
      "Restoration",
      "Used Car Dealers"]
    state =  row.state
    count = row.count
    postal_code = row.postal_code
    |> Enum.map(fn x -> %{state: state, postal_code: x, count: count} end)

This almost gets me to the point where I can start to try to add in the strikezones but this code as it stands results in a list of lists of maps which I can’t figure out how to flatten in a way that still works:

current output:

[%{count: 1000000000, postal_code: "92887", state: "California"},
    %{count: 1000000000, postal_code: "92887", state: "California"},
    %{count: 1000000000, postal_code: "92887", state: "California"}
],
[%{count: 1000000000, postal_code: "29627", state: "Oregon"},
    %{count: 1000000000, postal_code: "97045", state: "Oregon"},
    %{count: 1000000000, postal_code: "97080", state: "Oregon"}
]
  ...]

I am constantly running into this issue with elixir where I inadvertently nest lists inside of lists and I just can’t figure out out to map the data that I need without completely screwing myself up. Any direction appreciated.

Can you post the error you’re getting, please?

1 Like

see updated original post

In your example you pass a map with postal_codes and states but in your first post you are doing data.post_code and data.state. It is a bit confusing when the examples do not match up.

Maybe that is the error?
Can you show us what Helpers.pull_with_postal_code/4 looks like too?

1 Like

@tomkonidas, I realized after you pointed out my initial mistake that that approach wouldn’t work anyway. I need to map the same count and state to each zip code in a list. I have updated the original post to explain my current issue if you have a minute to take a look at it. Thanks!

Seems you’re looking for Enum.flat_map.

…or the power of for comprehensions.

parsed_data = [
  %{
    count: 1000000000,
    postal_code: ["92887", "92886"],
    state: "California",
  },
  %{
    count: 1000000000,
    postal_code: ["97206", "97205"],
    state: "Texas",
  }
]

for item <- parsed_data, code <- item.postal_code do
  %{count: item.count, postal_code: code, state: item.state}
end

[
  %{count: 1000000000, postal_code: "92887", state: "California"},
  %{count: 1000000000, postal_code: "92886", state: "California"},
  %{count: 1000000000, postal_code: "97206", state: "Texas"},
  %{count: 1000000000, postal_code: "97205", state: "Texas"}
]
3 Likes

Worked perfectly! Thank you so much! I also got it to work to do the same thing with inputting the strikezone for each map. Most of the “more advanced” code I’ve seen seemed to use Enum.reduce for this kind of thing so I kind of got tunnel vision thinking that was the only solution (I was expecting the answer given to incorporate that actually) and for loops were discouraged as “not very elixir-like” but this works great!. In production are comprehensions acceptable as idomatic to elixir? Thanks again all for your help.

Comprehensions are definitely idiomatic and common. Here’s an example from Ecto, using pattern matching to keep only those tuples tagged with :pos, and remove the tag:

vars = for {:pos, var, count} <- vars, do: {var, count}

and another one with a guard-style filter:

bound_vars = for {_, var, _} <- vars, var != :_, do: var

Most of the “more advanced” code I’ve seen seemed to use Enum.reduce for this kind of thing so I kind of got tunnel vision thinking that was the only solution (I was expecting the answer given to incorporate that actually)

IIRC, for comprehensions are syntactic sugar for Enum.flat_map, which is built upon Enum.reduce :wink:

1 Like

@APB9785 Thanks so much for this added insight. And that’s interesting to know about the syntactic sugar. It looks like there is an Enum.flat_map_reduce(enumerable, acc, fun) as well. I clearly just need to get to know all the tools in the toolbox better. I appreciate your help!