16
$\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 seperated in selector by a 0 to be seperate 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$
17
$\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}}

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

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

Select[SplitBy[mylist*selector, Positive], #[[1]] > 0 &]
$\endgroup$
  • $\begingroup$ Select[SplitBy[mylist*selector, # != 0 &], #[[1]] != 0 &] would also work for negative numbers - +1 anyway $\endgroup$ – eldo Nov 28 '15 at 20:45
15
$\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$
  • 2
    $\begingroup$ I should upgrade :) $\endgroup$ – eldo Nov 28 '15 at 20:49
11
$\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$
2
$\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$
0
$\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$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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