how to separate this text into a hash ruby - ruby

sorry my bad english, im new
i have this document.txt
paul gordon,jin kazama,1277,1268,21-12,21-19
yoshimistu,the rock,2020,2092,21-9,21-23,25-27
... lot more
i mean, how to strip each line, and comma sparator, into a hash like this
result = {
line_num: { name1: "paula wood", name2: "sarah carnley", m1: 1277, m2: 1268, sc1: 21, sc2: 12, sc3: 21, sc4: 19 }
}
i try to code like this
im using text2re for regex here
doc = File.read("doc.txt")
lines = doc.split("\n")
counts = 0
example = {}
player1 = '((?:[a-z][a-z]+))(.)((?:[a-z][a-z]+))'
player2 = '((?:[a-z][a-z]+))(.)((?:[a-z][a-z]+))'
re = (player1 + player2 )
m = Regexp.new(re, Regexp::IGNORECASE)
lines.each do |line|
re1='((?:[a-z][a-z]+))' # Word 1
re2='(.)' # Any Single Character 1
re3='((?:[a-z][a-z]+))' # Word 2
re4='(.)' # Any Single Character 2
re5='((?:[a-z][a-z]+))' # Word 3
re6='(.)' # Any Single Character 3
re7='((?:[a-z][a-z]+))' # Word 4
re=(re1+re2+re3+re4+re5+re6+re7)
m=Regexp.new(re,Regexp::IGNORECASE);
if m.match(line)
word1=m.match(line)[1];
c1=m.match(line)[2];
word2=m.match(line)[3];
c2=m.match(line)[4];
word3=m.match(line)[5];
c3=m.match(line)[6];
word4=m.match(line)[7];
counts += 1
example[counts] = word1+word2
puts example
end
end
# (/[a-z].?/)
but the output does not match my expectation
1=>"", 2=>"indahdelika", 3=>"masam",
..more

Your data is comma-separated, so use the CSV class instead of trying to roll your own parser. There are dragons waiting for you if you try to split simply using commas.
I'd use:
require 'csv'
data = "paul gordon,jin kazama,1277,1268,21-12,21-19
yoshimistu,the rock,2020,2092,21-9,21-23,25-27
"
hash = {}
CSV.parse(data).each_with_index do |row, i|
name1, name2, m1, m2, sc1_2, sc3_4 = row
sc1, sc2 = sc1_2.split('-')
sc3, sc4 = sc3_4.split('-')
hash[i] = {
name1: name1,
name2: name2,
m1: m1,
m2: m2,
sc1: sc1,
sc2: sc2,
sc3: sc3,
sc4: sc4,
}
end
Which results in:
hash
# => {0=>
# {:name1=>"paul gordon",
# :name2=>"jin kazama",
# :m1=>"1277",
# :m2=>"1268",
# :sc1=>"21",
# :sc2=>"12",
# :sc3=>"21",
# :sc4=>"19"},
# 1=>
# {:name1=>"yoshimistu",
# :name2=>"the rock",
# :m1=>"2020",
# :m2=>"2092",
# :sc1=>"21",
# :sc2=>"9",
# :sc3=>"21",
# :sc4=>"23"}}
Since you're reading from a file, modify the above a bit using the "Reading from a file a line at a time" example in the documentation.
If the numerics need to be integers, tweak the hash definition to:
hash[i] = {
name1: name1,
name2: name2,
m1: m1.to_i,
m2: m2.to_i,
sc1: sc1.to_i,
sc2: sc2.to_i,
sc3: sc3.to_i,
sc4: sc4.to_i,
}
Which results in:
# => {0=>
# {:name1=>"paul gordon",
# :name2=>"jin kazama",
# :m1=>1277,
# :m2=>1268,
# :sc1=>21,
# :sc2=>12,
# :sc3=>21,
# :sc4=>19},
# 1=>
# {:name1=>"yoshimistu",
# :name2=>"the rock",
# :m1=>2020,
# :m2=>2092,
# :sc1=>21,
# :sc2=>9,
# :sc3=>21,
# :sc4=>23}}
# :sc4=>"23"}}

