0

My file content is

blablabla    
Name  : 'XYZ'  
Age   : '30'  
Place : 'ABCD'    
blablabla  

How can I grep for "Name", "Age", "Place" and store name "XYZ", age "30" and place "ABCD" in a hash?

What should be the '?' in this code to get those?

data = {}
name = /Name/
age = /Age/
place = /Place/
read_lines(file) { |l|
  case l
    when name
      data[:name] = ?
    when age
      data[:age] = ?
    when place
      data[:place]= ?
  end
}
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Navaneeth Cp
  • 31
  • 3
  • 9

4 Answers4

0

You can use something like this.

data = {}
keys = {:name => "Name", :age => "Age", :place => "Place"}

File.open("test.txt", "r") do |f|
  f.each_line do |line|
    line.chomp!
    keys.each do |hash_key, string|
      if line[/#{string}/]
        data[hash_key] = line.strip.split(" : ")[-1].gsub("'", "")
        break
      end
    end
  end
end

output

p data
# => {:name=>"XYZ", :age=>"30", :place=>"ABCD"}
Lukas Baliak
  • 2,849
  • 2
  • 23
  • 26
0

Strange code, but in this case:

  data[:name] = l.split(':')[1] if l.match(name)
when age
  data[:age] = l.split(':')[1] if l.match(age)
when place
  data[:place]= l.split(':')[1] if l.match(place)

Are you interested in refactoring?

One option is to:

mapping =
    [
        { name: :name, pattern: /Name/ },
        { name: :age, pattern: /Age/ },
        { name: :place, pattern: /Place/ }
    ]
data = str.split(/\r?\n|\r/).map do |line|
  mapping.map{|pair|
    { pair[:name] => line.split(' : ')[1].gsub("'", "") } if line.match(pair[:pattern])
  }.compact.reduce({}, :merge)
end.reduce({}, :merge)
Mike S
  • 324
  • 1
  • 10
0

Suppose we first read the file into a string:

str = File.read('fname')

which is:

str =<<_
blablabla
Name : 'XYZ'
Age : '30'
Place : 'ABCD'
blablabla
_
  #=> "blablabla\nName : 'XYZ'\nAge : '30'\nPlace : 'ABCD'\nblablabla\n"

Then use the regex

r = /
    ^                    # match beginning of line 
    Name\s*:\s*'(.*)'\n  # match 'Name`, ':' possibly surrounded by spaces, any number
                         # of any character in capture group 1, end of line
    Age\s*:\s*'(.*)'\n   # match 'Age`, ':' possibly surrounded by spaces, any number
                         # of any character in capture group 2, end of line
    Place\s*:\s*'(.*)'\n # match 'Place`, ':' possibly surrounded by spaces, any number
                         # of any character in capture group 3, end of line
    /x                   # free-spacing regex definition mode

with String#scan to form the hash:

[:name, :age, :place].zip(str.scan(r).first).to_h
  #=> {:name=>"XYZ", :age=>"30", :place=>"ABCD"} 
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
0

I'd do something like this:

str = <<EOT
blablabla    
Name  : 'XYZ'  
Age   : '30'  
Place : 'ABCD'    
blablabla  
EOT

str.scan(/(Name|Age|Place)\s+:\s'([^']+)/).to_h  # => {"Name"=>"XYZ", "Age"=>"30", "Place"=>"ABCD"}

scan will create sub-arrays if it sees pattern groups in the regular expression. Those make it easy to turn the returned array of arrays into a hash.

If you need to fold the keys to lower-case, or convert them to symbols:

str.scan(/(Name|Age|Place)\s+:\s'([^']+)/)
  .map{ |k, v| [k.downcase, v] } # => [["name", "XYZ"], ["age", "30"], ["place", "ABCD"]]
  .to_h  # => {"name"=>"XYZ", "age"=>"30", "place"=>"ABCD"}

Or:

str.scan(/(Name|Age|Place)\s+:\s'([^']+)/)
  .map{ |k, v| [k.downcase.to_sym, v] } # => [[:name, "XYZ"], [:age, "30"], [:place, "ABCD"]]
  .to_h  # => {:name=>"XYZ", :age=>"30", :place=>"ABCD"}

Or some variation on:

str.scan(/(Name|Age|Place)\s+:\s'([^']+)/)
   .each_with_object({}){ |(k,v), h| h[k.downcase.to_sym] = v}
# => {:name=>"XYZ", :age=>"30", :place=>"ABCD"}

If the example string truly is the complete file, and there won't be any other reoccurrence of the key/value pairs, then this will work. If there could be more than one then the resulting hash will not be correct because the subsequent pairs will stomp on the first one. If the file is as you said, then it'll work fine.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303