newbie, get index or reorder array (insert) by regexp

T

Timothy Byrd

Hi all,

I'm just beginning to "get" Ruby. (Coming from a mostly C/C++
background), and I have a question.

I want to rearrange some elements in an array. For a simple example,
given this:

a = %w(Ruby is useful C++ is very very useful)
b, c = a.partition {|w| w =~ /^v/}

then either of these commands seem to do what I want:

c.insert(c.index( c.find {|i| i =~ /^u/} ), b).flatten

or

c[c.index( c.find {|i| i =~ /^u/} ),0] = b

Yes, it works, but the nested calls to #index and #find seem ugly to
me. It seems there ought to be a cleaner way to get the index of the
first item matching the pattern. Or is there a more Ruby-like way to
do this?

Btw, the reason for this is that I want to generate a list of my CDs
off of my local CDDB database. But I want to sort artists like
"Loreena McKennitt" as "McKennitt, Loreena", so I'm going to put some
custom comments in the files, e.g. - "#SORT=McKennitt, Loreena" and
then use them in the ruby script that generates the list. According to
the CDDB standard, though, all comment lines for a record must precede
any lines with keywords, so I want to insert them in the middle.

Thanks,

-- Timothy
 
C

Csaba Henk

Hi all,

I'm just beginning to "get" Ruby. (Coming from a mostly C/C++
background), and I have a question.

I want to rearrange some elements in an array. For a simple example,
given this:

a = %w(Ruby is useful C++ is very very useful)
b, c = a.partition {|w| w =~ /^v/}

then either of these commands seem to do what I want:

c.insert(c.index( c.find {|i| i =~ /^u/} ), b).flatten

or

c[c.index( c.find {|i| i =~ /^u/} ),0] = b

As I see there is somewhat difference between the two solutions.

In the first case, the expression returns the array you need, but c won't
refer to that array.

In the second case, c will refer to the desired array, but the
expression doesn't return this array, you'll also have to
append a c to get it...

Nevermind.

Better patterns for that index + find part:

(0...c.size).find{|i| c =~ /^u/ }

or if you are sure it will match somewhere:

c.each_index {|i| break i if c =~ /^u/ }

(if it didn't match, it would return an array with the numbers from zero
to c.size, excluding the latter).

Yes, it works, but the nested calls to #index and #find seem ugly to
me. It seems there ought to be a cleaner way to get the index of the
first item matching the pattern. Or is there a more Ruby-like way to
do this?

Imho it would be great to have a method Array#each_with_index (but with
a better name than this :) ) which behaves as follows:

c.each_with_index {|i,v| break i if v =~ /^u/ }

(It's mainly an issue if you work with an anonymous array (eg., a slice
of some other array, or a result of Array#map) -- then you can't get the
value at the given index in the way as it's done above, simply by
writing "c").

Csaba
 
W

William James

Timothy said:
Btw, the reason for this is that I want to generate a list of my CDs
off of my local CDDB database. But I want to sort artists like
"Loreena McKennitt" as "McKennitt, Loreena", so I'm going to put some
custom comments in the files, e.g. - "#SORT=McKennitt, Loreena" and
then use them in the ruby script that generates the list.

irb(main):002:0> "Loreena Sue McKennitt".sub(/(.*) (.*?)$/,'\2, \1')
=> "McKennitt, Loreena Sue"
 
T

Timothy Byrd

Csaba said:
In the first case, the expression returns the array you need, but c
won't refer to that array.

Huh? Oh. My fault - I was using #flatten! and failed to copy/paste the
bang sign. That should assign to c, since #insert is also
self-modifying (hard to use the word "destructive" here).

Anyway, yeah, a #find_index method would have been handy. But now, I
may not need it (see below). And it's a beauty of Ruby that if I do
need it in the future, I should simply be able to extend either Array
or Enumerable.

William said:
irb(main):002:0> "Loreena Sue McKennitt".sub(/(.*) (.*?)$/,'\2, \1')
=> "McKennitt, Loreena Sue"

True, but I still want to sort "Jethro Tull" under the 'J's. :)

Also in certain cases, I want to change how an artist or album title
appears in my list. As an example I want "Kim Robertson and Virginia
Kron / Gratitude" to appear as "Kim Robertson - Gratitude" so I can put
her albums in chronological order.

I got it working (except for a final tweak to the list format) last
night - and then realized I was stupid. I'd gone so far as to make a
script to gather all my custom comments into a text file, so I could
re-apply them to the database. At that point, I thought, why bother
modifying all the CDDB files, when I can just fold in my custom changes
when I make the list?

Time to simplify.

The last bit is a simple puzzle (did it in C years ago) - I just want
to find a clever Ruby-ish way to do it.

Given a list of albums:

albums = [
"Apocalyptica - Plays Metallica By Four Cellos",
"Kim Robertson - Gratitude",
"Synergy - Electronic Realizations For Rock Orchestra",
"Synergy - Sequencer",
"Synergy - Chords",
"Synergy - Games",
]

I want the list output to look like:

1.\tApocalyptica - Plays Metallica By Four Cellos
2.\tKim Robertson - Gratitude
\tSynergy:
3.\t- Electronic Realizations For Rock Orchestra
4.\t- Sequencer
5.\t- Chords
6.\t- Games

So if an artist has more than one album, I'll gather the albums under
the artist. It's close to trivial, and there are a ton of ways to do
this, so I'll have fun. First I'll write it in a C-like style, then
I'll try Ruby-ifying it.

-- Timothy
 

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,766
Messages
2,569,569
Members
45,045
Latest member
DRCM

Latest Threads

Top