Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > Find the first non-nil element in an array

Reply
Thread Tools

Find the first non-nil element in an array

 
 
lianliming
Guest
Posts: n/a
 
      12-11-2007
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!
 
Reply With Quote
 
 
 
 
Stefano Crocco
Guest
Posts: n/a
 
      12-11-2007
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



 
Reply With Quote
 
 
 
 
lianliming
Guest
Posts: n/a
 
      12-11-2007
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


 
Reply With Quote
 
Phrogz
Guest
Posts: n/a
 
      12-11-2007
On Dec 11, 2:28 am, Stefano Crocco <(E-Mail Removed)> 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 arrayfor
> 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 returnsa
> 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!
 
Reply With Quote
 
clouder
Guest
Posts: n/a
 
      12-11-2007
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 <(E-Mail Removed)> wrote:
> On Dec 11, 2:28 am, Stefano Crocco <(E-Mail Removed)> 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!


 
Reply With Quote
 
Clifford Heath
Guest
Posts: n/a
 
      12-11-2007
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.
 
Reply With Quote
 
Phrogz
Guest
Posts: n/a
 
      12-11-2007
On Dec 11, 1:09 pm, clouder <(E-Mail Removed)> 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.
 
Reply With Quote
 
Brian Adkins
Guest
Posts: n/a
 
      12-12-2007
On Dec 11, 5:06 pm, Clifford Heath <(E-Mail Removed)> 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.lojic.com
http://lojic.com/blog/
 
Reply With Quote
 
Phrogz
Guest
Posts: n/a
 
      12-12-2007
On Dec 12, 7:11 am, Brian Adkins <(E-Mail Removed)> wrote:
> On Dec 11, 5:06 pm, Clifford Heath <(E-Mail Removed)> 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.34234
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.55457
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.11842
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.55047
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.98016
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

 
Reply With Quote
 
Lee Jarvis
Guest
Posts: n/a
 
      12-13-2007
clouder wrote:
>Is this correct?


Yes

Regards,
Lee


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

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
FAQ 4.45 How do I find the first array element for which a condition is true? PerlFAQ Server Perl Misc 0 03-11-2011 05:00 AM
find if an array has any element present in another array Charanya Nagarajan Ruby 6 04-30-2009 11:14 AM
Once again pointer to first element vs pointer to array cast topointer to element Szabolcs Borsanyi C Programming 6 05-23-2008 11:06 AM
how to Update/insert an xml element's text----> (<element>text</element>) HANM XML 2 01-29-2008 03:31 PM
If given 1 element in an array of textboxes... find which element number it is \A_Michigan_User\ Javascript 4 11-16-2007 12:58 PM



Advertisments