Find sublists in a list by contents, then union them

Using rules:

groups //. {a___, x : {___, b | g, ___}, b___, y : {___, b | g, ___}, c___} :>
{a, Union[x, y], b, c}

This behaves differently compared to your code when there are more than two sublists which contain either b or g, in that it will take the union of all of them whereas your code doesn't do that. I hope this is what you intended, or perhaps it's never the case that there are more than two sublists involved?

Or, inspired by march's solution, we might also write

{hasNot, has} = GatherBy[groups, MemberQ[b | g]];
Append[hasNot, Union @@ has]

which can also be written

Append[#, Union @@ #2] & @@ GatherBy[groups, MemberQ[b | g]]

MemberQ can be replaced by ContainsAny. If performance is important, one might try this.


Flatten /@ Gather[groups, MemberQ[#1, b | g] && MemberQ[#2, b | g] &]

ClearAll[conditionalUnion]
conditionalUnion[test_] := Reap[
   If[test @ #, 
      Sow[#, Apply @ Union], 
      Sow[#, Apply @ Sequence]] & /@ #,
    _, Construct][[2]] &;

Examples:

conditionalUnion[MatchQ[{___, b | g, ___}]] @ groups

{{a}, {d}, {b, c, e, f, g}}

conditionalUnion[ContainsAny[{b, g}]] @ groups

{{a}, {d}, {b, c, e, f, g}}

Take the union of sublists with lengths less than 3:

conditionalUnion[Length[#] <= 2&] @ groups

{{a, b, c, d}, {e, f, g}}