A list of maps

Hi all

I created a schema that looks as follow:

schema "countries" do

    field :code, :string
    field :en, :string
    field :de, :string
    field :it, :string
    field :fr, :string

    timestamps

  end

Now a I want to create changeset function that would accept a list of map. How can I define it? Is this right(I would say it is wrong):

def changeset_all(model, []) do

Then how can I validate if the passed parameter contains maps or not?

The second question is, how can I loop through the list of maps, that after I would get a changeset of all pass values? I would do like this:

def changeset_all(model, [params]) do

  for param <- [params] do
      model
      |> cast(param, [:code, :en, :de, :it, :fr])
      |> validate_required([:code, :en])
      |> validate_length(:code, max: 3)
      |> unique_constraint(:code)
    end
  end

I hope my questions are clear.

Thanks

The only way to ‘enforce’ this in Elixir, is to create a recursive function:

  • it has a clause def changeset_all(model, []) do, which finishes the execution as it was passed an empty list.
  • it has a clause def changeset_all(model, [mymap = %{} | tail]) do, which enforces that the current element mymap is a map. It does whatever you want with mymap, and then calls itself recursively, as changeset_all(model, tail).
  • It has a clause that raises an error (in the way that is most appropriate for this part of your application), def changeset_all(model, _not_a_list_of_maps) do which is called when you call the function with anything else.

There is no way to enforce that someone can only put a list of maps in your function at compile-time. You can add type annotations with Dyalizer to show how the functions should be used, which does let you say ‘I want a list of maps’, but Dialyzer is something that is opt-in and as such can easily be ignored by anyone who uses your function.

Yes, it is important to be aware that while at compile time dialyzer can check your code and find many errors, at run time any function can be called with any arguments so to be safe you need to check your arguments. Or, if your system is correctly designed, you can just assume everything is correct and “let it crash” if it is not.

2 Likes

What is the best way to check the arguments? With for example is_list() function?

Yes, I would say that if you want to check for basic types you should use the is_X functions as guard clauses in your own functions.

If you need more complex guarantees, such as checking if a value is of a given struct type, or that a map has a certain key, you should go with pattern matching.

I think it very much depends on what you want to do if it is not a list. One way is to program is as if it is a list then crash if it isn’t. Or maybe you can do something sensible if it is not a list. A thing to remember with is_list is that it just checks the front of the list whether it is a list cell, an empty list [] or something else, it does not step down the whole list. For example:

iex(1)> is_list([1,2,3|:a])
true
1 Like