4
$\begingroup$

Suppose I have a nested list such as,

{{{A, B}, {A, D}}, {{C, D}, {A, A}, {H, A}}, {{A, H}}}

Where the elements of interest are,

{{A, B}, {A, D}}

{{C, D}, {A, A}, {H, A}}

{{A, H}}

How would I use select to pick up only elements that contain two or more As in the first part of their sub-elements. In this example I would want the following as an output,

{{A,B},{A,D}}
$\endgroup$
2
  • 2
    $\begingroup$ This does it: Cases[{{{A, B}, {A, D}}, {{C, D}, {A, A}, {H, A}}, {{A, H}}}, {___, {A, _}, ___, {A, _}, ___}] $\endgroup$
    – Coolwater
    Commented Jul 28, 2014 at 19:29
  • $\begingroup$ Yep this is exactly what I am looking for. Thank you. $\endgroup$
    – Ibrahim
    Commented Jul 28, 2014 at 19:35

5 Answers 5

3
$\begingroup$
list = {{{a, b}, {a, d}}, {{c, d}, {a, a}, {h, a}}, {{a, h}}}

Pick[list, Count[#[[All, 1]], a] >= 2 & /@ list]

or

Select[list, Count[#[[All, 1]], a] >= 2 &]

or

Cases[list, _?(Count[#[[All, 1]], a] >= 2 &)]

or

DeleteCases[list, _?(! Count[#[[All, 1]], a] >= 2 &)]

all give

{{{a, b}, {a, d}}}
$\endgroup$
5
  • $\begingroup$ This is probably the best answer, since it uses the select command explicitly. Thanks! $\endgroup$
    – Ibrahim
    Commented Jul 28, 2014 at 20:23
  • $\begingroup$ @kguler executing your Select with, f.e., list = {{{a, b}, {c, a}}, {{c, d}, {a, a}, {h, a}}, {a, h}, {{a, b}, {a, c}}, {{a, b}, {q, r}, {a, d}}} I get an error message and a result which is, the question being utterly ambigous, debatable. $\endgroup$
    – eldo
    Commented Jul 28, 2014 at 20:37
  • $\begingroup$ @eldo, good observation re implicit assumption that is the source of the error message: although OP's example has that structure, it is not explicitly stated that all sublists have depth >=2. However -- removing {a,h} or changing it to {{a,h}} -- I don't understand why the result is "debatable". $\endgroup$
    – kglr
    Commented Jul 28, 2014 at 20:55
  • $\begingroup$ @kguler - This question has a short history of accepted and de-accepted answers and comments which were subsequently deleted. "Debatable" because the OP is unsure about what he really wants. Hopefully, your excellent answer stays awarded. (But a Case-solution would be more appropriate imo) :) $\endgroup$
    – eldo
    Commented Jul 28, 2014 at 21:13
  • $\begingroup$ @eldo, I see. I missed the deleted comments. And I now see why the wording "elements that contain two or more As in the first part of their sub-elements" could be read differently from my reading of it -- of course, with the help of desired output -- as " two or more elements whose first part is equal to A" $\endgroup$
    – kglr
    Commented Jul 28, 2014 at 21:23
2
$\begingroup$

I chose a slightly different formulation:

expr = {{{A, B}, {A, D}}, {{C, D}, {A, A}, {H, A}}, {{A, H}}};

Select[expr, Count[#, {A, _}] > 1 &]
{{{A, B}, {A, D}}}

I will note that this form is faster all four in the Accepted answer:

list = RandomChoice[CharacterRange["A", "H"], {500000, 3, 2}];

Select[list, Count[#, {"A", _}] > 1 &]                  // Timing // First

Pick[list, Count[#[[All, 1]], "A"] >= 2 & /@ list]      // Timing // First
Select[list, Count[#[[All, 1]], "A"] >= 2 &]            // Timing // First
Cases[list, _?(Count[#[[All, 1]], "A"] >= 2 &)]         // Timing // First
DeleteCases[list, _?(! Count[#[[All, 1]], "A"] >= 2 &)] // Timing // First

0.592804

0.795605
0.811205
0.936006
1.060807

$\endgroup$
2
$\begingroup$
list = {{{A, B}, {A, D}}, {{C, D}, {A, A}, {H, A}}, {{A, H}}};

Using Position and Extract:

Extract[#, Position[#, m_?MatrixQ /; Count[m[[All, 1]], A] > 1]] &@list

(*{{{A, B}, {A, D}}}*)

Or using Replace:

Replace[list, m_?MatrixQ /; Count[m[[All, 1]], A] <= 1 :> Nothing, {1}]

(*{{{A, B}, {A, D}}}*)
$\endgroup$
2
$\begingroup$
list = {{{a, b}, {a, d}}, {{c, d}, {a, a}, {h, a}}, {{a, h}}};

p = Position[list, x_ /; Count[First /@ x, a] <= 1, 1, Heads -> False]

{{2}, {3}}

Delete[p] @ list

{{{a, b}, {a, d}}}

MapAt[Nothing, p] @ list

{{{a, b}, {a, d}}}

$\endgroup$
1
$\begingroup$
list = {{{A, B}, {A, D}}, {{C, D}, {A, A}, {H, A}}, {{A, H}}};

If[Count[First /@ #, A] >= 2, #, ## &[]] & /@ list

{{{A, B}, {A, D}}}

$\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.