Idiomatic ruby version of this code?

(Brock Weaver) #1

Showing off ruby to a coworker, and I want to emphasize how succinct yet
clear it can be. So I want to rename all .mp3 files to .temp, or vice versa
in the current directory -- in the shortest LOC *that you would actually write*;
not the "here's a one-liner nobody in their right mind would every write if
they had to maintain it".

Here's my first solution:

Dir.new("./").each do |file|
  if file.match(/mp3$/)
    File.rename(file, file.gsub(/mp3$/, 'temp'))
  elsif file.match(/temp$/)
    File.rename(file, file.gsub(/temp$/, 'mp3'))
  end
end

Note you can NOT do the following, as it renames mp3 to temp,
then immediately renames that same file back to mp3...

Dir.new("./").each do |file|
  File.rename(file, file.gsub(/mp3$/, 'temp')) if file.match(/mp3$/)
  File.rename(file, file.gsub(/temp$/, 'mp3')) if file.match(/temp$/)
end

Any ideas?

···

--
Brock Weaver
[OBC]Technique

(Brian Schröder) #2

If it where a one time solution, I'd do something like this. Though
maybe I'd extract the file endings into variables, to make it easier
to change them.

mp3 = Dir['*.mp3']
temp = Dir['*.temp']
mp3.each do | fn | File.rename(fn, File.basename(fn, '.mp3') + '.temp') end
temp.each do | fn | File.rename(fn, File.basename(fn, '.temp') + '.mp3') end

regards,

Brian

···

On 18/08/05, Brock Weaver <brockweaver@gmail.com> wrote:

Showing off ruby to a coworker, and I want to emphasize how succinct yet
clear it can be. So I want to rename all .mp3 files to .temp, or vice versa
in the current directory -- in the shortest LOC *that you would actually write*;
not the "here's a one-liner nobody in their right mind would every write if
they had to maintain it".

Here's my first solution:

Dir.new("./").each do |file|
        if file.match(/mp3$/)
                File.rename(file, file.gsub(/mp3$/, 'temp'))
        elsif file.match(/temp$/)
                File.rename(file, file.gsub(/temp$/, 'mp3'))
        end
end

Note you can NOT do the following, as it renames mp3 to temp,
then immediately renames that same file back to mp3...

Dir.new("./").each do |file|
        File.rename(file, file.gsub(/mp3$/, 'temp')) if file.match(/mp3$/)
        File.rename(file, file.gsub(/temp$/, 'mp3')) if file.match(/temp$/)
end

Any ideas?

--
Brock Weaver
[OBC]Technique

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/

(Ben Giddings) #3

def switch_endings(current_ending, new_ending)
  files = Dir.glob("*.#{current_ending}") do |filename|
    File.rename(filename, filename.gsub(/\.#{current_ending}$/,
".#{new_ending}"))
  end
end

As for doing both renames at the same time (i.e. swapping the various
endings) you can't really do that without using a third temporary ending,
so you could do:

switch_endings("mp3", "mp3foo")
switch_endings("temp", "mp3")
switch_endings("mp3foo", "temp")

Ben

(Berger, Daniel) #4

Brock Weaver wrote:

Showing off ruby to a coworker, and I want to emphasize how succinct yet
clear it can be. So I want to rename all .mp3 files to .temp, or vice versa
in the current directory -- in the shortest LOC *that you would actually write*;
not the "here's a one-liner nobody in their right mind would every write if
they had to maintain it".

Here's my first solution:

Dir.new("./").each do |file|
  if file.match(/mp3$/)
    File.rename(file, file.gsub(/mp3$/, 'temp'))
  elsif file.match(/temp$/)
    File.rename(file, file.gsub(/temp$/, 'mp3'))
  end
end

Dir["*.mp3"].each{ |file|
    new_name = File.basename(file, ".mp3") + ".temp"
    File.rename(file, new_name)
}

Regards,

Dan

(Zach Dennis) #5

Brock Weaver wrote:

Showing off ruby to a coworker, and I want to emphasize how succinct yet
clear it can be. So I want to rename all .mp3 files to .temp, or vice versa
in the current directory -- in the shortest LOC *that you would actually write*;
not the "here's a one-liner nobody in their right mind would every write if
they had to maintain it".

Here's my first solution:

Dir.new("./").each do |file|
  if file.match(/mp3$/)
    File.rename(file, file.gsub(/mp3$/, 'temp'))
  elsif file.match(/temp$/)
    File.rename(file, file.gsub(/temp$/, 'mp3'))
  end
end

Note you can NOT do the following, as it renames mp3 to temp,
then immediately renames that same file back to mp3...

