Re: selecting from lists

*To*: mathgroup at smc.vnet.net*Subject*: [mg6184] Re: [mg6120] selecting from lists*From*: fransm at win.tue.nl (Frans Martens)*Date*: Thu, 27 Feb 1997 02:52:53 -0500*Sender*: owner-wri-mathgroup at wolfram.com

Murray wrote: The problem: given an arbitrary list v and a list b of the same length, whose entries are all Boolean (True or False), construct a function s[v, b] whose result is a list consisting only of those entries of v for which the corresponding entries in b are True. For example, v = {3, 82, 7, -12, 5}; b = {False, True, True, False, False}; s[v, b] {82, 7} Of course, a looping solution is obvious. But I want something that works on the entire list at once (as in the APL or J programming languages, where this sort of thing is utterly trivial). The best I've come up with so far is the following: bpick[x_, b_] := If[b, {x}, {}] SetAttributes[bpick, Listable] s[v_, b_] := Flatten[bpick[v, b]] Is there some nicer, more direct way? No Table's, Do's, or For's, please! Yes, there is a more direct way! I have added some computations. t[v_,b_] := Cases[Transpose[{v,b}],{a_,True}:>a] In[100]:= (2/25/97 at 12:38:42) n = 1000; v = Table[Random[Integer,{1,100}],{n}]; b = Table[If[Positive[Random[ Integer,{0,1}]],True,False],{n}]; Timing[s1 = s[v,b];] Timing[t1 = t[v,b];] s1 === t1 Out[103]= (2/25/97 at 12:38:42) {0.316667 Second, Null} Out[104]= (2/25/97 at 12:38:43) {0.133333 Second, Null} Out[105]= (2/25/97 at 12:38:43) True In[106]:= (2/25/97 at 12:39:07) n = 10000; v = Table[Random[Integer,{1,100}],{n}]; b = Table[If[Positive[Random[ Integer,{0,1}]],True,False],{n}]; Timing[s1 = s[v,b];] Timing[t1 = t[v,b];] s1 === t1 Out[109]= (2/25/97 at 12:39:07) {3.53333 Second, Null} Out[110]= (2/25/97 at 12:39:09) {1.2 Second, Null} Out[111]= (2/25/97 at 12:39:10) True Murray wrote furher with some modifications: Actually, the general problem as I formulated it above is just the tip of the iceberg of things that are giving me trouble in Mathematica that are so easy for me in APL or J. For example, construct a function firstnonzero that returns the index of the first nonzero entry in a list (or, more generally, the first entry in a list satisfying a given property). Here the best I've come up with that avoids explicit looping is: nonzero[x_]:= x =!= 0; SetAttributes[nonzero, Listable] firstnonzero[v_] := First[s[Range[Length[v]], nonzero[v]]] A solution is (in case the lists v have atleast one nonzero element) secondfirstnonzero[v_]:=Part[Position[v,a_ /; a =!= 0],2,1] Some computations: In[205]:= (2/25/97 at 13:36:29) n = 1000; v = Table[Random[Integer,{0,2}],{n}]; Timing[fnz1 = firstnonzero[v];] Timing[fnz2 = secondfirstnonzero[v];] fnz1 === fnz2 Out[207]= (2/25/97 at 13:36:29) {0.683333 Second, Null} Out[208]= (2/25/97 at 13:36:30) {0.283333 Second, Null} Out[209]= (2/25/97 at 13:36:30) True In[210]:= (2/25/97 at 13:36:54) n = 10000; v = Table[Random[Integer,{0,2}],{n}]; Timing[fnz1 = firstnonzero[v];] Timing[fnz2 = secondfirstnonzero[v];] fnz1 === fnz2 Out[212]= (2/25/97 at 13:36:54) {6.6 Second, Null} Out[213]= (2/25/97 at 13:36:57) {2.95 Second, Null} Out[214]= (2/25/97 at 13:36:58) True I don't like thesekind of solutions for this kind of problems. I prefer a while loop in these circumstances. Frans Martens Eindhoven The Netherlands