Find the first non-nil element in an array

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!

Alle martedì 11 dicembre 2007, lianliming ha scritto:

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

On Dec 11, 2:28 am, Stefano C. [email protected] wrote:

doing:

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!

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? 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.

Really helpful, thx!

On Dec 11, 5:06 pm, Clifford H. [email protected] 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

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 H…

clouder wrote:

Is this correct?

Yes :slight_smile:

Regards,
Lee

On Dec 11, 1:09 pm, clouder [email protected] wrote:

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.

On Dec 12, 7:11 am, Brian A. [email protected] wrote:

than using detect.

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

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs