[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Find the first non-nil element in an array

lianliming

12/11/2007 9:19:00 AM

Hi all,

Is there any build-in method of class Array which can be used to find
the first non-nil element in an array? I can't find any so I am trying
to achieve it by my own approach. I don't think my approach is smart
enough, so I would like to know better one to do so. Here is what I am
doing:

a = [nil, "hello", "world"]
b = a
b.compact!
first_non_nil_elem = b.first

I don't call "compact!" directly on a because it will change a's
structure, I want to reserve nil elements in a.

Any suggestions are really appreciated!
9 Answers

Stefano Crocco

12/11/2007 9:28:00 AM

0

Alle marted=EC 11 dicembre 2007, lianliming ha scritto:
> Hi all,
>
> Is there any build-in method of class Array which can be used to find
> the first non-nil element in an array? I can't find any so I am trying
> to achieve it by my own approach. I don't think my approach is smart
> enough, so I would like to know better one to do so. Here is what I am
> doing:
>
> a =3D [nil, "hello", "world"]
> b =3D a
> b.compact!
> first_non_nil_elem =3D b.first
>
> I don't call "compact!" directly on a because it will change a's
> structure, I want to reserve nil elements in a.
>
> Any suggestions are really appreciated!

You can use Enumerable#find, which will return the first item in the array =
for=20
which the block returns true:

a =3D [nil, "hello", "world"]
puts a.find{|i| !i.nil?}
=3D> "hello"

By the way, you can use compact, instead of compact! to avoid using the=20
temporary variable b:

a =3D [nil, "hello", "world"]
first_non_nil_elem =3D a.compact.first

While compact! removes the nil elements from the receiver, compact returns =
a=20
new array with the nil element removed.

I hope this helps

Stefano



lianliming

12/11/2007 9:31:00 AM

0

Really helpful, thx!

> > Hi all,
>
> > Is there any build-in method of class Array which can be used to find
> > the first non-nil element in an array? I can't find any so I am trying
> > to achieve it by my own approach. I don't think my approach is smart
> > enough, so I would like to know better one to do so. Here is what I am
> > doing:
>
> > a = [nil, "hello", "world"]
> > b = a
> > b.compact!
> > first_non_nil_elem = b.first
>
> > I don't call "compact!" directly on a because it will change a's
> > structure, I want to reserve nil elements in a.
>
> > Any suggestions are really appreciated!
>
> You can use Enumerable#find, which will return the first item in the array for
> which the block returns true:
>
> a = [nil, "hello", "world"]
> puts a.find{|i| !i.nil?}
> => "hello"
>
> By the way, you can use compact, instead of compact! to avoid using the
> temporary variable b:
>
> a = [nil, "hello", "world"]
> first_non_nil_elem = a.compact.first
>
> While compact! removes the nil elements from the receiver, compact returns a
> new array with the nil element removed.
>
> I hope this helps
>
> Stefano

Phrogz

12/11/2007 2:43:00 PM

0

On Dec 11, 2:28 am, Stefano Crocco <stefano.cro...@alice.it> wrote:
> Alle martedì 11 dicembre 2007, lianliming ha scritto:
>
>
>
> > Hi all,
>
> > Is there any build-in method of class Array which can be used to find
> > the first non-nil element in an array? I can't find any so I am trying
> > to achieve it by my own approach. I don't think my approach is smart
> > enough, so I would like to know better one to do so. Here is what I am
> > doing:
>
> > a = [nil, "hello", "world"]
> > b = a
> > b.compact!
> > first_non_nil_elem = b.first
>
> > I don't call "compact!" directly on a because it will change a's
> > structure, I want to reserve nil elements in a.
>
> > Any suggestions are really appreciated!
>
> You can use Enumerable#find, which will return the first item in the array for
> which the block returns true:
>
> a = [nil, "hello", "world"]
> puts a.find{|i| !i.nil?}
> => "hello"
>
> By the way, you can use compact, instead of compact! to avoid using the
> temporary variable b:
>
> a = [nil, "hello", "world"]
> first_non_nil_elem = a.compact.first
>
> While compact! removes the nil elements from the receiver, compact returns a
> new array with the nil element removed.

Moreover, there was no reason to assign to the 'b' variable in the
first place. Doing so doesn't duplicate the array, but just creates
another reference to it. The compact! method still modified the same
array:

irb(main):006:0> a = [ nil, "foo", "bar" ]
=> [nil, "foo", "bar"]

irb(main):007:0> b = a
=> [nil, "foo", "bar"]

irb(main):008:0> b.compact!
=> ["foo", "bar"]

irb(main):009:0> a
=> ["foo", "bar"]

So if the intent was, as I assume, to preserve the original array with
the nil values, that's even more reason to use #compact instead of
#compact!

clouder

12/11/2007 8:09:00 PM

0

I had some time wrapping my head around this one messing with irb

I was wondering why b.compact! would change the value of a

As i continually tried doing
a =3D [1,2,3]
b =3D a
b =3D [1,3,4]
and was getting a =3D> [1,2,3] and b =3D> [1,3,4]

After trying with strings and gsub and such it finally dinged that b=3Da
has assigned 'b' to the object stored in 'a', so running a method on
it was effecting the object stored in both 'a' and 'b'. Where as when
I was doing b =3D [1,3,4] it was creating a new object all together so
'a' was never effected. Is this correct? If so, sorry for making a
dumb post if everyone else git this :X I thought I'd post it anyways
to possibly help some poor soul like me struggling over why this might
not work as expected.

On Dec 11, 7:45 am, Phrogz <phr...@mac.com> wrote:
> On Dec 11, 2:28 am, Stefano Crocco <stefano.cro...@alice.it> wrote:
>
>
>
> > Alle marted=EC 11 dicembre 2007, lianliming ha scritto:
>
> > > Hi all,
>
> > > Is there any build-in method of class Array which can be used to find
> > > the first non-nil element in an array? I can't find any so I am trying=

> > > to achieve it by my own approach. I don't think my approach is smart
> > > enough, so I would like to know better one to do so. Here is what I am=

> > > doing:
>
> > > a =3D [nil, "hello", "world"]
> > > b =3D a
> > > b.compact!
> > > first_non_nil_elem =3D b.first
>
> > > I don't call "compact!" directly on a because it will change a's
> > > structure, I want to reserve nil elements in a.
>
> > > Any suggestions are really appreciated!
>
> > You can use Enumerable#find, which will return the first item in the arr=
ay for
> > which the block returns true:
>
> > a =3D [nil, "hello", "world"]
> > puts a.find{|i| !i.nil?}
> > =3D> "hello"
>
> > By the way, you can use compact, instead of compact! to avoid using the
> > temporary variable b:
>
> > a =3D [nil, "hello", "world"]
> > first_non_nil_elem =3D a.compact.first
>
> > While compact! removes the nil elements from the receiver, compact retur=
ns a
> > new array with the nil element removed.
>
> Moreover, there was no reason to assign to the 'b' variable in the
> first place. Doing so doesn't duplicate the array, but just creates
> another reference to it. The compact! method still modified the same
> array:
>
> irb(main):006:0> a =3D [ nil, "foo", "bar" ]
> =3D> [nil, "foo", "bar"]
>
> irb(main):007:0> b =3D a
> =3D> [nil, "foo", "bar"]
>
> irb(main):008:0> b.compact!
> =3D> ["foo", "bar"]
>
> irb(main):009:0> a
> =3D> ["foo", "bar"]
>
> So if the intent was, as I assume, to preserve the original array with
> the nil values, that's even more reason to use #compact instead of
> #compact!

Clifford Heath

12/11/2007 10:07:00 PM

0

clouder wrote:
> I had some time wrapping my head around this one messing with irb
>
> I was wondering why b.compact! would change the value of a

I can't work out why you want to build a new (compacted) array in
order to discard it and use the first element. Detect is much more
suitable:

[nil,nil,3, nil, 9, nil].detect{|e| e}
=> 3

Beware of false; you might want to say !e.nil?

Clifford Heath.

Phrogz

12/11/2007 10:19:00 PM

0

On Dec 11, 1:09 pm, clouder <clouder...@gmail.com> wrote:
> I had some time wrapping my head around this one messing with irb
>
> I was wondering why b.compact! would change the value of a
>
> As i continually tried doing
> a = [1,2,3]
> b = a
> b = [1,3,4]
> and was getting a => [1,2,3] and b => [1,3,4]
>
> After trying with strings and gsub and such it finally dinged that b=a
> has assigned 'b' to the object stored in 'a', so running a method on
> it was effecting the object stored in both 'a' and 'b'. Where as when
> I was doing b = [1,3,4] it was creating a new object all together so
> 'a' was never effected. Is this correct?

You've got it! Assigning to a variable is like moving a sticky note
with the name of that variable onto an object floating in space.
Reading the 'value' of a variable is like finding the sticky note with
that variable on it and instead grabbing the object underneath.

So, when you write "a = [1,2,3]", you're creating a new Array object
floating in space, and then slapping a sticky note on it.

When you then write "b = a", you're finding that Array floating in
space, and slapping another sticky note next to the one that says "a".

If you then say "b.compact!", you are telling the Array that 'b'
refers to to compact itself, which, of course, is the same Array as
'a' is refering to.

If instead you say "b = [1,3,4]" then you're taking the 'b' sticky
note off of the first Array, and sticking it on the new array that you
just created.

> If so, sorry for making a
> dumb post if everyone else git this :X I thought I'd post it anyways
> to possibly help some poor soul like me struggling over why this might
> not work as expected.

Don't apologize - references confuse a lot of new users, who often
think of a variable as a 'shoebox' into which objects are stuffed,
instead of lightweight references. There have been a few in-depth
threads about this in the past, but it still bears repeating to help
all readers who might not have read those discussions.

Brian Adkins

12/12/2007 2:12:00 PM

0

On Dec 11, 5:06 pm, Clifford Heath <n...@spam.please.net> wrote:
> I can't work out why you want to build a new (compacted) array in
> order to discard it and use the first element. Detect is much more
> suitable:

Depending on the nature of the array (in particular, the number of
initial nils), speed might be a reason to use compact instead of
detect. Although it seems counterintuitive, creating a new compacted
array just to grab the first element and throw it away can be faster
than using detect.

With the compact approach, there is one call to a method that is
compiled C code. With the detect approach, there are (possibly)
multiple iterations of interpreted Ruby.

I'd say compact is perfectly suitable for this purpose - it's more
concise, usually faster and logical.

require 'benchmark'
include Benchmark

ITER = 10000

def bench size, num_nils
xs = Array.new(size, 'a')
xs.fill(nil, 0, num_nils)

bm(5) do |bench|
bench.report('compact') do
ITER.times { xs.compact[0] }
end
bench.report('detect') do
ITER.times { xs.detect {|e| !e.nil? } }
end
end
end

[10,100,1000].each do |size|
[0,1,9].each do |nils|
bench(size, nils)
end
end

--
Brian Adkins
http://www...
http://lojic...

Phrogz

12/12/2007 2:37:00 PM

0

On Dec 12, 7:11 am, Brian Adkins <lojicdot...@gmail.com> wrote:
> On Dec 11, 5:06 pm, Clifford Heath <n...@spam.please.net> wrote:
>
> > I can't work out why you want to build a new (compacted) array in
> > order to discard it and use the first element. Detect is much more
> > suitable:
>
> Depending on the nature of the array (in particular, the number of
> initial nils), speed might be a reason to use compact instead of
> detect. Although it seems counterintuitive, creating a new compacted
> array just to grab the first element and throw it away can be faster
> than using detect.
>
> With the compact approach, there is one call to a method that is
> compiled C code. With the detect approach, there are (possibly)
> multiple iterations of interpreted Ruby.
>
> I'd say compact is perfectly suitable for this purpose - it's more
> concise, usually faster and logical.
>
> require 'benchmark'
> include Benchmark
>
> ITER = 10000
>
> def bench size, num_nils
> xs = Array.new(size, 'a')
> xs.fill(nil, 0, num_nils)
>
> bm(5) do |bench|
> bench.report('compact') do
> ITER.times { xs.compact[0] }
> end
> bench.report('detect') do
> ITER.times { xs.detect {|e| !e.nil? } }
> end
> end
> end
>
> [10,100,1000].each do |size|
> [0,1,9].each do |nils|
> bench(size, nils)
> end
> end

For the curious, here are the results of Brian's test on my computer
(with a small change to simplify the results and make things run long
enough to be noticeable):

Rehearsal --------------------------------------------------
compact 10 0 0.320000 0.000000 0.320000 ( 0.349166)
detect 10 0 0.610000 0.010000 0.620000 ( 0.604446)
compact 10 1 0.340000 0.000000 0.340000 ( 0.342348)
detect 10 1 0.820000 0.000000 0.820000 ( 0.827957)
compact 10 9 0.340000 0.000000 0.340000 ( 0.346089)
detect 10 9 2.540000 0.020000 2.560000 ( 2.554578)
compact 100 0 0.410000 0.000000 0.410000 ( 0.413763)
detect 100 0 0.590000 0.010000 0.600000 ( 0.605422)
compact 100 1 0.440000 0.000000 0.440000 ( 0.436081)
detect 100 1 0.820000 0.000000 0.820000 ( 0.828730)
compact 100 9 0.450000 0.010000 0.460000 ( 0.449880)
detect 100 9 2.540000 0.010000 2.550000 ( 2.559824)
compact 1000 0 2.100000 0.020000 2.120000 ( 2.118428)
detect 1000 0 0.600000 0.000000 0.600000 ( 0.603317)
compact 1000 1 1.900000 0.010000 1.910000 ( 1.966759)
detect 1000 1 0.850000 0.010000 0.860000 ( 1.014724)
compact 1000 9 2.070000 0.020000 2.090000 ( 2.431632)
detect 1000 9 2.600000 0.020000 2.620000 ( 3.047283)
---------------------------------------- total: 20.480000sec

user system total real
compact 10 0 0.320000 0.000000 0.320000 ( 0.323211)
detect 10 0 0.590000 0.000000 0.590000 ( 0.594916)
compact 10 1 0.350000 0.000000 0.350000 ( 0.343759)
detect 10 1 0.820000 0.010000 0.830000 ( 0.822784)
compact 10 9 0.340000 0.000000 0.340000 ( 0.344985)
detect 10 9 2.540000 0.010000 2.550000 ( 2.552693)
compact 100 0 0.400000 0.010000 0.410000 ( 0.409807)
detect 100 0 0.590000 0.000000 0.590000 ( 0.596081)
compact 100 1 0.430000 0.000000 0.430000 ( 0.437479)
detect 100 1 0.820000 0.010000 0.830000 ( 0.826087)
compact 100 9 0.450000 0.000000 0.450000 ( 0.451402)
detect 100 9 2.530000 0.020000 2.550000 ( 2.550478)
compact 1000 0 2.110000 0.010000 2.120000 ( 2.142801)
detect 1000 0 0.600000 0.010000 0.610000 ( 0.597523)
compact 1000 1 1.890000 0.010000 1.900000 ( 1.903376)
detect 1000 1 0.820000 0.000000 0.820000 ( 0.831492)
compact 1000 9 1.950000 0.010000 1.960000 ( 1.980168)
detect 1000 9 2.540000 0.000000 2.540000 ( 2.537653)


Here's the actual test code I ran:
require 'benchmark'
ITER = 500_000

def bench size, num_nils, bm
xs = Array.new(size, 'a')
xs.fill(nil, 0, num_nils)
bm.report("compact #{size} #{num_nils}") do
ITER.times { xs.compact[0] }
end
bm.report("detect #{size} #{num_nils}") do
ITER.times { xs.detect {|e| !e.nil? } }
end
end

Benchmark.bmbm do |bm|
[10,100,1000].each do |size|
[0,1,9].each do |nils|
bench(size, nils, bm)
end
end
end

Lee Jarvis

12/13/2007 7:38:00 AM

0

clouder wrote:
>Is this correct?

Yes :-)

Regards,
Lee


--
Posted via http://www.ruby-....