find index of first non zeo value in array

J

Josselin

with :
array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0]

I wrote :
array.index(array.detect {|x| x > 0}) => 15

is there a better and simpler way to do it ?
thanks

joss
 
O

Olivier

Le dimanche 26 novembre 2006 15:00, Josselin a =E9crit=A0:
with :
array =3D [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0]

I wrote :
array.index(array.detect {|x| x > 0}) =3D> 15

is there a better and simpler way to do it ?
thanks

joss

In that case, it is simpler to use an external counter, i think :

c =3D 0
array.each{|v| break if not v.zero?; c +=3D 1}
puts c # =3D> 15
 
D

dblack

---2049402039-511287211-1164554586=:11709
Content-Type: MULTIPART/MIXED; BOUNDARY="-2049402039-511287211-1164554586=:11709"

This message is in MIME format. The first part should be readable text,
while the remaining parts are likely unreadable without MIME-aware tools.

---2049402039-511287211-1164554586=:11709
Content-Type: TEXT/PLAIN; charset=X-UNKNOWN; format=flowed
Content-Transfer-Encoding: QUOTED-PRINTABLE

Hi --

Le dimanche 26 novembre 2006 15:00, Josselin a =E9crit=A0:
with :
array =3D [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0]

I wrote :
array.index(array.detect {|x| x > 0}) =3D> 15

is there a better and simpler way to do it ?
thanks

joss

In that case, it is simpler to use an external counter, i think :

c =3D 0
array.each{|v| break if not v.zero?; c +=3D 1}
puts c # =3D> 15

Or:

c =3D array.each_with_index {|e,i| break i unless e.zero? }


David

--=20
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
---2049402039-511287211-1164554586=:11709--
---2049402039-511287211-1164554586=:11709--
 
D

dblack

J

James Edward Gray II

Le dimanche 26 novembre 2006 15:00, Josselin a =E9crit :
with :
array =3D [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, = 0,
0, 0, 0, 0, 0, 0]

I wrote :
array.index(array.detect {|x| x > 0}) =3D> 15

is there a better and simpler way to do it ?
thanks

joss

In that case, it is simpler to use an external counter, i think :

c =3D 0
array.each{|v| break if not v.zero?; c +=3D 1}
puts c # =3D> 15

You can ask Ruby to maintain the counter, if you want:
require "enumerator" =3D> true
array =3D [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, =20=

0, 0,
?> 0, 0, 0, 0, 0, 0]
=3D> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, =20=

0, 0, 0, 0, 0]rescue nil
=3D> 15
array.slice!(15, 1) =3D> [21]
array
=3D> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, =20=

0, 0, 0]rescue nil
=3D> nil

James Edward Gray II=
 
P

Park Heesob

Hi,
From: Josselin <[email protected]>
Reply-To: (e-mail address removed)
To: (e-mail address removed) (ruby-talk ML)
Subject: find index of first non zeo value in array
Date: Sun, 26 Nov 2006 23:00:10 +0900

with :
array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0]

I wrote :
array.index(array.detect {|x| x > 0}) => 15

is there a better and simpler way to do it ?
thanks

joss
How about this:

array.index((array-[0])[0])

Regards,

Park Heesob

_________________________________________________________________
Don't just search. Find. Check out the new MSN Search!
http://search.msn.com/
 
D

dblack

---2049402039-1184309673-1164556388=:11991
Content-Type: MULTIPART/MIXED; BOUNDARY="-2049402039-1184309673-1164556388=:11991"

This message is in MIME format. The first part should be readable text,
while the remaining parts are likely unreadable without MIME-aware tools.

---2049402039-1184309673-1164556388=:11991
Content-Type: TEXT/PLAIN; charset=X-UNKNOWN; format=flowed
Content-Transfer-Encoding: QUOTED-PRINTABLE

Hi --

nil

