9
$\begingroup$

I want to select all elements that repeat 4 times in the list. I propose this code

list = {a, a, a, b, c, a, b, b, b, e};

elementsplus4 = Select[list, Count[list, #] == 4 &];

I obtain:

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

How can I obtain just {a,b}

$\endgroup$
2
  • 2
    $\begingroup$ DeleteDuplicates $\endgroup$
    – Ajasja
    Jan 13, 2014 at 7:08
  • 9
    $\begingroup$ Cases[Tally[list], {i_, 4}:>i] $\endgroup$
    – Rojo
    Jan 13, 2014 at 7:09

4 Answers 4

19
$\begingroup$

You may use Tally to finish the task as follows:

Cases[Tally[list], {x_, 4} :> x]

the result will be {a,b}.

$\endgroup$
2
  • $\begingroup$ Yup, that's the one. +1 $\endgroup$
    – Mr.Wizard
    Jan 13, 2014 at 7:18
  • $\begingroup$ Cases[Tally[list], {x_, 4} -> x] seems to work too. $\endgroup$ Jan 28, 2014 at 16:06
9
$\begingroup$

The code given by Rojo and sunt05 is almost surely the cleanest:

Cases[Tally @ list, {x_, 4} :> x]

However, here are some other possibilities:

Cases[Split @ Sort @ list, {x_, _, _, _} :> x]

Cases[Split @ Sort @ list, {Repeated[x_, {4}]} :> x]

Cases[Last @ Reap[Sow[1, list], _, {#, Tr@#2} &], {x_, 4} :> x]

Module[{c},
  c[_] = 0;
  Scan[c[#]++ &, list];
  Cases[DownValues[c], (_@_@x_ :> 4) :> x]
]

Performance

Interestingly, some of these may be significantly faster than Tally in certain cases:

list = FromCharacterCode /@ RandomInteger[15000, 100000];

Cases[Tally @ list, {x_, 4} :> x]                               // Timing // First

Cases[Last @ Reap[Sow[1, list], _, {#, Tr@#2} &], {x_, 4} :> x] // Timing // First

Module[{c},
 c[_] = 0;
 Scan[c[#]++ &, list];
 Cases[DownValues[c], (_@_@x_ :> 4) :> x]
] // Timing // First
0.546

0.109

0.2622

Since it seems to be only in the case of String objects that Sow/Reap is faster, for clarity one might write the second method as:

stringTally = Last @ Reap[Sow[1, #], _, {#, Tr@#2} &] &;

Cases[stringTally @ list, {x_, 4} :> x] // Timing // First
0.103

Addendum

The OP wrote: "I want to list all elements that appear at least four times." In light of that here are all the methods modified accordingly:

stringTally = Last @ Reap[Sow[1, #], _, {#, Tr@#2} &] &;

Cases[stringTally @ list, {x_, n_} /; n >= 4 :> x]

Cases[Split @ Sort @ list, {x_, _, _, __} :> x]

Cases[Split @ Sort @ list, {Repeated[x_, {4, ∞}]} :> x]

Cases[Last @ Reap[Sow[1, list], _, {#, Tr@#2} &], {x_, n_} /; n >= 4 :> x]

Module[{c},
  c[_] = 0;
  Scan[c[#]++ &, list];
  Cases[DownValues[c], (_@_@x_ :> n_) /; n >= 4 :> x]
]
$\endgroup$
8
  • $\begingroup$ How can we change Cases[Last @ Reap[Sow[1, list], _, {#, Tr@#2} &], {x_, 4} :> x] when we need to select the elements repated greater than 4 times ? $\endgroup$ Jan 13, 2014 at 7:50
  • $\begingroup$ @developer2000 just change the 4 in {x_, 4} :> x, as you would with the Tally solution. $\endgroup$
    – Mr.Wizard
    Jan 13, 2014 at 7:52
  • $\begingroup$ I think that you did not understand me. I would select the elements that repeated 4 times, 5 times, 6 times and so on (greater than 4). I think that I can use Table[Cases[stringTally @ list, {x_, i} :> x],{i,4,n}] but is there any other pretty solution? $\endgroup$ Jan 13, 2014 at 9:02
  • $\begingroup$ @developer2000 Oh, that was not clear. Do you mean that you wish to list the elements that appear four times separately from those that appear five times, etc., or list all elements that appear at least four times? $\endgroup$
    – Mr.Wizard
    Jan 13, 2014 at 9:10
  • $\begingroup$ I want to list all elements that appear at least four times. sorry for the bed english. $\endgroup$ Jan 13, 2014 at 11:37
4
$\begingroup$
DeleteDuplicates[Select[list, Count[list, #] == 4 &]]
$\endgroup$
0
$\begingroup$

Or be lazy and just run the set theory-inspired Union[] command over the result of your initial computation, provided that the initial one yields the results you listed -- it doesn't for me.

$\endgroup$
1
  • $\begingroup$ sometimes being lazy is a good choice!! I know this solution! $\endgroup$ Jan 13, 2014 at 18:20

Your Answer

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

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