Dir.new("./").each do |file|
  File.rename(file, file.gsub(/mp3$/, 'temp')) if file.match(/mp3$/)
  File.rename(file, file.gsub(/temp$/, 'mp3')) if file.match(/temp$/)
end

Any ideas?

arr1, arr2 = Dir["*.mp3"], Dir["*.temp"]
arr1.each { |f| File.rename( f, f.sub( /mp3$/, 'temp' ) ) }
arr2.each { |f| File.rename( f, f.sub( /temp$/, 'mp3' ) ) }

Zach

(why the lucky stiff) #6

Brock Weaver wrote:

Here's my first solution:

Dir.new("./").each do |file|
  if file.match(/mp3$/)
    File.rename(file, file.gsub(/mp3$/, 'temp'))
  elsif file.match(/temp$/)
    File.rename(file, file.gsub(/temp$/, 'mp3'))
  end
end
  

You could use the tertiary operator to switch file names, but the (exp .. and .. or) is also nice for this kind of swapping thing.

  Dir["*.{temp,mp3}"].each do |file|
    File.rename( file, file.gsub( /\w+$/ ) { |ext| ext == 'mp3' and 'temp' or 'mp3' } )
  end

If you don't want your extensions hardcoded everywhere, use an array:

  exts = ['temp', 'mp3']
  Dir["*.{#{exts.join ','}}"].each do |file|
    File.rename( file, file.gsub( /(\w+)$/ ) { exts.detect { |x| x != $1 } } )
  end

_why

(Mark Probert) #7

Hi ..

Showing off ruby to a coworker, and I want to emphasize how succinct yet
clear it can be. ...
Here's my first solution:

...
Any ideas?

I know that this is kind of heretical, but in a situation like this, I'd
rather use a shell script (I tend to inexpertly use zsh). In this case,
the simple solution is something like:

$ a=(*.mp3)
$ for f in $a; do
mv $f `basename $f .mp3`.temp
done

