New to Ruby - pls help in translating this

>
> seen_ary = Array.new
>
> File.open("nums","r").each do |elem|
> print elem if seen_ary.include?(elem)
> seen_ary.push(elem)
> end
>
> (there are probably still better ways of doing this though)

I'll go with:

seen = {}

ARGF.each do |elem|
   print elem if seen.include? elem
   seen[elem] = true
end

And yes, this way is much better (I knew it would be but, it's nice to
see just how much better):

require 'benchmark'
n = 100

# build a big array with lots of duplication
@nums = (1..1_000).to_a
(1..1_000).step(17) {|num| @nums.push(num) }

# set up two versions of our rubylike version
# the original perl-like version and the
# final version
def as_ary
  seen_ary = Array.new

  @nums.each do |elem|
    $stderr.print elem if seen_ary.include?(elem)
    seen_ary.push(elem)
  end
end

def as_hash
  seen = Hash.new

  @nums.each do |elem|
    $stderr.print elem if seen.include?(elem)
    seen[elem] = true
  end
end

# then benchmark them to see how they perform
Benchmark.bm(10) do |x|
  x.report("as Array") {for i in 1..n; as_ary; end }
  x.report("as Hash") {for i in 1..n; as_hash; end }
end

# and the results are:
# ./print_dups.rb 2> /dev/null
# user system total real
#as Array 12.690000 0.030000 12.720000 ( 13.940651)
#as Hash 0.230000 0.000000 0.230000 ( 0.613217)

···

On 12/9/05, Eric Hodel <drbrain@segment7.net> wrote:

On Dec 9, 2005, at 11:23 AM, pat eyler wrote:

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

--
thanks,
-pate
-------------------------

Why? Backticks are great.

···

On Sat, Dec 10, 2005 at 10:03:06AM +0900, pat eyler wrote:

but if this is supposed to be part of a larger program, shell
is probably the wrong way to go about it.

--
Chad Perrin [ CCD CopyWrite | http://ccd.apotheon.org ]

unix virus: If you're using a unixlike OS, please forward
this to 20 others and erase your system partition.

Eric Hodel wrote:

seen = {}

ARGF.each do |elem|
   print elem if seen.include? elem
   seen[elem] = true
end

seen = {}
while s = gets
  print s if seen.key? s
  seen[ s ] = nil
end

Or:

seen = Hash.new(0)
while s = gets
  print s if ( seen[s] += 1 ) > 1
end

Hi --

I'll go with:

seen = {}

ARGF.each do |elem|
  print elem if seen.include? elem
  seen[elem] = true
end

Thanks, this is how ruby should look like!

here is another one (i like it a bit more
functional style):

quantities = Hash.new{|h, k| h[k]=0}
ARGF.each{|l| quantities[l.chomp.to_i] += 1}

Since you're doing a h += 1 thing, you don't need to do the
auto-assignment in the block:

   quantities = Hash.new(0)

will work fine.

Also, String#to_i is smart enough not to need the chomp.

So, as so often happens, code evaporates from the screen as one works
on a Ruby program :slight_smile:

David

···

On Sat, 10 Dec 2005, Simon Kröger wrote:

--
David A. Black
dblack@wobblini.net

"Ruby for Rails", forthcoming from Manning Publications, April 2006!

Here's what I decided on using (looks more like what I originally posted
in perl):

hash = Hash.new(0)
File.open(ARGV[0],"r").each do |line|

Replacing the above with:

ARGF.each do |line|

Is the same thing, but more powerful. You could feed it multiple files at once, or use it in pipelined processing. Just FYI.

James Edward Gray II

···

On Dec 9, 2005, at 2:53 PM, Sam Dela Cruz wrote:

    hash[line.chomp] += 1
end

hash.keys.sort.each do |key|
    puts "#{key}: #{hash[key]}" if hash[key] > 1
end

Regards,
Sam Dela Cruz

Eric Hodel <drbrain@segment7.net>
12/09/2005 11:39 AM
Please respond to
ruby-talk@ruby-lang.org