This is another way you could do it. I have made no assumptions about the number of items per line which are to be the values of :namex, :scx or :mx, or the order of those items.
Code
def hashify(str)
str.lines.each_with_index.with_object({}) { |(s,i),h| h[i] = inner_hash(s) }
end
def inner_hash(s)
n = m = sc = 0
s.split(',').each_with_object({}) do |f,g|
case f
when /[a-zA-Z].*/
g["name#{n += 1}".to_sym] = f
when /\-/
g["sc#{sc += 1}".to_sym], g["sc#{sc += 1}".to_sym] = f.split('-').map(&:to_i)
else
g["m#{m += 1}".to_sym] = f.to_i
end
end
end
Example
str = "paul gordon,jin kazama,1277,1268,21-12,21-19
yoshimistu,the rock,2020,2092,21-9,21-23,25-27"
hashify(str)
#=> {0=>{:name1=>"paul gordon", :name2=>"jin kazama",
# :m1=>1277, :m2=>1268,
# :sc1=>21, :sc2=>12, :sc3=>21, :sc4=>19},
# 1=>{:name1=>"yoshimistu", :name2=>"the rock",
# :m1=>2020, :m2=>2092,
# :sc1=>21, :sc2=>9, :sc3=>21, :sc4=>23, :sc5=>25, :sc6=>27}
# }

Related

How can I capitalize a letter from a word one at a time, then add each instance of the word with a caps letter into a array?

My code:
def wave(str)
ary = []
increase_num = 0
str = str.chars
until increase_num > str.size
ary << str[increase_num].upcase && increase_num += 1
end
end
What it's supposed to do:
wave("hello") => ["Hello", "hEllo", "heLlo", "helLo", "hellO"]
I would really appreciate some help, as you probably know by looking at it I'm relatively new.
str = "hello"
str.size.times.map { |i| str[0,i] << str[i].upcase << str[i+1..] }
#=> ["Hello", "hEllo", "heLlo", "helLo", "hellO"]
I would go about it as follows:
def wave(str)
str = str.downcase # so we can ensure a wave even if the original string had capitalization
str.each_char.with_index.map do |c,idx|
str[0...idx].concat(c.upcase,str[idx.+(1)..-1])
end
end
wave("hello")
#=> ["Hello", "hEllo", "heLlo", "helLo", "hellO"]
str.each_char.with_index.map do |c,idx| - This converts the String into an Enumerator and yields each character and its index to the map block.
str[0...idx] - In the block we slice the string into characters 0 through index (exclusive)
.concat(c.upcase,str[idx.+(1)..-1]) - Then we concatenate that with the current character upcased and the remaining portion of the String (index + 1 through the end of the String)
First 2 passes will look like:
# idx = 0
# c = "h"
# str[0...idx].concat(c.upcase,str[idx.+(1)..-1])
"".concat("H","ello")
# idx = 1
# c = "e"
# str[0...idx].concat(c.upcase,str[idx.+(1)..-1])
"h".concat("E","llo")

How to grep for a pattern in a file and store the content following it?

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
}
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"}
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)
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"}
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.

How to name the symbol in a hash automatically?

