11
$\begingroup$

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)

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

11 Answers 11

15
$\begingroup$
 Select[listA,MemberQ[listB,#[[1]]]&]  

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

$\endgroup$
8
$\begingroup$

You can also use Cases:

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

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

$\endgroup$
6
$\begingroup$

You can use an association:

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

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}}}

$\endgroup$
4
$\begingroup$
Complement[listA, Extract[listA , #] & /@ Position[Intersection[#, listB ] & /@ listA , {}] ]

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

$\endgroup$
4
$\begingroup$

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}}

$\endgroup$
4
$\begingroup$

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}}*)
$\endgroup$
4
$\begingroup$
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}}

$\endgroup$
4
$\begingroup$
Extract[listA,Position[listA[[All,1]],Alternatives@@listB]]

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

In addition:

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

(* {{a,1,3},{e,4,4}} *)
$\endgroup$
2
$\begingroup$
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}}

$\endgroup$
2
$\begingroup$
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}}

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.