To
ruby-talk@ruby-lang.org (ruby-talk ML)
cc

Subject
Re: new to Ruby - pls help in translating this
Classification

On Dec 9, 2005, at 11:23 AM, pat eyler wrote:

On 12/9/05, Sam Dela Cruz <sam.dela.cruz@philips.com> wrote:

Hi,

I'm starting to use Ruby in one of my projects at work. I'm
coming from a
Perl background.
In my project I would need to parse a list of numbers (thousands
of them)
and then return the duplicates. In perl, I can do this:

[elided perl goo]

I tried to translate this in Ruby, but could not find en
equivalent of
$hash{$_}++, this is auto increment.
Can somebody tell me how this is to be done in Ruby?

translating from Perl to Ruby seems often to be a bad idea ... a
common idea, but not necessarily a good one. I'd rather work
with a Ruby solution to a problem than a Rubification of a Perl
solution to a problem.

Ditto. I often find that I can make my code close to readable
English and find that a very good thing.

Or maybe the Ruby way on how to attack this whole thing. Thanks.

I'm assuming that your list comes from a file (but you can change that
pretty easily in the code below), given that, how about something
like:

seen_ary = Array.new

File.open("nums","r").each do |elem|
  print elem if seen_ary.include?(elem)
  seen_ary.push(elem)
end

(there are probably still better ways of doing this though)

I'll go with:

seen = {}

ARGF.each do |elem|
   print elem if seen.include? elem
   seen[elem] = true
end

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

Quoting Sam Dela Cruz <sam.dela.cruz@philips.com>:

Here's what I decided on using (looks more like what I originally
posted in perl):

hash = Hash.new(0)
File.open(ARGV[0],"r").each do |line|
    hash[line.chomp] += 1
end

hash.keys.sort.each do |key|
    puts "#{key}: #{hash[key]}" if hash[key] > 1
end

One caution here -- if you want the file handle to be closed when
you are finished, you must do either:

stream = File.open(ARGV[0],"r")
stream.each do |line|
   hash[line.chomp] += 1
end
stream.close

or (this is often better because it will automatically close the
handle even if there's an exception):

File.open(ARGV[0],"r") do |stream|
   stream.each do |line|
     hash[line.chomp] += 1
   end
end

-mental

well, there is a+=1
and a++ would be a+! imho...

···

Am Freitag, 9. Dezember 2005 21:31 schrieb Logan Capaldo:

On Dec 9, 2005, at 2:24 PM, JB Eriksson wrote:
> well, there is the succ method. and there's a succ! on String,
> which could
> work in this case, I guess(?), since:
>
> irb(main):023:0> a = "9"
> => "9"
> irb(main):024:0> a.succ!
> => "10"

Well, if it was a string. The problem is fixnums are immediate
values. ++ would therefore be an operation on the variable a, rather
than the object that a points at.

Platform independence?

Granted I get away with a lot via MingGW, but if you want to code on
Linux / OS X and work for people who want the code to run on
Windows... well... platform independance is important

Though it does of course depend what you're working on. :slight_smile:

···

On 12/9/05, Chad Perrin <perrin@apotheon.com> wrote:

Why? Backticks are great.

William James wrote:

Eric Hodel wrote:

> seen = {}
>
> ARGF.each do |elem|
> print elem if seen.include? elem
> seen[elem] = true
> end

seen = {}
while s = gets
  print s if seen.key? s
  seen[ s ] = nil
end

Or:

seen = Hash.new(0)
while s = gets
  print s if ( seen[s] += 1 ) > 1
end

seen, s = Hash.new(0)
print s if ( seen[s] += 1 ) > 1 while s = gets

Yes, file handles must be closed. I learned that the hard way on one of
my projects before. Thanks again.
Sam

mental@rydia.net
12/09/2005 01:02 PM

To
Sam Dela Cruz/SVL/SC/PHILIPS@PHILIPS
cc

Subject
Re: new to Ruby - pls help in translating this
Classification

Quoting Sam Dela Cruz <sam.dela.cruz@philips.com>:

Here's what I decided on using (looks more like what I originally
posted in perl):

