# Select list elements based on other list

I am trying to find a nice and clean method to select elements from one list which are in another list or the criteria is based on another list. The ones I want to returned are in listA and the ones that contain the matching criteria are in listB. listA has different dimensions than listB and I only apply the criterion on certain subset say, listA[[All,1]]. Obviously, not all elements in listA[[All,1]] have to be present in listB and not all elements of listB are present in listA[[All,1]] So far, I have come up with the two almost identical methods:

listA = {{"a", 1, 3}, {"b", 3, 4}, {"e", 4, 4}, {"r", 2, 2}}
listB = {"a", "c", "d", "e"}
Function[u, Select[listA, #[[1]] == u &]] /@
Intersection[listA[[All, 1]], listB]
Function[u, Select[listA, #[[1]] == u &]] /@ listB


Obviously the second one returns also empty sublists which need to be deleted. Both suffer from producing an additional dimension {{{"a", 1, 3}}, {{"e", 4, 4}}} which, of cource, can be corrected with flatten. But, I am interested if there is another, a more elegant way, that would keep the structure of listA, without having to figure out at which level to flatten (it is simple in the example above)? (to summarize, I already have a working solution, i am just interesting in other or better ways to do it which I am missing, thank you)

• Somewhat related: 4626. I particularly liked this rules based approach. A similar method using ToAssoication would be worth trying. Commented May 7, 2019 at 2:20

 Select[listA,MemberQ[listB,#[[1]]]&]


{{"a", 1, 3}, {"e", 4, 4}}

You can also use Cases:

Cases[{Alternatives @@ listB, __}] @ listA


{{"a", 1, 3}, {"e", 4, 4}}

You can use an association:

take = Lookup[AssociationThread[#[[All, 1]] -> #], #2, Nothing] &;
take[listA, listB]
(* {{"a", 1, 3}, {"e", 4, 4}} *)


Here's a very tolerant version that works without flattening listA. It compares the leaves of the elements of listA (extracted with Level[#, {-1}]) with the elements of listB. This makes it tolerant to reordering of elements in listA as well as arbitrary nesting.

listA = {{"a", 1, 3}, {"b", 3, 4}, {"e", 4, 4}, {"r", 2, 2}};
listB = {"a", "c", "d", "e"};
Select[listA, IntersectingQ[Level[#, {-1}], listB] &]


{{"a", 1, 3}, {"e", 4, 4}}

One more level of nesting:

listA = {{{"a", 1, 3}}, {{"b", 3, 4}}, {{"e", 4, 4}}, {{"r", 2, 2}}};
Select[listA, IntersectingQ[Level[#, {-1}], listB] &]


{{{"a", 1, 3}}, {{"e", 4, 4}}}

Crazy nesting:

listA = {{{{{{{"a", 1, 3}}}}}}, {{"b", 3, 4}}, {{{2}, {{"e"}, 3}, 4, 4}}, {{{{{{{{"r", 2, 2}}}}}}}}};
Select[listA, IntersectingQ[Level[#, {-1}], listB] &]


{{{{{{{"a", 1, 3}}}}}}, {{{2}, {{"e"}, 3}, 4, 4}}}

Complement[listA, Extract[listA , #] & /@ Position[Intersection[#, listB ] & /@ listA , {}] ]


{{"a", 1, 3}, {"e", 4, 4}}

Lookup also accepts Lists of Rules:

look = MapApply[# -> {##} &] @ listA


{"a" -> {"a", 1, 3}, "b" -> {"b", 3, 4}, "e" -> {"e", 4, 4}, "r" -> {"r", 2, 2}}

Lookup[look, listB, Nothing]


{{"a", 1, 3}, {"e", 4, 4}}

An alternative method using DeleteCases:

DeleteCases[listA, x_ /; FreeQ[listB, x[[1]]]]

(*{{"a", 1, 3}, {"e", 4, 4}}*)


Or using Cases:

Cases[listA, x_ /; ! FreeQ[listB, x[[1]]]]

(*{{"a", 1, 3}, {"e", 4, 4}}*)

Clear["Global*"];
listA = {{"a", 1, 3}, {"b", 3, 4}, {"e", 4, 4}, {"r", 2, 2}};
listB = {"a", "c", "d", "e"};
arule = First@# -> # & /@ listA;

KeyTake[arule, listB] // Values


or

Pick[listA, KeyMemberQ[arule, #] & /@ listB]


{{"a", 1, 3}, {"r", 2, 2}}

Extract[listA,Position[listA[[All,1]],Alternatives@@listB]]

(* {{a,1,3},{e,4,4}} *)


Pick[listA,listA[[All,1]]/.Thread[listB->True]]

(* {{a,1,3},{e,4,4}} *)

la = {{"a", 1, 3}, {"b", 3, 4}, {"e", 4, 4}, {"r", 2, 2}};

lb = {"a", "c", "d", "e"};


Using Pick with ContainsAny

Pick[la, ContainsAny[lb] /@ la]


{{"a", 1, 3}, {"e", 4, 4}}

la = {{"a", 1, 3}, {"b", 3, 4}, {"e", 4, 4}, {"r", 2, 2}};

lb = {"a", "c", "d", "e"};


Another way using Replace at level 1:

Replace[la,l_List/;FreeQ[l,Alternatives@@lb]:>Nothing,1]


{{a,1,3},{e,4,4}}`