That seems kind of like a reinvention of Array#index, though. It also
has the usual problem with rescue, i.e., that you might rescue the
wrong thing (if nonzero? is mistyped or whatever). Do you see an
advantage to doing it this way, rather than the index/detect way?
(I'm being lazy and not benchmarking them....)


David

--=20
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
---2049402039-1184309673-1164556388=:11991--
---2049402039-1184309673-1164556388=:11991--
 
J

James Edward Gray II

That seems kind of like a reinvention of Array#index, though. It also
has the usual problem with rescue, i.e., that you might rescue the
wrong thing (if nonzero? is mistyped or whatever). Do you see an
advantage to doing it this way, rather than the index/detect way?
(I'm being lazy and not benchmarking them....)

Well, it only walks the Array once. The other way walks it once for =20
detect() and again for index(). I agree that it's not sexy code though.

I believe there has been talk in the past of having index() take a =20
block for matching. That would solve this problem ideally. I can =20
submit an RCR if people think it's worth it, but I'm pretty sure Matz =20=

said it was OK last time it came up... (Correct me if I am wrong!)

James Edward Gray II
 
D

dblack

---2049402039-103646226-1164557367=:12154
Content-Type: MULTIPART/MIXED; BOUNDARY="-2049402039-103646226-1164557367=:12154"

This message is in MIME format. The first part should be readable text,
while the remaining parts are likely unreadable without MIME-aware tools.

---2049402039-103646226-1164557367=:12154
Content-Type: TEXT/PLAIN; charset=X-UNKNOWN; format=flowed
Content-Transfer-Encoding: QUOTED-PRINTABLE

Hi --

Well, it only walks the Array once. The other way walks it once for dete= ct()=20
and again for index(). I agree that it's not sexy code though.

I believe there has been talk in the past of having index() take a block = for=20
matching. That would solve this problem ideally. I can submit an RCR if= =20
people think it's worth it, but I'm pretty sure Matz said it was OK last = time=20
it came up... (Correct me if I am wrong!)

Yes, there's an accepted RCR for it. That will be good.

This exchange relates to something I've been pondering for a while,
namely: is there always (or very, very often) an inverse relation
between elegance of code and efficiency? I don't mean to sound like
I'm singling out your example -- on the contrary, it seems that over
and over we see cases where a nice concise solution bombs out compared
to one that's longer, possibly less clear (I still can't read the
enum* stuff as quickly and confidently as I can read the regular
Enumerable stuff, though that may just be due to stupidity), but
faster.

I haven't really documented or studied this in detail, though it does
seem to keep happening. It might be interesting to look into further.


David

--=20
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
---2049402039-103646226-1164557367=:12154--
---2049402039-103646226-1164557367=:12154--
 
D

Devin Mullins

James said:
I believe there has been talk in the past of having index() take a
block for matching.
Meh... talk schmalk...

class Array
alias orig_index index
def index(*args)
return orig_index(*args) unless block_given?
(0...length).each do |i|
return i if yield self
end
nil
end
end

array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0]
array.index 21 #=> 15
array.index {|n| n.nonzero? } #=> 15

Devin
 
R

Ross Bamford

I believe there has been talk in the past of having index() take a
block for matching. That would solve this problem ideally. I can
submit an RCR if people think it's worth it, but I'm pretty sure Matz
said it was OK last time it came up... (Correct me if I am wrong!)

IIRC Matz accepted it straight into 1.9. It's implemented there now:

$ ruby9 -ve 'p [0,0,0,15,0].index { |e| e > 0 }'
ruby 1.9.0 (2006-11-26 patchlevel 0) [i686-linux]
3
 
M

Martin DeMello

This exchange relates to something I've been pondering for a while,
namely: is there always (or very, very often) an inverse relation
between elegance of code and efficiency? I don't mean to sound like
I'm singling out your example -- on the contrary, it seems that over
and over we see cases where a nice concise solution bombs out compared
to one that's longer, possibly less clear (I still can't read the
enum* stuff as quickly and confidently as I can read the regular
Enumerable stuff, though that may just be due to stupidity), but
faster.

Seems to be a general symptom of everything being optimised to run C
code - the C-like solutions win out.

m.
 
R

Robert Klemme

with :
array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0]

I wrote :
array.index(array.detect {|x| x > 0}) => 15

is there a better and simpler way to do it ?
thanks

Did we have a solution with #inject already? In case we didn't:

irb(main):001:0> require 'enumerator'
=> true
irb(main):002:0> a=Array.new(10,0) << 666 << 0
=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 666, 0]
irb(main):003:0> a.to_enum:)each_with_index).inject(nil) {|pp,(x,i)|
break i unless x == 0}
=> 10
irb(main):004:0> a[0..5]
=> [0, 0, 0, 0, 0, 0]
irb(main):005:0> a[0..5].to_enum:)each_with_index).inject(nil)
{|pp,(x,i)| break i unless x == 0}
=> nil

Kind regards

robert
 