hash = Hash.new(0)
File.open(ARGV[0],"r").each do |line|
    hash[line.chomp] += 1
end

hash.keys.sort.each do |key|
    puts "#{key}: #{hash[key]}" if hash[key] > 1
end

One caution here -- if you want the file handle to be closed when
you are finished, you must do either:

stream = File.open(ARGV[0],"r")
stream.each do |line|
   hash[line.chomp] += 1
end
stream.close

or (this is often better because it will automatically close the
handle even if there's an exception):

File.open(ARGV[0],"r") do |stream|
   stream.each do |line|
     hash[line.chomp] += 1
   end
end

-mental

Or:

File.foreach(ARGV.first) do |line|
   # ...
end

James Edward Gray II

···

On Dec 9, 2005, at 3:02 PM, mental@rydia.net wrote:

Quoting Sam Dela Cruz <sam.dela.cruz@philips.com>:

Here's what I decided on using (looks more like what I originally
posted in perl):

hash = Hash.new(0)
File.open(ARGV[0],"r").each do |line|
    hash[line.chomp] += 1
end

hash.keys.sort.each do |key|
    puts "#{key}: #{hash[key]}" if hash[key] > 1
end

One caution here -- if you want the file handle to be closed when
you are finished, you must do either:

stream = File.open(ARGV[0],"r")
stream.each do |line|
   hash[line.chomp] += 1
end
stream.close

