# Take list elements until a specific number occurs $n$ times

I want to take enough elements from list such that 2 is included 3 times.

SeedRandom[1]
list = RandomChoice[{.2, .5, .3} -> {1, 2, 3}, 20]


{3,1,3,1,2,1,2,2,2,3,2,3,2,2,3,3,3,2,2,2}

I hope to get {3,1,3,1,2,1,2,2}. I think I make no mistake in my code:

TakeWhile[list, Count[Append[{}, #], 2] <= 3 &]


But I just get {}.

• I don't have Mathematica 10.1, so I can't test this, but does the following work? First@SequenceCases[list, {Shortest[___], 2, 2}]. Mar 1, 2017 at 5:22
• @march Actually not,but First[SequenceCases[ list, {Except[2] ..., 2, Except[2] ..., 2, Except[2] ..., 2}]] work for my case,Thanks. :)
– yode
Mar 1, 2017 at 5:30
• It might be worth noting that your attempt and the accepted answer will take everything before the fourth 2. In your example that's identical but for {1,2,1,2,1,2,1,2} you'd get {1,2,1,2,1,2,1}. Is that your intention? Mar 1, 2017 at 9:01
• @MartinEnder Oh,god.You give me a big remind that I have made a mistake.Actually,I want to get {1,2,1,2,1,2} in your case.
– yode
Mar 1, 2017 at 9:07
• Also lst[[;;Position[lst,2][[3,1]]]] Apr 26, 2023 at 5:35

Perhaps:

i = 1; While[Count[list[[1 ;; i]], 2] < 3, i++]
list[[1 ;; i]]


or

list[[1 ;; Catenate[Position[list, 2]][[3]]]]

• Look little consice,i=0;While[Count[list[[1;;++i]],2]<3];list[[;;i]].And do you know why my TakeWhile don't work?
– yode
Mar 1, 2017 at 5:04
• TakeWhile tests each element not the cumulative list. You could do something ugly like: list[[1 ;; Length@TakeWhile[ Count[list[[1 ;; #]], 2] & /@ Range[Length[list]], # < 3 &] + 1]] though I am sure others would have better ways Mar 1, 2017 at 5:14
• Yep,I know that.So I give a Append to collect it.
– yode
Mar 1, 2017 at 5:16
• @yode but your append is in your test Mar 1, 2017 at 5:17

I think you meant to do this:

Module[{tmp = {}}, TakeWhile[list, Count[AppendTo[tmp, #], 2] <= 3 &]]

• Thanks,I have adjust that ==.But since AppendTo can work,why Append not?Confusing still.
– yode
Mar 1, 2017 at 5:25
• Because you are always appending to {} whereas I am appending to tmp, which is hence a growing list. In my case, tmp contains all the elements processed so far. In your case, it's just the same as {#}, i.e., just the current element wrapped in {} Mar 1, 2017 at 5:27
• Oh,I see now.Thanks very much.
– yode
Mar 1, 2017 at 5:31
• @Felix nicely instructive Mar 1, 2017 at 5:50

Using PositionIndex:

Take[list, Check[PositionIndex[list][2][[3]], 1 ;; -1]]


Check will substitute specs to get the entire list 1;;-1, should the PositionIndex command fail.

{3, 1, 3, 1, 2, 1, 2, 2}

• (+1) Nice use for Check! Apr 25, 2023 at 14:42

As of 10.1 we can also do this:

list[[;; First@First@SequencePosition[list, {2, 2, 2}]]]

Or to generalize:

takeBefore[list_,obj_,reps_]:=
list[[;; First@First@SequencePosition[list, ConstantArray[obj,reps]]]]


Of course that assumes the sublist is in there. If it's not the First calls will whine at you:

In[11]:= SeedRandom[3]
list = RandomChoice[{.2, .5, .3} -> {1, 2, 3}, 20]

Out[12]= {2, 1, 2, 1, 1, 2, 2, 3, 2, 3, 3, 2, 1, 3, 3, 1, 2, 1, 2, 2}

In[13]:= list[[;; First@First@SequencePosition[list, {2, 2, 2}]]]

Block[{i = 1}, NestWhileList[list[[i++]] &, Nothing, (Count[{##}, 2] < 3) &, All ]]


{3, 1, 3, 1, 2, 1, 2, 2}

We can define an operator f[v, k, m] that splits an input list every time the number of vs reaches k, and use the optional third argument m to get desired part(s):

ClearAll[f]
f[v_, k_, m_ : 1] := Module[{s = 0},
Split[#, Or[(s += Boole[# == v]) < k, s = 0] &][[m]]] &


Examples:

f[2, 3] @ list /. 2 -> Highlighted[2]


f[2, 3, All] @ list /. 2 -> Highlighted[2]


f[2, 6] @ list /. 2 -> Highlighted[2]


Another way using Partition:

Last[
Map[Function @ If[SameQ[Count[#, 2], 3], #, Nothing],
Apply[Join,
Map[Function @ Partition[list, {#}], Range @ Length @list]
]
]
]

(*{3,1,3,1,2,1,2,2}*)


The following form is courtesy of @Syed (Thanks, mate!):

list /.
{k : Except[2] ...,h : OrderlessPatternSequence[___, 2, 2, 2, ___], ___} :> {k, h}

(*{3,1,3,1,2,1,2,2}*)

• You are looking for the first consecutive occurrence of 2. The task is to grab enough of the list such that three 2s are included.
– Syed
Apr 25, 2023 at 15:02
• You're right, I will think hard to improve my answer. Apr 25, 2023 at 15:06
• list /. {k : Except[2] ..., h : OrderlessPatternSequence[___, 2, 2, 2, ___], ___} :> {k, h}
– Syed
Apr 25, 2023 at 15:22
list = {3, 1, 3, 1, 2, 1, 2, 2, 2, 3, 2, 3, 2, 2, 3, 3, 3, 2, 2, 2};

First @ SequenceCases[list, _?(Count[#, 2] == 3 &)]


{3, 1, 3, 1, 2, 1, 2, 2}

yet another Sow and Reap

index = 1;

x = 0;

arr = {3, 1, 3, 1, 2, 1, 2, 2, 2, 3, 2, 3, 2, 2, 3, 3, 3, 2, 2, 2};

While[
x <= 3
,
Sow[arr[[index]]];
index++;
If[arr[[index]] == 2,
x++
];
] //
Reap //
Last //
First