D

Devin Mullins

Robert said:
Did we have a solution with #inject already? In case we didn't:

irb(main):001:0> require 'enumerator'
=> true
irb(main):002:0> a=Array.new(10,0) << 666 << 0
=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 666, 0]
irb(main):003:0> a.to_enum:)each_with_index).inject(nil) {|pp,(x,i)|
break i unless x == 0}
=> 10

sub 'inject(nil) {|pp,(x,i)|', 'find {|x,i|'

But I vote for adding to Enumerable or Array. #index_where or #find_by
or something. (My preference over alias-and-delegate.)

Devin
 
P

Pradeep Jindal

Hi,
From: Josselin <[email protected]>
Reply-To: (e-mail address removed)
To: (e-mail address removed) (ruby-talk ML)
Subject: find index of first non zeo value in array
Date: Sun, 26 Nov 2006 23:00:10 +0900

with :
array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0]

I wrote :
array.index(array.detect {|x| x > 0}) => 15

is there a better and simpler way to do it ?
thanks

joss

How about this:

array.index((array-[0])[0])

Regards,

Park Heesob

_________________________________________________________________
Don't just search. Find. Check out the new MSN Search!
http://search.msn.com/

Not a generic way, but pretty good for this specific problem.

Regards,
Pradeep
 
W

William James

Robert said:
with :
array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0]

I wrote :
array.index(array.detect {|x| x > 0}) => 15

is there a better and simpler way to do it ?
thanks

Did we have a solution with #inject already?

array.inject(0){|i,e| if e>0; break i else i+1 end}
 
W

William James

William said:
Robert said:
with :
array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0]

I wrote :
array.index(array.detect {|x| x > 0}) => 15

is there a better and simpler way to do it ?
thanks

Did we have a solution with #inject already?

array.inject(0){|i,e| if e>0; break i else i+1 end}

array.inject(0){|i,e| break i if e>0; i+1 }
 
T

Tim Pease

William said:
Robert said:
On 26.11.2006 14:59, Josselin wrote:
with :
array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0]

I wrote :
array.index(array.detect {|x| x > 0}) => 15

is there a better and simpler way to do it ?
thanks

Did we have a solution with #inject already?

array.inject(0){|i,e| if e>0; break i else i+1 end}

array.inject(0){|i,e| break i if e>0; i+1 }

Using a regular expression ...

array.join =~ /[^0]/

The inject method is faster, though, because you do not have to create
a string. I'm assuming that the "array.index((array-[0])[0])" solution
suffers from the same problem -- creating another object under the
covers ...

$ ruby tmp.rb
user system total real
inject 2.874000 0.000000 2.874000 ( 2.884000)
index 0.721000 0.000000 0.721000 ( 0.731000)
regexp 3.755000 0.000000 3.755000 ( 3.765000)

Wow! Looks like the "index" method wins hands down.

But mine is still shorter ;)

TwP
 
P

Phrogz

Devin said:
James said:
I believe there has been talk in the past of having index() take a
block for matching.
Meh... talk schmalk...

class Array
alias orig_index index
def index(*args)
return orig_index(*args) unless block_given?
(0...length).each do |i|
return i if yield self
end
nil
end
end


Hrm, I assumed that using #each_with_index would be speedier, since
there would be no Ruby call to the #[] method of the array for each
iteration. Instead, it's much slower:

class Array
alias orig_index index

def index1(*args)
return orig_index(*args) unless block_given?
(0...length).each do |i|
return i if yield self
end
nil
end

def index2( *args )
return orig_index(*args) unless block_given?
each_with_index{ |value,index| return index if yield value }
nil
end
end

require 'benchmark'
N = 1000
a = (1..1000).to_a
Benchmark.bmbm{ |x|
x.report( '0...length' ){
N.times{ |i| a.index1{ |v| v==i } }
}

x.report( 'each_with_i' ){
N.times{ |i| a.index2{ |v| v==i } }
}
}

#=> Rehearsal -----------------------------------------------
#=> 0...length 2.060000 0.030000 2.090000 ( 2.333379)
#=> each_with_i 3.340000 0.050000 3.390000 ( 3.727040)
#=> -------------------------------------- total: 5.480000sec
#=>
#=> user system total real
#=> 0...length 2.060000 0.020000 2.080000 ( 2.286168)
#=> each_with_i 3.350000 0.040000 3.390000 ( 3.716824)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top