I have been making a chess game and I need some help with hashes. Specifically how do I automatically name a hash table symbol using an iterator 'i'
8.times do |i = 0, x = 0|
i += 1
x += 1
pawnHash[:P] = "P#{i}",Pawn.new(x,2,"P#{i}","black")
end
puts pawnHash
the symbol should look like this:
:P1. But is seems impossible to name a hash using the variable 'i'
The full set of 8 symbols should look like this: :P1, :P2, :P3 ... etc.
I tried doing :P + i when declaring the key/value pair, but I got a syntax error due to the '+' sign.
Are you trying to make the key a symbol?
You can do hash["P#{i}".to_sym]
2.0.0-p247 :016 > i = 2
=> 2
2.0.0-p247 :017 > h = {}
=> {}
2.0.0-p247 :018 > h["P#{i}".to_sym] = "value"
=> "value"
2.0.0-p247 :019 > h
=> {:P2=>"value"}
2.0.0-p247 :020 > h.keys.first.class
=> Symbol
Or you can do :"P#{i}"
You can simplify your loop and make it more Ruby-like:
pawn_hash = {}
8.times { |i| pawn_hash["P#{ i + 1 }".to_sym] = "P#{ i + 1}" }
pawn_hash
# => {:P1=>"P1",
# [...]
# :P8=>"P8"}
You could avoid using i + 1 by assigning it to an intermediate variable if you want to play the DRY game:
pawn_hash = {}
8.times do |i|
c = i + 1
pawn_hash["P#{ c }".to_sym] = "P#{ c }"
end
pawn_hash
# => {:P1=>"P1",
# [...]
# :P8=>"P8"}
Or, use a different loop:
pawn_hash = {}
1.upto(8) { |i| pawn_hash["P#{ i }".to_sym] = "P#{ i }" }
pawn_hash
# => {:P1=>"P1",
# [...]
# :P8=>"P8"}
In Ruby we use snake_case, instead of camelCase, for variable and method names. Classes and Modules get camelCase.
Also, meditate on these:
pawn_hash = 8.times.map { |i| ["P#{ i + 1 }".to_sym, "P#{ i + 1}"] }.to_h
# => {:P1=>"P1",
# :P2=>"P2",
# :P3=>"P3",
# :P4=>"P4",
# :P5=>"P5",
# :P6=>"P6",
# :P7=>"P7",
# :P8=>"P8"}
pawn_hash = Hash[8.times.map { |i| ["P#{ i + 1 }".to_sym, "P#{ i + 1}"] }]
# => {:P1=>"P1",
# :P2=>"P2",
# :P3=>"P3",
# :P4=>"P4",
# :P5=>"P5",
# :P6=>"P6",
# :P7=>"P7",
# :P8=>"P8"}
# :P8=>"P8"}
It's not necessary to loop and assign to the hash. Instead, it's very Ruby-like to do it all in one pass. The times method is an iterator. map can iterate over that and will return the block values for each iteration. to_h is a more modern way in Ruby to convert an array to a hash, just as is using Hash[...].

How to write code ruby to collect data while run loop condition

