       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,

Yes, there is a more direct way! I have added some computations.

t[v_,b_] := Cases[Transpose[{v,b}],{a_,True}:>a]

In:=  (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=  (2/25/97 at 12:38:42)
{0.316667 Second, Null}
Out=  (2/25/97 at 12:38:43)
{0.133333 Second, Null}
Out=  (2/25/97 at 12:38:43)
True
In:=  (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=  (2/25/97 at 12:39:07)
{3.53333 Second, Null}
Out=  (2/25/97 at 12:39:09)
{1.2 Second, Null}
Out=  (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:=  (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=  (2/25/97 at 13:36:29)
{0.683333 Second, Null}
Out=  (2/25/97 at 13:36:30)
{0.283333 Second, Null}
Out=  (2/25/97 at 13:36:30)
True
In:=  (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=  (2/25/97 at 13:36:54)
{6.6 Second, Null}
Out=  (2/25/97 at 13:36:57)
{2.95 Second, Null}
Out=  (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

• Prev by Date: global names in contexts
• Next by Date: Re: Solving non-linear equations
• Previous by thread: Re: selecting from lists
• Next by thread: Re: selecting from lists