or (this is often better because it will automatically close the
handle even if there's an exception):

File.open(ARGV[0],"r") do |stream|
   stream.each do |line|
     hash[line.chomp] += 1
   end
end

dblack@wobblini.net wrote:

quantities = Hash.new{|h, k| h[k]=0}
ARGF.each{|l| quantities[l.chomp.to_i] += 1}

Since you're doing a h += 1 thing, you don't need to do the
auto-assignment in the block:

  quantities = Hash.new(0)

will work fine.

yes, saw that after pressing the submit button, but as said before
using this form of Hash.new is source of hard to track bugs.

Also, String#to_i is smart enough not to need the chomp.

So, as so often happens, code evaporates from the screen as one works
on a Ruby program :slight_smile:

you are so true:

require 'set'
seen = Set.new
puts ARGF.reject{|l| seen.add? l}

cheers

Simon

Quoting James Edward Gray II <james@grayproductions.net>:

Replacing the above with:

ARGF.each do |line|

Is the same thing, but more powerful. You could feed it multiple
files at once, or use it in pipelined processing. Just FYI.

Ah, thank you. I couldn't remember how to do that. That's closer
in spirit to his original Perl fragment, which used <>.

Perl:

while (<>) {
   ...
}

Ruby:

while ARGF.gets
   ...
end

(not that I'm advocating $_)

-mental

Good point. Okay, you win.

. . . except when I'm writing unix administration scripts.

···

On Sat, Dec 10, 2005 at 11:37:02AM +0900, Gregory Brown wrote:

On 12/9/05, Chad Perrin <perrin@apotheon.com> wrote:

> Why? Backticks are great.

Platform independence?

Granted I get away with a lot via MingGW, but if you want to code on
Linux / OS X and work for people who want the code to run on
Windows... well... platform independance is important

Though it does of course depend what you're working on. :slight_smile:

--
Chad Perrin [ CCD CopyWrite | http://ccd.apotheon.org ]

unix virus: If you're using a unixlike OS, please forward
this to 20 others and erase your system partition.

William James wrote:

William James wrote:
> Eric Hodel wrote:
>
> > seen = {}
> >
> > ARGF.each do |elem|
> > print elem if seen.include? elem
> > seen[elem] = true
> > end
>
> seen = {}
> while s = gets
> print s if seen.key? s
> seen[ s ] = nil
> end
>
> Or:
>
> seen = Hash.new(0)
> while s = gets
> print s if ( seen[s] += 1 ) > 1
> end

seen, s = Hash.new(0)
print s if ( seen[s] += 1 ) > 1 while s = gets

seen = Hash.new(0)
print if ( seen[$_] += 1 ) > 1 while gets

Of COURSE! :slight_smile:
You can even port a lot of those straight to windows via MSys and MinGW.
(Or it's ugly cousin Cygwin)

···

On 12/10/05, Chad Perrin <perrin@apotheon.com> wrote:

Good point. Okay, you win.

. . . except when I'm writing unix administration scripts.

William James wrote:

William James wrote:

Eric Hodel wrote:

seen = {}

ARGF.each do |elem|
   print elem if seen.include? elem
   seen[elem] = true
end

seen = {}
while s = gets
  print s if seen.key? s
  seen[ s ] = nil
end

Or:

seen = Hash.new(0)
while s = gets
  print s if ( seen[s] += 1 ) > 1
end

This is starting to get pointlessly obfuscated.

seen, s = Hash.new(0)
print s if ( seen[s] += 1 ) > 1 while s = gets

Stop. You're making things hard to read for the new people.

seen = Hash.new(0)
print if ( seen[$_] += 1 ) > 1 while gets

I can't read that so I don't know how you expect someone new to Ruby to read it.

I've never seen 'ruby golf' do anything positive. I find it especially inappropriate to be performing these vile manipulations while trying to show new people idiomatic Ruby like they asked for:

···

On Dec 11, 2005, at 7:27 AM, William James wrote:

Can somebody tell me how this is to be done in Ruby? Or maybe the Ruby
way on how to attack this whole thing. Thanks.

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

Hi --

quantities = Hash.new{|h, k| h[k]=0}
ARGF.each{|l| quantities[l.chomp.to_i] += 1}

Since you're doing a h += 1 thing, you don't need to do the
auto-assignment in the block:

  quantities = Hash.new(0)

will work fine.

yes, saw that after pressing the submit button, but as said before
using this form of Hash.new is source of hard to track bugs.

[I didn't see this post until just now.]

I don't think so. The only problems I've ever seen people encounter
with it are from not knowing that:

   Hash.new()

will always give you the same object. But once you know that, and
therefore know when not to use it, it should be fine.

David

···

On Sat, 10 Dec 2005, Simon Kröger wrote:

dblack@wobblini.net wrote:

--
David A. Black
dblack@wobblini.net

"Ruby for Rails", from Manning Publications, coming April 2006!

Eric Hodel wrote:

> William James wrote:
>> William James wrote:
>>> Eric Hodel wrote:
>>>
>>>> seen = {}
>>>>
>>>> ARGF.each do |elem|
>>>> print elem if seen.include? elem
>>>> seen[elem] = true
>>>> end
>>>
>>> seen = {}
>>> while s = gets
>>> print s if seen.key? s
>>> seen[ s ] = nil
>>> end
>>>
>>> Or:
>>>
>>> seen = Hash.new(0)
>>> while s = gets
>>> print s if ( seen[s] += 1 ) > 1
>>> end

This is starting to get pointlessly obfuscated.

>> seen, s = Hash.new(0)
>> print s if ( seen[s] += 1 ) > 1 while s = gets

Stop. You're making things hard to read for the new people.

> seen = Hash.new(0)
> print if ( seen[$_] += 1 ) > 1 while gets

I can't read that so I don't know how you expect someone new to Ruby
to read it.

A standard Ruby idiom: instead of 3 lines . . .

  if test
    print "ok"
  end

.. . . 1 line:

  print "ok" if test

Also, as in Awk, "print" with no argument prints the line just read.
Very simple. If one needs to explicitly refer to the line just read,
one uses "$_".

And "while gets" is simpler and clearer than "ARGF.each do |elem|".

···

On Dec 11, 2005, at 7:27 AM, William James wrote:

You would get a lot of support in Perland, but this kind of code is very out of fashion in the Ruby community.

James Edward Gray II

···

On Dec 12, 2005, at 12:32 PM, William James wrote:

Also, as in Awk, "print" with no argument prints the line just read.
Very simple. If one needs to explicitly refer to the line just read,
one uses "$_".

And "while gets" is simpler and clearer than "ARGF.each do |elem|".