I am quit new in ruby and I need your help.
Now I want to write ruby code to collect some data while looping.
I have 2 code for this work.
My objective is collect sum score from text that split from input file.
-first, run test_dialog.rb
-Second, change input file for this format
from
AA:0.88:320:800|BB:0.82:1040:1330|CC:0.77:1330:1700 enquire-privilege_card
to
AA 0.88
BB 0.82
CC 0.77
-Then use each text that separate check on dialog condition. If this data appear in dialog ,store point until end of text (AA --> BB --> CC)
-Finally get average score.
I have problem will separating and use loop for collect point in same time.
Please help.
Best regard.
PS.
score will return if match with dialog
score of input line 1 should be (0.88+0.82+0.77/3) [match condition 1].
if no match, no score return.
Input data
AA:0.88:320:800|BB:0.82:1040:1330|CC:0.77:1330:1700 enquire-privilege_card
BB:0.88:320:800|EE:0.82:1040:1330|FF:0.77:1330:1700 enquire-privilege_card
EE:0.88:320:800|QQ:0.82:1040:1330|AA:0.77:1330:1700|RR:0.77:1330:1700|TT:0.77:1330:1700 enquire-privilege_card
test_dialog.rb
#!/usr/bin/env ruby
# encoding: UTF-8
#
# Input file:
# hyp(with confidence score), ref_tag
#
# Output:
# hyp, ref_tag, hyp_tag, result
#
require_relative 'dialog'
require_relative 'version'
unless ARGV.length > 0
puts 'Usage: ruby test_dialog.rb FILENAME [FILENAME2...]'
exit(1)
end
counter = Hash.new{|h,k| h[k]=Hash.new{|h2,k2| h2[k2]=Hash.new{|h3,k3| h3[k3]=0}}}
thresholds = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
puts %w(hyp ref_tag hyp_tag result).join("\t")
ARGV.each do |fname|
open(fname, 'r:UTF-8').each do |line|
hyp, ref_tag = line.strip.split(/\t/)
key = if ref_tag == "(reject)"
:reject
else
:accept
end
counter[fname][key][:all] += 1
thresholds.each do |threshold|
hyp_all = get_response_text(hyp, threshold)
hyp_tag = if hyp_all==:reject
"(reject)"
else
hyp_all.split(/,/)[1]
end
result = ref_tag==hyp_tag
counter[fname][key][threshold] += 1 if result
puts [hyp.split('|').map{|t| t.split(':')[0]}.join(' '),
ref_tag, hyp_tag, result].join("\t") if threshold==0.0
end
end
end
STDERR.puts ["Filename", "Result"].concat(thresholds).join("\t")
counter.each do |fname, c|
ca_all = c[:accept].delete(:all)
cr_all = c[:reject].delete(:all)
ca = thresholds.map{|t| c[:accept][t]}.map{|n| ca_all==0 ? "N/A" : '%4.1f' % (n.to_f/ca_all*100) }
cr = thresholds.map{|t| c[:reject][t]}.map{|n| cr_all==0 ? "N/A" : '%4.1f' % (n.to_f/cr_all*100) }
STDERR.puts [fname, "Correct Accept"].concat(ca).join("\t")
STDERR.puts [fname, "Correct Reject"].concat(cr).join("\t")
end
dialog.rb
# -*- coding: utf-8 -*-
#
# text : AA:0.88:320:800|BB:0.82:1040:1330|CC:0.77:1330:1700|DD:0.71:1700:2010|EE:1.00:2070:2390|FF:0.56:320:800|GG:0.12:1330:1700
#
def get_response_text text, threshold, dsr_session_id=nil
# ...
#p "result text >> " + text
# Promotion => detail => rate
# Promotion IR/IDD => high priority than enquire-promotion
# Rate IR/IDD => high priority than enquire-rate
# Problem IR/IDD => high priority than enquire-service_problem
# Internet IR/IDD => high priority than enquire-internet
# Cancel Net => enquire-internet NOT cancel-service
# Lost-Stolen => +Broken
memu = ""
intent = ""
prompt = ""
intent_th = ""
intent_id = ""
# strInput = text.gsub(/\s/,'')
strInput = text.split('|').map{|t| t.split(':')[0]}.join('')
puts ("****strINPUT*****")
puts strInput
scores = text.split('|').map{|t| t.split(':')[1].to_f}
puts ("****SCORE*****")
puts scores
avg_score = scores.inject(0){|a,x| a+=x} / scores.size
puts ("****AVG-Score*****")
puts avg_score
if avg_score < threshold
return :reject
end
# List of Country
country_fname = File.dirname(__FILE__)+"/country_list.txt"
country_list = open(country_fname, "r:UTF-8").readlines.map{|line| line.chomp}
contry_reg = Regexp.union(country_list)
# List of Mobile Type
mobile_fname = File.dirname(__FILE__)+"/mobile_list.txt"
mobile_list = open(mobile_fname, "r:UTF-8").readlines.map{|line| line.chomp}
mobile_reg = Regexp.union(mobile_list)
# List of Carrier
carrier_fname = File.dirname(__FILE__)+"/carrier_list.txt"
carrier_list = open(carrier_fname, "r:UTF-8").readlines.map{|line| line.chomp}
carrier_reg = Regexp.union(carrier_list)
if (strInput =~ /AA|BB/ and strInput =~ /CC/)
intent = "enquire-payment_method"
elsif (strInput =~ /EE/) and ("#{$'}" =~ /QQ|RR/)
intent = "enquire-balance_amount"
elsif (strInput =~ /AA|EE/i) and (strInput =~ /TT/i)
intent = "enquire-balance_unit"
elsif (strInput =~ /DD|BB|/i) and (strInput =~ /FF|AA/i)
intent = "service-balance_amount"
end
Parse as follows:
str = 'AA:0.88:320:800|BB:0.82:1040:1330|CC:0.77:1330:1700 enquire-privilege_card'
str.split( /[:|]/ ).select.with_index {| code, i | i % 4 < 2 ; }.join( ' ' )
# => "AA 0.88 BB 0.82 CC 0.77"

