Ruby "storing" format string - ruby

How can I store a format string like this
s = "test with #{value}"
so that later on I can do this
puts s % {:value => 'hello'}
If I write the first thing, it complains that value is not found (true, I want to provide it later). If I use the raw string s = 'test with #{value}' it is not interpolated.
I specifically tried this:
#format_html = "%{who} receives %{got[1]} from %{from} and sends %{given[1]} to %{to}"
puts #format_html % {:who => 'who',
:given => 'given',
:from => 'from',
:got => 'got',
:to => 'to'}
and I get this:
KeyError (key{who.sub ' ', '+'} not found):

This works only with ruby 1.9+:
s = "test with %{value}"
puts s % { value: 'hello' } # => test with hello

The pickaxe http://pragprog.com/book/ruby3/programming-ruby-1-9 says under String#%:
If the format specification contains more than one substitution, then arg must be an Array containing the values to be substituted.
#format_html = "%s receives %s from %s and sends %s to %s"
h = {:who => 'who',
:given => ['given1', 'given2'],
:from => 'from ',
:got => ['got1', 'got2'],
:to => 'to '}
who, given, from, got, to = h.values
who_plus = who.gsub(' ', '+')
got0 = got[0]
got1 = got[1]
from_plus = from.gsub(' ', '+')
given0 = given[0]
given1 = given[1]
to_plus = to.gsub(' ', '+')
puts #format_html % [who_plus, who, got0, got1, from_plus, from, given0, given1, to_plus, to]
Execution :
$ ruby -w t.rb
who receives got2 from from and sends given2 to to

Related

How to return to the calling method?

I have a program that uses a method for verification, if that verification failed I would like to return to the method it was called from, for example:
def obtain_pokemon_name
print 'Enter Pokemon: '
pokemon = gets.chomp.capitalize
obtain_basic_attack(pokemon)
end
def obtain_basic_attack(poke)
print 'Enter basic attack: '
basic_attack = gets.chomp.downcase
check_attacks(poke, basic_attack)
obtain_spec_attack(poke)
end
def obtain_spec_attack(poke)
print 'Enter special attack: '
spec_attack = gets.chomp.downcase
check_attacks(poke, spec_attack)
end
def check_attacks(pokemon, attack)
if POKEMON_ATTACKS[pokemon][attack] == nil
puts "#{attack} is not one of #{pokemon}'s attacks, try again.."
return # to where this function was called
else
attack
end
end
begin
obtain_pokemon_name
rescue => e
puts "Failed with error code: #{e}"
end
When this is run:
Enter Pokemon: arbok
Enter basic attack: eat
eat is not one of Arbok's attacks, try again..
Enter special attack: test
test is not one of Arbok's attacks, try again..
Attack list:
POKEMON_ATTACKS = {
'Bulbasaur' => {'tackle' => 10.9, 'vine whip' => 15.4, 'power whip' => 21.4, 'seed bomb' => 12.5, 'sludge bomb' => 19.2},
'Ivysaur' => {'razor leaf' => 10.3, 'vine whip' => 15.4, 'power whip' => 21.4, 'sludge bomb' => 19.2, 'solar beam' => 13.3},
'Kakuna' => {'bug bite' => 13.3, 'poison sting' => 10.3, 'struggle' => 8.8},
'Beedrill' => {'bug bite' => 13.3, 'poison jab' => 14.3, 'aerial ace' => 8.6, 'sludge bomb' => 19.2, 'x-scissor' => 14.3},
'Pidgey' => {'quick attack' => 7.5, 'tackle' => 10.9, 'aerial ace' => 8.6, 'air cutter' => 7.6, 'twister' => 5.6},
'Ekans' => {'acid' => 9.5, 'poison sting' => 10.3, 'gunk shot' => 20.0, 'sludge bomb' => 19.2, 'wrap' => 3.8},
'Arbok' => {'acid' => 9.5, 'bite' => 12.0, 'dark pulse' => 12.9, 'gunk shot' => 20.0, 'sludge wave' => 17.6},
}
So my question is, if the attack is not present in the data, how can I return back to the calling method? So for instance if I call arbok and his attack is tackle if it doesn't exist in the hash, how would I return to the obtain_basic_attack(poke) method?
RIght here:
puts "#{attack} is not one of #{pokemon}'s attacks, try again.."
return # to where this function was called
you should call the original method again. i.e.
if POKEMON_ATTACKS[pokemon][attack] == nil
puts "#{attack} is not one of #{pokemon}'s attacks, try again.."
return obtain_spec_attack(poke)
You could alternatively add this logic to obtain_spec_attack:
def obtain_spec_attack(poke)
loop do
print 'Enter special attack: '
spec_attack = gets.chomp.downcase
attack_found = check_attacks(poke, spec_attack)
if attack_found
break attack_found # this will return attack_found from the loop
else
puts "attack not found"
end
end
end
edit
looking at your question again, I realize you want to return to a method multiple levels up. You could use the approaches I've already outlined, or alternatively use rescue:
def obtain_basic_attack(poke)
begin
print 'Enter basic attack: '
basic_attack = gets.chomp.downcase
check_attacks(poke, basic_attack)
obtain_spec_attack(poke)
rescue AttackNotFoundError
retry # runs the 'begin' block again
end
end
def obtain_spec_attack(poke)
print 'Enter special attack: '
spec_attack = gets.chomp.downcase
check_attacks(poke, spec_attack)
end
def check_attacks(pokemon, attack)
if POKEMON_ATTACKS[pokemon][attack] == nil
puts "#{attack} is not one of #{pokemon}'s attacks, try again.."
raise AttackNotFoundError
else
attack
end
end
In order to use a custom error like AttackNotFoundError, you need to define the error class somewhere:
class AttackNotFoundError < StandardError; end
You could use any error really, such as raise StandardError, but it's better to restrict what errors you're rescuing so that you don't accidentally rescue an unrelated error.