Pretty easy to read (I'd say more so than the Ruby, IMO) and almost a
one-liner. An easy idiom to remember, no editing required. If you need
to reverse it, then up-arrow and change the lines (or use another
variable, if needed).

However, if I had to do this as part of a larger project done in Ruby,
then I would have no hesitation to take the Ruby route (and I have done
almost exactly this at times).

My $0.02

Regards,

···

On Fri, 2005-08-19 at 01:24 +0900, Brock Weaver wrote:

--
-mark. (probertm at acm dot org)

(Brock Weaver) #8

Perfect! Thank you for your quick response. I decided to take the
shortcut of parallel variable assignment too, so I'm violating my own
rule a little I guess.

Anyway, thanks Brian.

···

On 8/18/05, Brian Schröder <ruby.brian@gmail.com> wrote:

On 18/08/05, Brock Weaver <brockweaver@gmail.com> wrote:
> Showing off ruby to a coworker, and I want to emphasize how succinct yet
> clear it can be. So I want to rename all .mp3 files to .temp, or vice versa
> in the current directory -- in the shortest LOC *that you would actually write*;
> not the "here's a one-liner nobody in their right mind would every write if
> they had to maintain it".
>
> Here's my first solution:
>
> Dir.new("./").each do |file|
> if file.match(/mp3$/)
> File.rename(file, file.gsub(/mp3$/, 'temp'))
> elsif file.match(/temp$/)
> File.rename(file, file.gsub(/temp$/, 'mp3'))
> end
> end
>
> Note you can NOT do the following, as it renames mp3 to temp,
> then immediately renames that same file back to mp3...
>
> Dir.new("./").each do |file|
> File.rename(file, file.gsub(/mp3$/, 'temp')) if file.match(/mp3$/)
> File.rename(file, file.gsub(/temp$/, 'mp3')) if file.match(/temp$/)
> end
>
> Any ideas?
>
> --
> Brock Weaver
> [OBC]Technique
>
>

If it where a one time solution, I'd do something like this. Though
maybe I'd extract the file endings into variables, to make it easier
to change them.

mp3 = Dir['*.mp3']
temp = Dir['*.temp']
mp3.each do | fn | File.rename(fn, File.basename(fn, '.mp3') + '.temp') end
temp.each do | fn | File.rename(fn, File.basename(fn, '.temp') + '.mp3') end

regards,

Brian

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/

--
Brock Weaver
[OBC]Technique

(Berger, Daniel) #9

why the lucky stiff wrote:

Brock Weaver wrote:

Here's my first solution:

Dir.new("./").each do |file|
    if file.match(/mp3$/)
        File.rename(file, file.gsub(/mp3$/, 'temp'))
    elsif file.match(/temp$/)
        File.rename(file, file.gsub(/temp$/, 'mp3'))
    end
end
  
You could use the tertiary operator to switch file names, but the (exp .. and .. or) is also nice for this kind of swapping thing.

Dir["*.{temp,mp3}"].each do |file|
   File.rename( file, file.gsub( /\w+$/ ) { |ext| ext == 'mp3' and 'temp' or 'mp3' } )
end

If you don't want your extensions hardcoded everywhere, use an array:

exts = ['temp', 'mp3']
Dir["*.{#{exts.join ','}}"].each do |file|
   File.rename( file, file.gsub( /(\w+)$/ ) { exts.detect { |x| x != $1 } } )
end

_why

gsub and backreferences? Why take that approach when File.basename is available?

def switch(old, new)
    Dir["*.#{old}"].each{ |file|
       new_name = File.basename(file, old) + "." + new
       File.rename(file, new_name)
    }
end

Regards,

Dan

(Gavin Kistner) #10

Slightly more DRY (and yes, I'd use this myself):

mappings = { '.mp3' => '.temp', '.temp' => '.mp3' }
mappings.each{ |orig_ext, new_ext|
  Dir[ "*#{orig_ext}" ].each{ |filename|
    File.rename( filename, File.basename( filename, orig_ext ) +
new_ext )
  }
}

(Guest) #11

Sorry for being late to the party (and wildly off topic for a ruby-talk thread).

Hi ..

I know that this is kind of heretical, but in a situation like this, I'd
rather use a shell script (I tend to inexpertly use zsh). In this case,
the simple solution is something like:

$ a=(*.mp3)
$ for f in $a; do
mv $f `basename $f .mp3`.temp
done

(BTW, my local zsh doesn't like basename.)

This doesn't really answer the initial requirement though, here's
an idiomatic bash version ... although the variable mangling's a
neat trick, a lot of folks here will probably find it too much like
line noise.

MP3=`echo *.mp3`
TEMP=`echo *.temp`
for file in $MP3; do mv $file ${file%.mp3}.temp; done
for file in $TEMP; do mv $file ${file%.temp}.mp3; done

it's still not safe in terms of overwriting existing files, and there's
no question in my mind that the Ruby versions are prettier.

···

On 8/18/05, Mark Probert <probertm@acm.org> wrote:

Pretty easy to read (I'd say more so than the Ruby, IMO) and almost a
one-liner. An easy idiom to remember, no editing required. If you need
to reverse it, then up-arrow and change the lines (or use another
variable, if needed).

However, if I had to do this as part of a larger project done in Ruby,
then I would have no hesitation to take the Ruby route (and I have done
almost exactly this at times).

My $0.02

Regards,

--
-mark. (probertm at acm dot org)

--
thanks,
-pate
-------------------------
We are often unable to tell people what they need to know, because
they want to know something else, and would therefore only
misunderstand what we said
- the Raven (George MacDonald, Lilith)

(Pit) #12

Brock Weaver schrieb:

Perfect! Thank you for your quick response. I decided to take the
shortcut of parallel variable assignment too, so I'm violating my own
rule a little I guess.

Note that all the offered solutions including your original version don't work if there are two files named "x.mp3" and "x.temp".

Regards,
Pit

(why the lucky stiff) #13

Daniel Berger wrote:

gsub and backreferences? Why take that approach when File.basename is available?

def switch(old, new)
   Dir["*.#{old}"].each{ |file|
      new_name = File.basename(file, old) + "." + new
      File.rename(file, new_name)
   }
end

You understand he's trying to swap extensions, right? (I think?) Well, you've got to get the extension name. And File.extname( file )[1..-1] is ugly.

I always go for short and dirty. I steal second base!

_why

(Berger, Daniel) #14

Pit Capitain wrote:

Brock Weaver schrieb:

Perfect! Thank you for your quick response. I decided to take the
shortcut of parallel variable assignment too, so I'm violating my own
rule a little I guess.

Note that all the offered solutions including your original version don't work if there are two files named "x.mp3" and "x.temp".

Regards,
Pit

def switch(old, new)
    Dir["*.#{old}"].each{ |file|
       new_name = File.basename(file, old) + "." + new
       File.rename(file, new_name) unless File.exists?(new_name)
    }
end

Regards,

Dan

(Pit) #15

Pit Capitain schrieb:

Brock Weaver schrieb:

Perfect! Thank you for your quick response. I decided to take the
shortcut of parallel variable assignment too, so I'm violating my own
rule a little I guess.

Note that all the offered solutions including your original version don't work if there are two files named "x.mp3" and "x.temp".

Sorry, Ben's version works if you choose a non-existing temporary ending.

Regards,
Pit

(Berger, Daniel) #16

why the lucky stiff wrote:

Daniel Berger wrote:

gsub and backreferences? Why take that approach when File.basename is available?

def switch(old, new)
   Dir["*.#{old}"].each{ |file|
      new_name = File.basename(file, old) + "." + new
      File.rename(file, new_name)
   }
end

You understand he's trying to swap extensions, right? (I think?) Well, you've got to get the extension name. And File.extname( file )[1..-1] is ugly.

I always go for short and dirty. I steal second base!

_why

Oh, he wants to switch both. Hm, ok. Still no gsub or backreferences:

djberge@~/programming/ruby/temp-670>cat switcher.rb
Dir["*.{mp3,temp}"].each{ |file|
    ext = File.extname(file) == ".mp3" ? ".temp" : ".mp3"
    new_name = File.basename(file, ".*") + ext
    File.rename(file, new_name)
}
djberge@~/programming/ruby/temp-671>ls -l
total 2
-rw-r--r-- 1 djberge staff 0 Aug 18 13:08 bar.temp
-rw-r--r-- 1 djberge staff 0 Aug 18 13:08 baz.mp3
-rw-r--r-- 1 djberge staff 0 Aug 18 13:09 blah.nada
-rw-r--r-- 1 djberge staff 0 Aug 18 13:08 foo.mp3
-rw-r--r-- 1 djberge staff 169 Aug 18 13:15 switcher.rb
djberge@~/programming/ruby/temp-672>ruby switcher.rb
djberge@~/programming/ruby/temp-673>ls -l
total 2
-rw-r--r-- 1 djberge staff 0 Aug 18 13:08 bar.mp3
-rw-r--r-- 1 djberge staff 0 Aug 18 13:08 baz.temp
-rw-r--r-- 1 djberge staff 0 Aug 18 13:09 blah.nada
-rw-r--r-- 1 djberge staff 0 Aug 18 13:08 foo.temp
-rw-r--r-- 1 djberge staff 169 Aug 18 13:15 switcher.rb

(Brock Weaver) #17

Thank you all for your replies. I've chosen to use a cross between
Zach's and Brian's implementations.

arr1, arr2 = Dir["*.mp3"], Dir["*.temp"]
arr1.each do | fn | File.rename(fn, File.basename(fn, '.mp3') + '.temp') end
arr2.each do | fn | File.rename(fn, File.basename(fn, '.temp') + '.mp3') end

Zach's is both concise and clear -- I don't need flexibility per se;
hardcoding extensions is fine. Brian's use of File.basename instead
of regex made the script run roughly 5x faster on my XP box (I'm
renaming about 2,500 files).

···

On 8/18/05, Daniel Berger <Daniel.Berger@qwest.com> wrote:

why the lucky stiff wrote:
> Daniel Berger wrote:
>
>> gsub and backreferences? Why take that approach when File.basename is
>> available?
>>
>> def switch(old, new)
>> Dir["*.#{old}"].each{ |file|
>> new_name = File.basename(file, old) + "." + new
>> File.rename(file, new_name)
>> }
>> end
>
> You understand he's trying to swap extensions, right? (I think?) Well,
> you've got to get the extension name. And File.extname( file )[1..-1]
> is ugly.
>
> I always go for short and dirty. I steal second base!
>
> _why

Oh, he wants to switch both. Hm, ok. Still no gsub or backreferences:

djberge@~/programming/ruby/temp-670>cat switcher.rb
Dir["*.{mp3,temp}"].each{ |file|
    ext = File.extname(file) == ".mp3" ? ".temp" : ".mp3"
    new_name = File.basename(file, ".*") + ext
    File.rename(file, new_name)
}
djberge@~/programming/ruby/temp-671>ls -l
total 2
-rw-r--r-- 1 djberge staff 0 Aug 18 13:08 bar.temp
-rw-r--r-- 1 djberge staff 0 Aug 18 13:08 baz.mp3
-rw-r--r-- 1 djberge staff 0 Aug 18 13:09 blah.nada
-rw-r--r-- 1 djberge staff 0 Aug 18 13:08 foo.mp3
-rw-r--r-- 1 djberge staff 169 Aug 18 13:15 switcher.rb
djberge@~/programming/ruby/temp-672>ruby switcher.rb
djberge@~/programming/ruby/temp-673>ls -l
total 2
-rw-r--r-- 1 djberge staff 0 Aug 18 13:08 bar.mp3
-rw-r--r-- 1 djberge staff 0 Aug 18 13:08 baz.temp
-rw-r--r-- 1 djberge staff 0 Aug 18 13:09 blah.nada
-rw-r--r-- 1 djberge staff 0 Aug 18 13:08 foo.temp
-rw-r--r-- 1 djberge staff 169 Aug 18 13:15 switcher.rb

--
Brock Weaver
[OBC]Technique