# Find sublists in a list by contents, then union them

I want to find two sublists in a list by an element they contain, and then union them, keeping them in the list.

I have this code that seems to do the job, but it seems rather clunky. Is there a better way to do it?

It finds the sublists from the example list 'groups' containing b and g, then unions them. You have to do the check to verify that the elements don't belong to the same sub-list already, or else you end up deleting the sub-list they're both contained in.

The master list will always be two levels deep, and wouldn't have any duplicates.

groups = {{a}, {b, c}, {d}, {e, f, g}}
firstPart = b;
secondPart = g;
fPAddress = Part[Position[groups, firstPart, 2], 1, 1];
sPAddress = Part[Position[groups, secondPart, 2], 1, 1];
Union[
]
];
]


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.

• Yes, they work as intended, thank you! – Eriek Jan 27 '17 at 16:54
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}}