How can I shrink and make a better use of this method? - RUBY (raw)

Working on a project and trying to turn this method (I Have some more similar methods like that in my project) into a more dynamic and concise way
Data from image
def proficiency_parser(stored_data, name, race, year, title, percentage)
if stored_data.has_key?(name)
if stored_data[name].has_key?(race)
if stored_data[name][race].has_key?(year)
stored_data[name][race][year][title] = percentage
else
stored_data[name][race][year] = {title => percentage}
end
else
stored_data[name][race] = {year => {title => percentage}}
end
else
stored_data[name] = {race => {year => {title => percentage}}}
end
end
so essentially this method through my data to identify whether it meets so of those specification showing in the code, essentially I just don't want to use this amount of "elses" and "Ifs" if at all possible.
Data
stored_data
# => {"COLORADO"=>{3=>{2008=>{:math=>0.697}}}}
name
# => "COLORADO"
race
# => 3
year
# => 2008
title
# => :math
percentage
# => 0.697
Take a look at Hash#dig which is included in Ruby versions 2.3.0 or newer.
To summarize:
hash_1 = { a: { a: { a: "b" } } }
hash_2 = { c: { c: { c: "d" } } }
hash_1.dig(:a, :a, :a) # returns "b"
hash_2.dig(:a, :a, :a) # returns nil
So you could say if hash_1.dig(:a, :a) instead of
if hash_1[:a]
if hash_1[:a][:a]
# etc
There's also another way to do it, which is to rescue your NoMethod [] errors.
Here's an example of that:
if hash_1[:a][:a][:a] rescue false
puts "the key exists"
else
puts "the key doesnt exist"
end
You can use some recursive call
Input data
stored_data = {}
name = 'COLORADO'
race = 3
year = 2008
title = :math
percentage = 0.697
Methods
def proficiency_parser(stored_data, name, race, year, title, percentage)
parser(stored_data, name, {race => {year => {title => percentage}}})
end
def parser(data, key, value)
data[key] ? value.each { |k, v| parser(data[key], k, v) } : data[key] = value
end
call
proficiency_parser(stored_data, name, race, year, title, percentage)
p stored_data
# => {"COLORADO"=>{3=>{2008=>{:math=>0.697}}}}
I hope this helps

