Problem populating a hash with regex results

Discussion in 'Ruby' started by RichardOnRails, Jan 16, 2011.

  1. Hi,

    I've got a text file with 7 fields. I successfully extract the data
    on each with a regex and for debugging purposes print each lines
    fields.

    Now I want to edit and organize the data for subsequent processing.
    But populating the hash fails. I posted the code on http://www.pastie.org/1468271

    The "puts" on line 15 shows that the regex captures the 7 fields in
    the text file.
    Lines 17-19 is an attempt to build a hash using the known values in
    the $n variables.
    That attempt fails because line 21 shows that it's nil instead the
    string 100.0000 reported by line 15.

    Maybe I should just build an array of the extracted values and use the
    symbols I've created to index the array. In the mean time, mainly
    for my education, I'd like to know where I went wrong with this hash.

    Thanks in Advance,
    Richard
     
    RichardOnRails, Jan 16, 2011
    #1
    1. Advertisements

  2. RichardOnRails

    Josh Cheek Guest

    [Note: parts of this message were removed to make it a legal post.]

    On Sun, Jan 16, 2011 at 5:45 PM, RichardOnRails <
    Hi, Richard. It looks like your problem is that you put your data into your
    hash with `:n_shares=>$2`, on line 17, but you pull it out with `puts
    h[:shares]`, on line 21. When you get in weird situations like that, where
    it seems correct on one line, and incorrect just a few later, give yourself
    a sanity check by breaking it down into as small of steps as possible. I'd
    think "well, if it isn't in there, then wtf does my hash look like?" and
    instead of `puts h[:shares]` I'd stick a `p h` in there, which will print
    out an inspected version of the array. At that point, I'd see the data
    exists, and know the problem must be how I'm pulling it out.

    Also, I don't think `h.each_pair { |n,v| h[n] = v }` does anything, you are
    asking it for each key and value that it has, then telling it to set that
    key to that value. Well, if it gave you that pair, then it is already set.
     
    Josh Cheek, Jan 17, 2011
    #2
    1. Advertisements

  3. Hi Josh,

    Thanks for your reply. I wrote that stupid stuff late last night and
    didn't like what I was composing. I wrote one approach for
    initializing a hash and liked the result well enough, but didn't like
    the copy & paste I did to create it. (I do as much cut and paste as I
    can 'cause I hate to feel like a clerk-typist.)

    So I had two redundant thing coded. And when I looked at it today, I
    forgot what I was doing, so I sent out my SOS.
    That's in keeping with Agile Programming's principle, and I'm
    striving to embrace that. I'm gearing up to actually write my RSpec
    stuff first before coding functionality.
    That's a great point. I should wean myself off writing so much code
    to inspect my code's result and use Ruby's inspect in the form of the
    'p' command. Lesson learned, I hope.

    This is my current idea for succinct code to support further data
    analysis:

    field_names =%w<Name Shares Opened_MDY Closed_MDY Proceeds Cost
    GainLoss>
    h = {}
    (1..field_names.size).each { |name| h[name] = eval($ + i.to_s) }

    Of course, that doesn't work, but despite my perusing of Pragmatic's
    Programming Ruby, 2nd ed., I haven't so far figured out how to code
    that third line. Any ideas. If you're too busy, or whatever, don't
    bother responding and I'll post about it tomorrow.

    Many thanks for your thoughtful help.

    Best wishes,
    Richard
     
    RichardOnRails, Jan 17, 2011
    #3
  4. Hey Josh,

    Please forget about this last question. It is garbled. I'm posting
    it anew.

    Thanks,
    Richard
     
    RichardOnRails, Jan 17, 2011
    #4
  5. i.to_s) }

    I'd use a different approach. When you make the match, you can get an
    array of the captured groups with:

    md.captures

    Then you can zip the two arrays together and use Hash#[] to create the hash=
    :

    For example:

    irb(main):001:0> reg =3D /(.)(.)(.)(.)/
    =3D> /(.)(.)(.)(.)/
    irb(main):005:0> s =3D "1234"
    =3D> "1234"
    irb(main):007:0> field_names =3D %w{a b c d}
    =3D> ["a", "b", "c", "d"]
    irb(main):008:0> md =3D reg.match(s)
    =3D> #<MatchData "1234" 1:"1" 2:"2" 3:"3" 4:"4">
    irb(main):017:0> h =3D Hash[*field_names.zip(md.captures).flatten]
    =3D> {"a"=3D>"1", "b"=3D>"2", "c"=3D>"3", "d"=3D>"4"}


    Jesus.
     
    Jesús Gabriel y Galán, Jan 17, 2011
    #5
  6. If you are on 1.9 there is a better approach: named capture groups.
    These let you use your MatchData object like a Hash:

    Ruby version 1.9.2
    irb(main):001:0> rx =3D /(?'name'\w+)\s*=3D\s*(?'value'\S+)/
    =3D> /(?'name'\w+)\s*=3D\s*(?'value'\S+)/
    irb(main):002:0> md =3D rx.match " foo =3D 123 "
    =3D> #<MatchData "foo =3D 123" name:"foo" value:"123">
    irb(main):003:0> md[:name]
    =3D> "foo"
    irb(main):004:0> md[:value]
    =3D> "123"
    irb(main):005:0> md.names
    =3D> ["name", "value"]
    irb(main):006:0>

    Note, there is an alternative syntax: (?<a name>rx)

    See http://www.geocities.jp/kosako3/oniguruma/doc/RE.txt

    Kind regards

    robert

    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, Jan 19, 2011
    #6
  7. Hi Robert,

    Thanks for your response. I didn't notice your reply because gave up
    on this thread and restarted on
    http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/6ddcea45d88ad810.

    I'm still running 1.8.6 'cause it's worked well for me. Like the song
    advises, "ya gotta dance with the guy that brung you." But I've copy
    your advice to apply it after this project's done, at which time I'll
    upgrade to 1.9.x.

    Again, thanks,
    Richars
     
    RichardOnRails, Jan 20, 2011
    #7
  8. Well, let's see what we can do...

    irb(main):001:0> MDWrap =3D Struct.new :md, :fields do
    irb(main):002:1* def [](name) md[fields[name]] end
    irb(main):003:1> end
    =3D> MDWrap
    irb(main):004:0> md =3D MDWrap.new("foobar".match(/(fo+)(ba\w+)/),
    :name=3D>1,:value=3D>2)
    =3D> #<struct MDWrap md=3D#<MatchData "foobar" 1:"foo" 2:"bar">,
    fields=3D{:value=3D>2, :name=3D>1}>
    irb(main):005:0> md[:name]
    =3D> "foo"

    or

    irb(main):007:0> names =3D {:name =3D> 1, :value =3D> 2}
    =3D> {:value=3D>2, :name=3D>1}
    irb(main):008:0> md =3D "foobar".match(/(fo+)(ba\w+)/)
    =3D> #<MatchData "foobar" 1:"foo" 2:"bar">
    irb(main):009:0> md[names[:name]]
    =3D> "foo"

    Or you use indexes directly.

    :)

    Cheers

    robert

    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, Jan 20, 2011
    #8
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.