# Joining all the keys of a list of rules that point to matching values

Suppose I have a list of rules whose keys are not of identical structure (in my case, the keys are either lists of at least one symbol or symbols alone):

rules = {{a} -> 1, {b, c} -> 1, d -> 2, {e, f} -> 2, {g, g} -> 3, g -> 4};


The aim is to get all keys with matching values grouped together to end up with

newRules = {{a, b, c} -> 1, {d, e, f} -> 2, {g, g} -> 3, {g} -> 4};


I have what seems to me to be a hacky attempt to do this:

Reverse /@
Normal[
Merge[
Association /@ Reverse /@ rules, Flatten]
] == newRules
(* True *)


Are there any better ways to do this?

• You could use Flatten[#[[;; , 1]]] -> #[[1, 2]] & /@ GatherBy[rules, Last]. Jan 4, 2018 at 21:34
• equally hacky, but different: GroupBy[List @@@ rules, Last -> Most, Flatten] // GeneralUtilitiesAssociationInvert Jan 4, 2018 at 21:37
• Reverse[Normal[GroupBy[rules, Last -> First, Flatten]], 2] Jan 4, 2018 at 21:56

There are many ways, here's one:

KeyValueMap[#2 -> # &] @ GroupBy[rules, Last -> First, Flatten]

Reverse /@ (Flatten /@ PositionIndex[Association@rules] // Normal)


{{a, b, c} -> 1, {d, e, f} -> 2, {g, g} -> 3, {g} -> 4}

Flatten /@ PositionIndex[Association@rules]


<|1 -> {a, b, c}, 2 -> {d, e, f}, 3 -> {g, g}, 4 -> {g}|>

Reap[Sow @@@ (List @@@ rules), _, Flatten[{#2}] -> #1 &][[-1]]


yields:

{{a, b, c} -> 1, {d, e, f} -> 2, {g, g} -> 3, {g} -> 4}

KeyValueMap[#2 -> #&] @ Merge[Flatten][Reverse /@ rules]


{{a, b, c} -> 1, {d, e, f} -> 2, {g, g} -> 3, {g} -> 4}

Als0

Flatten[#[[All, 1]]] -> #[[1, -1]]& /@ GatherBy[rules, Last]


{{a, b, c} -> 1, {d, e, f} -> 2, {g, g} -> 3, {g} -> 4}

and

Values @ GroupBy[rules, Last, Flatten @ #[[All, 1]] -> #[[1, -1]]&]
`

{{a, b, c} -> 1, {d, e, f} -> 2, {g, g} -> 3, {g} -> 4}