ExEssentials.Core.Fingerprint (ExEssentials v0.11.0)

View Source

Utilities for generating deterministic hashes (fingerprints) and comparing maps or structs based on selected keys.

ExEssentials.Core.Fingerprint extracts specific keys from a map or struct, formats their values, and hashes them using SHA-256 to generate a consistent hex-encoded fingerprint representation.

This is useful for:

  • Detecting payload changes across requests
  • Comparing subset attributes of structs or maps for equality
  • Generating cache keys or unique identifiers based on specific fields

Examples

Computing a fingerprint hash

user = %{id: 1, name: "Alice", email: "alice@example.com"}
ExEssentials.Core.Fingerprint.hash(user, [:id, :email])

Comparing two structures by specific keys

user_a = %{id: 1, name: "Alice", role: :admin}
user_b = %{id: 1, name: "Alice", role: :user}

ExEssentials.Core.Fingerprint.equal?(user_a, user_b, [:id, :name])
#=> true

ExEssentials.Core.Fingerprint.equal?(user_a, user_b, [:id, :role])
#=> false

Summary

Functions

Compares two maps or structs for equality based on the specified keys.

Generates a SHA-256 fingerprint hash for a map or struct based on the given keys.

Functions

equal?(left, right, keys)

@spec equal?(left :: map() | struct(), right :: map() | struct(), keys :: [atom()]) ::
  boolean()

Compares two maps or structs for equality based on the specified keys.

Calculates the SHA-256 fingerprint hash for both left and right using keys, and returns true if they are identical, or false otherwise.

Examples

iex> user_a = %{id: 42, name: "Joe"}
iex> user_b = %{id: 42, name: "Joe", age: 30}
iex> ExEssentials.Core.Fingerprint.equal?(user_a, user_b, [:id, :name])
true
iex> ExEssentials.Core.Fingerprint.equal?(user_a, user_b, [:id, :age])
false

hash(data, keys)

@spec hash(data :: map() | struct(), keys :: [atom()]) :: String.t()

Generates a SHA-256 fingerprint hash for a map or struct based on the given keys.

Values associated with keys are extracted in order, converted to strings, concatenated with "&", and hashed into a lowercase hex string.

Examples

iex> ExEssentials.Core.Fingerprint.hash(%{a: "hello", b: "world"}, [:a, :b])
"60c4a2ca447b1da45e2bf854d117faabda9a47a5656b8cfadc558cb61621eb78"