21

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!

tripleee
  • 127
  • 6
holistic
  • 2,975
  • 15
  • 37

9 Answers9

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

eldo
  • 67,911
  • 5
  • 60
  • 168
20

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

Revious
  • 540
  • 2
  • 12
Pillsy
  • 18,498
  • 2
  • 46
  • 92
18

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

Select[SplitBy[mylist*selector, Positive], #[[1]] > 0 &]
KennyColnago
  • 15,209
  • 26
  • 62
  • Select[SplitBy[mylist*selector, # != 0 &], #[[1]] != 0 &] would also work for negative numbers - +1 anyway – eldo Nov 28 '15 at 20:45
13

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} *)
ybeltukov
  • 43,673
  • 5
  • 108
  • 212
4
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}}
Pankaj Sejwal
  • 2,063
  • 14
  • 23
4
 SequenceSplit[mylist selector,{0}]

 (* {{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}} *)
user1066
  • 17,923
  • 3
  • 31
  • 49
3

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

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
3

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

Syed
  • 52,495
  • 4
  • 30
  • 85
3
f = Internal`CopyListStructure[Split @ #2, #][[2 - First @ #2 ;; ;; 2]] &;


f[mylist, selector]
{{1, 5, 7}, {7, 2}, {9, 1, 10}, {34, 64}}
kglr
  • 394,356
  • 18
  • 477
  • 896