# Single association: group keys together by equivalence relation and total their values

What is a relatively concise set of operations that will allow me to take a single association with a set of old keys and old values and map it to a new association with new keys that are effectively equivalence classes of old keys and values that are sums of the corresponding old values? Answers tailored to my specific problem below are appreciated but not necessary.

My specific problem is that I have a single association with keys that are lists of (e.g.) 4 pairs of symbols. That is, a typical key might be {{a1, a1}, {a1, a1}, {a2, a2}, {a2, a3}}. A given association will have keys with a fixed number of pairs and a fixed number of indices.

I want to group together keys via the equivalence relation of being equal up to permutation of the indices and to sum the values of keys within a given equivalence class. That is, I would like to return a new association with keys given by equivalence classes (or a given element of the equivalence class) and the total values found from summing over the old keys in the equivalence.

I would expect something like this, a simplified example with keys characterized by two pairs of numbers and two indices:

[in] foo( <| {{a1,a1}, {a2,a2}}->1, {{a1,a1}, {a1, a2}}->10, {{a1,a2}, {a2, a2}}->3 |>)

[out] <| {{a1,a1}, {a2,a2}}->1, {{a1,a1}, {a1, a2}}->13 |>


I'm a relative novice to Mathematica. Promising-sounding functions like KeyMap seem to delete all but one of duplicate keys and don't offer totaling capabilities like summing over the values of duplicate keys. Further, I'm unsure if mapping functions over the keys is the best way to go; I grow confused on trying to implement grouping by an equivalence relation. My current thought is to pre-generate all possible equivalence classes, extract the values of keys corresponding to a given equivalence class and sum them, make a new key-value pair of equivalence class and total, and generate a new association from those key-value pairs. I'm curious about alternative methods of approach and whether there is some go-to procedure for such problems.

• I do not understand you example; it just does not fit to what you write in the text. – Henrik Schumacher Jan 25 at 14:13

Assuming that you meant the third key of your input to be {{a1, a2}, {a1, a1}}, this might do want you ask for.

a = <|{{a1, a1}, {a2, a2}} -> 1, {{a1, a1}, {a1, a2}} -> 10, {{a1, a2}, {a1, a1}} -> 3|>;
f = Sort;


You may replace f by any mapping that maps each key to a canonical representative of its equivalence class.

• Thank you for Merge[Thread[Rule[f /@ Keys[a], Values[a]]], Total]. That was exactly the general purpose line I was looking for. (I actually did mean my input and output to be as they were but made the confusing mistake of not making the numbers 1,2,3 subscript indices. I meant equivalence under exchange of those subscript indices. ) – user196574 Jan 26 at 0:54

You can use the ResourceFunction "GroupByList" to do this:

ResourceFunction["GroupByList"][Values[a], Sort /@ Keys[a], Total]


<|{{a1, a1}, {a2, a2}} -> 1, {{a1, a1}, {a1, a2}} -> 13|>

ClearAll[mergE]
mergE[f_, red_] := Merge[red]@*KeyValueMap[f[#] -> #2 &];


Examples:

a = <|{{a1, a1}, {a2, a2}} -> 1, {{a1, a1}, {a1, a2}} -> 10, {{a1, a2}, {a1, a1}} -> 3|>;

mergE[Sort, Total]@a


<|{{a1, a1}, {a2, a2}} -> 1, {{a1, a1}, {a1, a2}} -> 13|>

mergE[Sort, foo]@a


<|{{a1, a1}, {a2, a2}} -> foo[{1}], {{a1, a1}, {a1, a2}} -> foo[{10, 3}]|>