output to csv in a similar manner 'print' outputs to terminal?

the logic included in my ruby code is a little difficult to translate into a csv output, is there a way to append each value in a similar manner that print works when outputting to the terminal?
cop_log is a hash of thousands of records
cop_log = { 'A1' => 'val1', 'B1' => 'val2', 'C1' => 'val3', 'D1' => 'val4, 'E1' => 'val5', 'F1' => 'val6', 'G1' => 'val7', 'H1' => 'val8', 'I1' => 'val9', 'J1' => 'val10, 'K1' => 'val11', 'A2' => 'val12', 'B2' => 'val13', 'C2' => 'val14', 'D2' => 'val15, 'E2' => 'val16', 'F2' => 'val17', 'G2' => 'val18', 'H2' => 'val19', 'I2' => 'val20', 'J2' => 'val21, 'K2' => 'val22'}
cop_log.each do |key, value|
if key.include?('K')
print "#{value}\n"
elsif key.include?('A')
print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif key.include?('B') || key.include?('C') || key.include?('D') ||
key.include?('E') || key.include?('F') || key.include?('G') ||
key.include?('H') || key.include?('I') || key.include?('J')
print "#{value},"
end
end
currently outputs in the terminal like this, i want it to print to a csv in the same way:
val1, val2, val3, val4, val5, val6, val7, val8, val9, val10, val11
val12, val13, val14, val15, val16, val17, val18, val19, val10, val21, val22
reading the documentation it looks like the common route is to do the following,
CSV.open("path/to/file.csv", "wb") do |csv|
csv << ["row", "of", "CSV", "data"]
csv << ["another", "row"]
# ...
end
unfortunately the structure of my code is going to make that a little difficult....
This is very confusing and, if cleaned up, would help make your code much more readable and maintainable:
if key.include?('K')
print "#{value}\n"
elsif key.include?('A')
print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif key.include?('B') || key.include?('C') || key.include?('D') ||
key.include?('E') || key.include?('F') || key.include?('G') ||
key.include?('H') || key.include?('I') || key.include?('J')
print "#{value},"
end
What is key? A string of characters or a single character? If you're matching single characters you could do something like:
if key == 'K'
print "#{value}\n"
elsif key == 'A'
print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif ('B'.. 'J').include?(key)
elsif
print "#{value},"
end
and even convert:
elsif ('B'.. 'J').include?(key)
to:
elsif ('B'..'J') === key
If you're searching for a character match inside strings:
if key['K']
print "#{value}\n"
elsif key['A']
print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif key[/[B-J]/]
print "#{value},"
end
At this point you're ready to tackle cleaning up the CSV output. I'd recommend starting over using one of the basic examples of CSV output in the class documentation. I'd also recommend carefully reading "Comma-separated values" so you're more familiar with the expectations of a CSV formatted file.
(Note: At this point the input sample hash was added to the question showing the actual format of the keys.)
I'll let you figure out how to incorporate CSV, but meditate on this:
cop_log = { 'A1' => 'val1', 'B1' => 'val2', 'C1' => 'val3', 'D1' => 'val4', 'E1' => 'val5', 'F1' => 'val6', 'G1' => 'val7', 'H1' => 'val8', 'I1' => 'val9', 'J1' => 'val10', 'K1' => 'val11', 'A2' => 'val12', 'B2' => 'val13', 'C2' => 'val14', 'D2' => 'val15', 'E2' => 'val16', 'F2' => 'val17', 'G2' => 'val18', 'H2' => 'val19', 'I2' => 'val20', 'J2' => 'val21', 'K2' => 'val22'}
cop_log.values.each_slice(11) do |slice|
puts slice.join(',')
end
# >> val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11
# >> val12,val13,val14,val15,val16,val17,val18,val19,val20,val21,val22
You need to rely on the CSV class to create CSV files; The format isn't as simple as it seems.
Here's a first pass of how I'd write the code:
require 'csv'
cop_log = { 'A1' => 'val1', 'B1' => 'val2', 'C1' => 'val3', 'D1' => 'val4', 'E1' => 'val5', 'F1' => 'val6', 'G1' => 'val7', 'H1' => 'val8', 'I1' => 'val9', 'J1' => 'val10', 'K1' => 'val11', 'A2' => 'val12', 'B2' => 'val13', 'C2' => 'val14', 'D2' => 'val15', 'E2' => 'val16', 'F2' => 'val17', 'G2' => 'val18', 'H2' => 'val19', 'I2' => 'val20', 'J2' => 'val21', 'K2' => 'val22'}
job_number = 1
pm_lookup = [0, 1]
CSV.open('./foo.csv', 'w') do |csv|
cop_log.group_by{ |k, v| k[1] }.values.each do |row|
csv << [ job_number, pm_lookup[job_number], *row.map{ |k, v| v } ]
end
end
After running, foo.csv contains:
1,1,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11
1,1,val12,val13,val14,val15,val16,val17,val18,val19,val20,val21,val22
We don't know what your expected output is, since you didn't tell us, but you could work from that code and figure it out.
Here's a break-down of the preprocessing of the data:
cop_log.group_by{ |k, v| k[1] } # => {"1"=>[["A1", "val1"], ["B1", "val2"], ["C1", "val3"], ["D1", "val4"], ["E1", "val5"], ["F1", "val6"], ["G1", "val7"], ["H1", "val8"], ["I1", "val9"], ["J1", "val10"], ["K1", "val11"]], "2"=>[["A2", "val12"], ["B2", "val13"], ["C2", "val14"], ["D2", "val15"], ["E2", "val16"], ["F2", "val17"], ["G2", "val18"], ["H2", "val19"], ["I2", "val20"], ["J2", "val21"], ["K2", "val22"]]}
.values # => [[["A1", "val1"], ["B1", "val2"], ["C1", "val3"], ["D1", "val4"], ["E1", "val5"], ["F1", "val6"], ["G1", "val7"], ["H1", "val8"], ["I1", "val9"], ["J1", "val10"], ["K1", "val11"]], [["A2", "val12"], ["B2", "val13"], ["C2", "val14"], ["D2", "val15"], ["E2", "val16"], ["F2", "val17"], ["G2", "val18"], ["H2", "val19"], ["I2", "val20"], ["J2", "val21"], ["K2", "val22"]]]
In the code above I'm making sure the input is in an expected order, in this case I'm grouping by single-digit rows. More robust code would use \d+ instead of 1 and would also sort to force the appropriate order.
This is really important any time you're massaging data from one format into another for later reuse. While current Rubies guarantee that a hash will maintain its insertion order, it's not a good practice to assume since old versions of Ruby didn't do that, and other languages you port the code to might not maintain order. Instead always program defensively, making sure you're going to return consistent results. To what extent you do that is something you'll learn when you find out a previous attempt wasn't enough. Programming is fun that way.
Finally, any time your code feels awkward or isn't flowing it's time to back up and look at what you're doing.
Couldn't you do this?
CSV.open("path/to/file.csv", "wb") do |csv|
cop_log.each do |key, value|
if key.include?('A')
csv << [ job_number, pm_lookup[job_number], value ]
elsif key.include?('K') || key.include?('B') || key.include?('C') || key.include?('D') ||
key.include?('E') || key.include?('F') || key.include?('G') ||
key.include?('H') || key.include?('I') || key.include?('J')
csv << [ value ]
end
end
end
def write_csv(cop_log, io = $stdout)
cop_log.each do |key, value|
if key.include?('K')
io.print "#{value}\n"
elsif key.include?('A')
io.print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif key.include?('B') || key.include?('C') || key.include?('D') ||
key.include?('E') || key.include?('F') || key.include?('G') ||
key.include?('H') || key.include?('I') || key.include?('J')
io.print "#{value},"
end
end
end
Then
File.open('path/to/file.csv', 'w') do |f|
write_csv(cop_log, f)
end
re fector the code
CSV.open("test.csv", "ab") do |csv|
b = {}
check_num = 0
cop_log.each do |key, value|
num = key.gsub(/[^\d]/, '').to_i
next if num < 8 ||
key.include?('L') || key.include?('M') || key.include?('N') ||
key.include?('O') || key.include?('P') || key.include?('Q') ||
key.include?('R') || key.include?('S') || key.include?('T') ||
key.include?('U') || key.include?('V')
a = { key => value }
b.merge!(a)
end # end of each loop
i = 8
while ((b.length / 9) - 7) > i do
csv << [ job_number, pm_lookup[job_number], b["A#{i}"], b["B#{i}"],
b["C#{i}"], b["D#{i}"], b["E#{i}"], b["F#{i}"], b["G#{i}"],
b["H#{i}"], b["I#{i}"], b["J#{i}"], b["K#{i}"] ]
i += 1
end
end # end of CSV.open