How to merge multiple hashes?

Right now, I'm merging two hashes like this:
department_hash = self.parse_department html
super_saver_hash = self.parse_super_saver html
final_hash = department_hash.merge(super_saver_hash)
Output:
{:department=>{"Pet Supplies"=>{"Birds"=>16281, "Cats"=>245512,
"Dogs"=>513926, "Fish & Aquatic Pets"=>46811, "Horses"=>14805,
"Insects"=>364, "Reptiles & Amphibians"=>5816, "Small
Animals"=>19769}}, :super_saver=>{"Free Super Saver
Shipping"=>126649}}
But now I want to merge more in the future. For example:
department_hash = self.parse_department html
super_saver_hash = self.parse_super_saver html
categories_hash = self.parse_categories html
How to merge multiple hashes?
How about:
[department_hash, super_saver_hash, categories_hash].reduce &:merge
You can just call merge again:
h1 = {foo: :bar}
h2 = {baz: :qux}
h3 = {quux: :garply}
h1.merge(h2).merge(h3)
#=> {:foo=>:bar, :baz=>:qux, :quux=>:garply}
You can do below way using Enumerable#inject:
h = {}
arr = [{:a=>"b"},{"c" => 2},{:a=>4,"c"=>"Hi"}]
arr.inject(h,:update)
# => {:a=>4, "c"=>"Hi"}
arr.inject(:update)
# => {:a=>4, "c"=>"Hi"}
It took me a while to figure out how to merge multi-nested hashes after going through this Question and its Answers. It turned out I was iterating through the collections of hashes incorrectly, causing all kinds of problems with null values.
This sample command-line app shows how to merge multiple hashes with a combination of store and merge!, depending on whether or not they were top-level hash keys. It uses command-line args with a few known key name for categorization purposes.
Full code from the Gist URL is provided below as a courtesy:
# Ruby - A nested hash example
# Load each pair of args on the command-line as a key-value pair
# For example from CMD.exe:
# call ruby.exe ruby_nested_hash_example.rb Age 30 Name Mary Fav_Hobby Ataraxia Fav_Number 42
# Output would be:
# {
# "data_info": {
# "types": {
# "nums": {
# "Age": 30,
# "Fav_Number": 42
# },
# "strings": {
# "Name": "Mary",
# "Fav_Hobby": "Ataraxia"
# }
# },
# "data_id": "13435436457"
# }
# }
if (ARGV.count % 2 != 0) || (ARGV.count < 2)
STDERR.puts "You must provide an even amount of command-line args to make key-value pairs.\n"
abort
end
require 'json'
cmd_hashes = {}
nums = {}
strings = {}
types = {}
#FYI `tl` == top-level
all_tl_keys = {}
data_info = {}
data_id = {:data_id => "13435436457"}
_key = ""
_value = ""
element = 0
ARGV.each do |i|
if element % 2 == 0
_key=i
else
if (i.to_i!=0) && (i!=0)
_value=i.to_i
else
_value=i
end
end
if (_key != "") && (_value != "")
cmd_hashes.store(_key, _value)
_key = ""
_value = ""
end
element+=1
end
cmd_hashes.each do |key, value|
if value.is_a? Numeric
nums.store(key, value)
else
strings.store(key, value)
end
end
if nums.size > 0; types.merge!(:nums => nums) end
if strings.size > 0; types.merge!(:strings => strings) end
if types.size > 0; all_tl_keys.merge!(:types => types) end
if data_id.size > 0; all_tl_keys.merge!(data_id) end
if all_tl_keys.size > 0; data_info.merge!(:data_info => all_tl_keys) end
if data_info.size > 0; puts JSON.pretty_generate(data_info) end
Suppose you are having arr = [{x: 10},{y: 20},{z: 30}]
then do
arr.reduce(:merge)

Resources