What is a good way to compare structs?

In some tests, we need to compare 2 structs between each other. However when we build

  • the test struct: this is done manually
%{
  data_actions: %{A, B}
}
  • the core struct, we go through a
data_actions |> Map.from_struct() |> Enum.reduce( ...)

Since OTP 26, Maps aren’t ordered anymore (Taking Control of Map Sort Order in Elixir · The Phoenix Files) so the result is random (in the resulting struct, the data_actions have a random order).

Is there any way to compare a struct recursively, without considering the values order? What is the elixir way to do it clean?

to clarify: you want to compare two maps, not two structs?

concretely yes, but recursively

Not sure I understand the problem? Can you give an example of which comparison is failing?

The == operator does not care about order when comparing maps. This applies for structs too.

%{key1: :equal, key2: :values} == %{key2: :values, key1: :equal}
#=> true
%{key1: :unequal, key2: :values} == %{key2: :values, key1: :equal}
#=> false

For tests, you can just assert map1 == map2 as you would expect.

7 Likes

just adding to that, if you’re talking about tests… if you pattern match you can just look for the stuff you care for in a particular test run… so that works

assert %{field: :i_care_for} = %MyStruct{field: :i_care_for, other_field: :dont_care_about}
4 Likes

Hi @Laurent,

If I understand your problem properly I would like to suggest this approach.

Let’s say we have map_a = %{a: "a", b: "b", c: "c"} and map_b: %{a: "a", b: "b", c: "no_match"}
We could use Enum.reduce_while/3 to iterate through map_a and compare key and value from map_a with the key and value of map_b.

Enum.reduce_while(map_a, true, fn {k, v}, acc ->
  # This statement checks if the key from map_a exists in map_b. if the key exists, also checks the 
  # value of the keys. If it is true, iteration is continued, otherwise it is halted and it will return false
  if v == Map.get(map_b, k) do
    {:cont, acc}
  else
    {:halt, false}
  end
end)

I hope this will help you :upside_down_face:

can you give me some example of map comparison that is failing? It should not be failing even if the order of fields looks different for some reason.

2 Likes

Again, what is wrong with a simple map_a == map_b ?

7 Likes

Absolutely nothing :+1: == operator is the best way to do it. For some reason my brain went for a more complex approach :man_shrugging:

2 Likes