Tring to Remove Comma Before or During Parsing CSV in ruby

I am importing a CSV File that is being Parsed and i have the seperators set to "|" i would like to remove the comma's or comment them so they do not mess up colums
Here is the part that i think should have a code to remove the ,'s
namespace :postonce do
desc "Check postonce ftp files and post loads and trucks."
task :post => :environment do
files = %x[ls /home/web2_postonce/].split("\n")
files.each do |file|
%x[ iconv -t UTF-8 /home/web2_postonce/#{file} > /home/deployer/postonce/#{file} ]
%x[ mv /home/web2_postonce/#{file} /home/deployer/postonce_backup/ ]
end
files = %x[ ls /home/deployer/postonce/ ].split("\n")
files.each do |file|
begin
lines = CSV.read("/home/deployer/postonce/#{file}")
rescue Exception => e
log.error e
next
end
h = lines.shift
header = CSV.parse_line(h[0], { :col_sep => "|" } )
lines.each do |line|
fields = CSV.parse_line(line[0],{:col_sep => "|"})
post = Hash[header.zip fields]
if post["EmailAddress"].blank?
log.error "Blank Email #{post["EmailAddress"]}"
else
log.debug "Email #{post["EmailAddress"]}"
end
Here is the the full code that pulls the file and parses the file into colums
require 'resque'
require 'logger'
log = Logger.new("#{Rails.root}/log/PostOnce.log")
log.datetime_format = "%F %T"
namespace :postonce do
desc "Check postonce ftp files and post loads and trucks."
task :post => :environment do
files = %x[ls /home/web2_postonce/].split("\n")
files.each do |file|
%x[ iconv -t UTF-8 /home/web2_postonce/#{file} > /home/deployer/postonce/#{file} ]
%x[ mv /home/web2_postonce/#{file} /home/deployer/postonce_backup/ ]
end
files = %x[ ls /home/deployer/postonce/ ].split("\n")
files.each do |file|
begin
lines = CSV.read("/home/deployer/postonce/#{file}")
rescue Exception => e
log.error e
next
end
h = lines.shift
header = CSV.parse_line(h[0], { :col_sep => "|" } )
lines.each do |line|
fields = CSV.parse_line(line[0],{:col_sep => "|"})
post = Hash[header.zip fields]
if post["EmailAddress"].blank?
log.error "Blank Email #{post["EmailAddress"]}"
else
log.error "Email #{post["EmailAddress"]}"
end
if post["Notes"].blank?
post["Notes"] = "~PostOnce~"
else
post["Notes"] = post["Notes"]+" ~PostOnce~"
end
if Company.where(:name => post["Company"]).first.nil?
c = Company.new
c.name = post["Company"]
c.dispatch = post["Customer_Phone"]
c.save
end
if User.where(:email => ["EmailAddress"]).first.blank?
u = User.new
c = Company.where(:name => post["Company"]).first unless Company.where(:name => post["Company"]).first.nil?
u.company_id = c.id
u.username = post["EmailAddress"].gsub(/#.*/,"") unless post["EmailAddress"].nil?
u.password = Time.now.to_s
u.email = post["EmailAddress"]
u.dispatch = post["Customer_Phone"]
u.save
end
#If Load
if file.start_with?("PO_loads")
record = Hash.new
begin
record[:user_id] = User.where(:email => post["EmailAddress"]).first.id
rescue Exception => e
log.error e
next
end
record[:origin] = "#{post["Starting_City"]}, #{post["Starting_State"]}"
record[:dest] = "#{post["Destination_City"]}, #{post["Destination_State"]}"
record[:pickup] = Time.parse(post["Pickup_Date_Time"])
record[:ltl] = false
record[:ltl] = true unless post["#Load_Type_Full"] = "FULL"
begin
record[:equipment_id] = Equipment.where(:code => post["Type_of_Equipment"]).first.id
rescue Exception => e
record[:equipment_id] = 34
end
record[:comments] = post["Notes"]
record[:weight] = post["Weight"]
record[:length] = post["Length"]
record[:rate] = post["Payment_amount"]
record[:rate] = '' if post["Payment_amount"] == 'Call' or post["Payment_amount"] == 'CALL'
Resque.enqueue(MajorPoster, record)
#If Truck
elsif file.start_with?("PO_trucks")
record = Hash.new
begin
record[:user_id] = User.where(:email => post["EmailAddress"]).first.id
rescue Exception => e
log.error e
next
end
record[:origin] = "#{post["Starting_City"]}, #{post["Starting_State"]}"
record[:dest] = "#{post["Destination_City"]}, #{post["Destination_State"]}"
record[:available] = Time.parse(post["Pickup_Date_Time"])
record[:expiration] = record[:available] + 8.days
begin
record[:equipment_id] = Equipment.where(:code => post["Type_of_Equipment"]).first.id
rescue Exception => e
record[:equipment_id] = 34
end
record[:comments] = post["Notes"]
Resque.enqueue(MajorPoster, record)
end
end
# %x[rm /home/deployer/postonce/#{file}]
end
end
end
here is a sample of data that i am tring to load up the commas come in Customer_Contact and in Notes this data comes to us thru FTP
Member_ID|Action_type|Entry_Number|Pickup_Date_Time|Starting_City|Starting_State|Destination_City|Destination_State|Type_of_Equipment|Length|Quantity|#Load_type_full|Extra_Stops|Payment_amount|Weight|Distance|Notes|Customer_Phone|Extension|Customer_Contact|EmailAddress|Company|
SUMMIT|L-delete|16491978|20140213|PEWAMO|MI|DENVER|CO|FT|45|1|FULL|0|Call|46000|||866-807-4968||DISPATCH, Dispatch|IANP#SUMMITTRANS.NET|SUMMIT TRANSPORTATION SERVICES INC.|
SUMMIT|L-delete|16490693|20140213|PEWAMO|MI|DENVER|CO|V|48|1|FULL|0|Call|44000|||866-807-4968||DISPATCH|IANP#SUMMITTRANS.NET|SUMMIT TRANSPORTATION SERVICES INC.|
SUMMIT|L-delete|16490699|20140214|PEWAMO|MI|DENVER|CO|V|48|1|FULL|0|Call|44000|||866-807-4968||DISPATCH|IANP#SUMMITTRANS.NET|SUMMIT TRANSPORTATION SERVICES INC.|
megacorpwv|L-Delete|16491928|20140214|WAITE PARK|MN|DOLTON|IL|R||1|FULL|0|CALL|0|0|(859) 538-1660 x2007|877-670-2837|||snewman#megacorplogistics.com|MEGACORP LOGISTICS 03|
My log shows this: As you see I manually put a comma in one field on the first record and it acted as a seperator
2014-02-13 12:29:41 ERROR -- Blank Email
2014-02-13 12:29:41 ERROR -- undefined method `id' for nil:NilClass
2014-02-13 12:29:41 DEBUG -- Email IANP#SUMMITTRANS.NET
2014-02-13 12:29:42 DEBUG -- Email IANP#SUMMITTRANS.NET
2014-02-13 12:29:42 DEBUG -- Email snewman#megacorplogistics.com
I think your problem is that you're only parsing the first element of the array "h" and "line". Try removing the "[0]" from those two lines. It's not that the email is blank it's that everything except Member_ID is blank.
header = CSV.parse_line(h, { :col_sep => "|" } )
lines.each do |line|
fields = CSV.parse_line(line,{:col_sep => "|"})
Ah. OK. Phillip Hallstrom has identified the problem. It's in the CSV.read statement. By default CSV.read will attempt to delimited by comma ",". What CSV.read is attempting to do is read each line as an array element and then parse each line into another array. Therefore, if your file looks like this:
a|b|c|d|e
apple|ball, bearing|cantelope|date|elephant
It will return the following array on CSV.read
[["a|b|c|d|e"], ["apple|ball", " bearing|cantelope|date|elephant"]]
You can see that CSV.read is attempting to do the full parse before you get the opportunity to specify a delimiter.
Either read the lines in using normal file I/O or recode to specify the delimiter in the CSV.read statement

How to read characters from a text file, then store them into a hash in Ruby

I am working on an assignment, and can't figure it out. We have to first parse a text file, and then feed the results into a hash. I have done this:
code = File.open(WORKING_DIR + '/code.txt','r')
char_count = {'a' => 0,'b' => 0,'c' => 0,'d' => 0,'e' => 0,'f' => 0,'g' => 0,'h' => 0,'i' => 0,
'j' => 0,'k' => 0,'l' => 0,'m' => 0,'n' => 0,'o' => 0,'p' => 0,'q' => 0,'r' => 0,
's' => 0,'t' => 0,'u' => 0,'v' => 0,'w' => 0,'x' => 0,'y' => 0,'z' => 0
}
# Step through each line in the file.
code.readlines.each do |line|
# Print each character of this particular line.
line.split('').each do
|ch|
char_count.has_key?('ch')
char_count['ch'] +=1
end
My line of thinking: open the file to a variable named code
read the individual lines
break the lines into each character.
I know this works, I can puts out the characters to screen.
Now I need to feed the characters into the hash, and it isn't working. I am struggling with the syntax (at least) and basic concepts (at most). I only want the alphabet characters, not the punctuation, etc. from the file.
Any help would be greatly appreciated.
Thanks.
I would directly do :
File.open(WORKING_DIR + '/code.txt','r') do |f|
char_count = Hash.new(0) # create a hash where 0 is the default value
f.each_char do |c| # iterate on each character
... # some filter on the character you want to reject.
char_count[c] +=1
end
end
PS : you wrote 'ch' the string instead of ch the variable name
EDIT : the filter could be
f.each_char do |c| # iterate on each character
next if c ~= \/W\ # exclude with a regexp non word character
....
Try this, using Enumerable class methods:
open("file").each_char.grep(/\w/).group_by { |char|
char
}.each { |char,num|
p [char, num.count]
}
(The grep method filter is using regex "\w" (any character, digit ou underscore); you can change to [A-Za-z] for filter only alphabets.)
I think the problem is here:
char_count.has_key?('ch')
char_count['ch'] +=1
end
You're not using the variable but a string 'ch', change that in both places for ch.
Also the hash could be created using range, for example:
char_count = {}
('a'..'z').each{|l| char_count[l] = 0}
or:
char_count = ('a'..'z').inject({}){|hash,l| hash[l] = 0 ; hash}

Resources