Function thinks my argument is a map even though it's a list

I am trying to create a sales territory, associate it with a specific industry id then insert it to the db then associate it with several states. It seems to be breaking at the Ecto.changeset of the pipeline saying that the states are invalid. I’m not seeing why that is as I am getting the states with the enum function then making sure to preload them before making the association. Any help appreciated! Thanks.

def create_territory(sub_vertical_name, territory_name, ownerids, states) when is_list(ownerids) do
    sub_vertical = Repo.get_by(SubVertical, name: "HVAC/Plumbing")
    |> Ecto.build_assoc(:territory, %{territory_name: territory_name, ownerids: ownerids})
    |> Repo.insert!()

    associate_states_with_territories(territory_name, states)  
  end

  defp associate_states_with_territories(territory_name, states) when is_list(states) do
    Enum.each(states, fn x -> Repo.get_by(State, name: x) end)
    |> IO.inspect(label: "STATES")


    __MODULE__
    |> Repo.get_by(territory_name: territory_name)
    |> Repo.preload([:states])
    |> Changeset.change()
    |> IO.inspect(label: "STATES SECOND")
    |> Ecto.Changeset.put_assoc(:states, states)
  end

Here is the output:

iex(36)> Territory.create_territory("HVAC/Plumbing", "dummy_test", ["0051P000003md9D"], ["Alaska"])
[debug] QUERY OK source="sub_verticals" db=6.8ms idle=579.4ms
SELECT s0."id", s0."name", s0."inserted_at", s0."updated_at", s0."vertical_id" FROM "sub_verticals" AS s0 WHERE (s0."name" = $1) ["HVAC/Plumbing"]
[debug] QUERY OK db=3.5ms idle=586.7ms
INSERT INTO "territories" ("sub_vertical_id","territory_name","inserted_at","updated_at") VALUES ($1,$2,$3,$4) RETURNING "id" [28, "dummy_test", ~N[2021-11-02 18:02:23], ~N[2021-11-02 18:02:23]]
[debug] QUERY OK source="states" db=8.5ms idle=591.1ms
SELECT s0."id", s0."name", s0."inserted_at", s0."updated_at", s0."country_id" FROM "states" AS s0 WHERE (s0."name" = $1) ["Alaska"]
STATES: :ok
[debug] QUERY OK source="territories" db=2.0ms idle=600.3ms
SELECT t0."id", t0."territory_name", t0."ownerid", t0."inserted_at", t0."updated_at", t0."sub_vertical_id" FROM "territories" AS t0 WHERE (t0."territory_name" = $1) ["dummy_test"]
[debug] QUERY OK source="states" db=8.0ms idle=602.8ms
SELECT s0."id", s0."name", s0."inserted_at", s0."updated_at", s0."country_id", t1."id" FROM "states" AS s0 INNER JOIN "territories" AS t1 ON t1."id" = ANY($1) INNER JOIN "territories_states" AS t2 ON t2."territory_id" = t1."id" WHERE (t2."state_id" = s0."id") ORDER BY t1."id" [[92]]
STATES SECOND: #Ecto.Changeset<action: nil, changes: %{}, errors: [],
 data: #Gimli.Prospector.Assignment.Territory<>, valid?: true>
#Ecto.Changeset<
  action: nil,
  changes: %{},
  errors: [states: {"is invalid", [type: {:array, :map}]}],
  data: #Gimli.Prospector.Assignment.Territory<>,
  valid?: false
>

Keep in mind (almost) everything is immutable in erlang/elixir, you probably want to do

states = Enum.map(states, 
fn x -> Repo.get_by(State, name: x) end) 
|> Enum.concat()

Enum.each is for side effects.

2 Likes

Thanks! It wouldn’t work with |> Enum.concat but removing that I got it to work! Thanks!

I thought Repo.get_by/2 would return a list. My bad.
Glad you got it to work.

1 Like