Ecto validate_format on array field

Hi all,

is there any helper to perform Ecto.Changeset.validate_format/4 for every element in an Array field in Ecto, or should I write my own?

Thank you

No, but indeed you can just write your own function and pass it to validate_change.

In the end I made something like:

def valid_format_array?(changeset, field, regex) do
  case
    Ecto.Changeset.get_field(changeset, field)
    |> Enum.all?(fn value -> String.match?(value, regex) end)
  do
    true -> changeset
    false -> Ecto.Changeset.add_error(changeset, field, "has invalid format")
  end
end

Thank you

1 Like

If you wanted to capture the each individual invalid value in the array, you could swap Enum.all? for Enum.reduce instead and use the changeset as an accumulator.

def validate_array_field(changeset, field, regex) do
  Enum.reduce(Ecto.Changeset.get_field(changeset, field), changeset, fn value -> 
    case String.match?(value, regex) do
      true -> changeset
      false -> Ecto.Changeset.add_error(changeset, field, "has invalid format: #{value}")
     end
  end)
end    
1 Like

Make sense! thank you

Also, just realized that I didn’t specify the accumulator above so it should have something like this instead.

def validate_array_field(changeset, field, regex) do
  Enum.reduce(Ecto.Changeset.get_field(changeset, field), changeset, fn value, acc -> 
    case String.match?(value, regex) do
      true -> acc
      false -> acc  = Ecto.Changeset.add_error(acc, field, "has invalid format: #{value}")
     end
  end)
end

And if you wanted to use Ecto.Changeset.validate_change that @dimitarvp had mentioned, the accumulator would need to be changed from a changeset to a list.

changeset = Ecto.Changeset.validate_change(changeset, :field, fn :field, field -> 
  Enum.reduce(Ecto.Changeset.get_field(changeset, field), [], fn value, acc -> 
    case String.match?(value, regex) do
      true -> acc
      false -> [{field, "has invalid format: #{value}"}, acc]
     end
  end)
end)

yeah I did that in my version of your previously proposed code :wink:

1 Like