22
$\begingroup$

I have the following list I want to pick elements from:

mylist = {1, 5, 7, 3, 4, 7, 2, 9, 9, 1, 10, 12, 2, 64, 34, 64}

I have another list with 0's and 1's which is my selector:

selector = {1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1}

I want to select all the entries from mylist where selector has a 1. I know I can do this via:

Pick[mylist,selector,1]

BUT: Pick gives me all the selected elements from mylist together like this:

{1, 5, 7, 7, 2, 9, 1, 10, 34, 64}

I want the elements which are separated in selector by a 0 to be separate lists. That is, as a result I want to have a list of lists like this:

{{1,5,7},{7,2},{9,1,10},{34,64}}

Any help appreciated!

$\endgroup$

11 Answers 11

24
$\begingroup$
mylist = {1, 5, 7, 3, 4, 7, 2, 9, 9, 1, 10, 12, 2, 64, 34, 64};
selector = {1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1};

pick = Pick[mylist, selector, 1];

split = Length /@ Cases[Split[selector], {1 ...}]

{3, 2, 3, 2}

Internal`PartitionRagged[pick, split]

{{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}}

Update

Since V 11.2 we can use TakeList

TakeList[pick, split]

{{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}}

$\endgroup$
1
  • 1
    $\begingroup$ Interesting solution, never would have thought of that :). Thanks very much! $\endgroup$
    – holistic
    Nov 28, 2015 at 17:59
20
$\begingroup$

You can combine Map, Take, and SequencePosition:

Map[Take[mylist, #] &,
 SequencePosition[selector, {1 ..}, Overlaps -> False]]

(* {{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}} *) 
$\endgroup$
1
  • 3
    $\begingroup$ I should upgrade :) $\endgroup$
    – eldo
    Nov 28, 2015 at 20:49
18
$\begingroup$

Because your selector is 0 or 1, SplitBy can be used as follows.

Select[SplitBy[mylist*selector, Positive], #[[1]] > 0 &]
$\endgroup$
1
  • $\begingroup$ Select[SplitBy[mylist*selector, # != 0 &], #[[1]] != 0 &] would also work for negative numbers - +1 anyway $\endgroup$
    – eldo
    Nov 28, 2015 at 20:45
13
$\begingroup$

You can find positions where differences of selector is nonzero. Then you can take corresponding sublists. This straightforward solution is relatively fast (two times faster then eldo's solution, which is already very efficient)

list = {1, 5, 7, 3, 4, 7, 2, 9, 9, 1, 10, 12, 2, 64, 34, 64};
selector = {1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1};

pick[list_, sel_] := 
  Take[list, #] & /@ Transpose@{#[[;; ;; 2]], #[[2 ;; ;; 2]] - 1} &@
     Pick[Range@Length@#, #, 1] &@Abs@Differences@Join[{0}, sel, {0}];

pick[list, selector]
(* {{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}} *)

n = 1000000;
list = RandomInteger[100, n];
selector = RandomInteger[1, n];

pick[list, selector]; // RepeatedTiming
(* {0.706, Null} *)
$\endgroup$
5
$\begingroup$
 SequenceSplit[mylist selector,{0}]

 (* {{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}} *)
$\endgroup$
4
$\begingroup$
Map[Take[mylist, #] &, 
 Map[Flatten[#] &, 
   FindClusters[Position[selector, 1]]] //. {w___, {q_, __, x_}, 
    k___} :> {w, {q, x}, k}]

{{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}}
$\endgroup$
4
$\begingroup$

Using TakeList:

Clear["Global`*"];
mylist = {1, 5, 7, 3, 4, 7, 2, 9, 9, 1, 10, 12, 2, 64, 34, 64};
selector = {1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1};

lens = Map[Length]@Split[selector];
pos = Position[Split[selector], {1 ..}];
Extract[TakeList[mylist, lens], pos]

{{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}}

$\endgroup$
4
$\begingroup$
f = Internal`CopyListStructure[Split @ #2, #][[2 - First @ #2 ;; ;; 2]] &;


f[mylist, selector]
{{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}}
$\endgroup$
3
$\begingroup$

Lots of nice answers already but coming to this question late here are my ideas.

Application of Szabolcs's How to efficiently find positions of duplicates? with adaptations:

fn1[a_, b_] :=
  a[[#]] & /@ SplitBy[Range@Length@a, b[[#]] &][[2 - b[[1]] ;; ;; 2]]

Application of my intervals from Find continuous sequences inside a list (load it first)

fn2[a_, b_] :=
  Take[a, #] & /@ intervals @ SparseArray[b]["AdjacencyLists"]

Test:

fn1[mylist, selector]

fn2[mylist, selector]
{{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}}

{{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}}
$\endgroup$
2
$\begingroup$
list={1, 5, 7, 3, 4, 7, 2, 9, 9, 1, 10, 12, 2, 64, 34, 64};

selector = {1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1};

Another way to do this is as follows:

sel=!FreeQ[#,KeyValuePattern[{1->a_}]]&;

Values@DeleteCases[
Pick[#,sel/@#]&@Split[Thread[selector>list],#1[[1]]!=0&],
Rule[0,a_],2]

{{1,5,7},{7,2},{9,1,10},{34,64}}

$\endgroup$
2
$\begingroup$
list = {1, 5, 7, 3, 4, 7, 2, 9, 9, 1, 10, 12, 2, 64, 34, 64};

selector = {1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1};

Using TakeList (new in 11.2) and SequenceCases

TakeList[
 Pick[list, selector, 1],
 SequenceCases[selector, x : {1 ..} :> Length[x]]]

{{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}}

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