I've think I've created multiple arrays from strings but if I try to inspect the array I receive an error.
File.open("livestock.txt", "r") do |file|
file.readlines.each do |x|
if x.match(/:*:/)
# puts x.inspect
# strip string
x.gsub!(/[^A-Za-z]/, '')
x.downcase!
puts x.inspect
x = Array.new(){Hash.new}
# puts x.inspect
pigs.inspect
else
# puts "no"
end
end
end
animals.rb:12:in `block (2 levels) in <main>': undefined local variable or method `pigs' for main:Object (NameError)
from animals.rb:2:in `each'
from animals.rb:2:in `block in <main>'
from animals.rb:1:in `open'
from animals.rb:1:in `<main>'
Ideally I want to create pigs =[] then add hashes to this array such as:
pigs = [{"name"=>"peggy", "id"=>1, "owner"=>"wolcott farms"},
{"name"=>"sue", "id"=>2, "owner"=>"blue moon farms"},
{"name"=>"eddie", "id"=>3, "owner"=>"sunrise farms"}
]
and the same for cows, etc.
my text file animals.txt is
::pigs::
name, id, owner
peggy, 1, wolcott farms
sue, 2, blue moon farms
eddie, 3, sunrise farms
::cows::
name, id, owner
dee, 3, black hat farms
sunny, 2, blue moon farms
bess, 4, wolcott farms
Since Ruby v1.8 it has not been possible to create local variables. Instance variables, yes (using Object#instance_variable_set ), local variables, no.
@CodeGnome has given one way to create instance variables with given names. I don't think a collection of instance variables is the appropriate data structure however. I would suggest you consider using a hash instead. You could do that as follows.
Code
def doit(fname)
File.read(fname).
split(/\n{2,}/). # split paragraphs
each_with_object({}) do |s,h|
outer_key, inner_key_names, *inner_values = s.split(/\n/)
inner_keys = inner_key_names.split(/,\s+/)
h[outer_key[/(?<=::)[^:]+/]] =
inner_values.each_with_object([]) { |values_str, a|
a << inner_keys.zip(values_str.split(/,\s+/)).to_h }
end
end
Example
First let's create your file.
FName = "animals.txt"
data =<<_
::pigs::
name, id, owner
peggy, 1, wolcott farms
sue, 2, blue moon farms
eddie, 3, sunrise farms
::cows::
name, id, owner
dee, 3, black hat farms
sunny, 2, blue moon farms
bess, 4, wolcott farms
_
File.write(FName, data)
#=> 203
Now execute the method on this file.
doit(FName)
#=> {"pigs"=>[{"name"=>"peggy", "id"=>"1", "owner"=>"wolcott farms "},
# {"name"=>"sue", "id"=>"2", "owner"=>"blue moon farms"},
# {"name"=>"eddie", "id"=>"3", "owner"=>"sunrise farms"}],
# "cows"=>[{"name"=>"dee", "id"=>"3", "owner "=>"black hat farms "},
# {"name"=>"sunny", "id"=>"2", "owner "=>"blue moon farms "},
# {"name"=>"bess", "id"=>"4", "owner "=>"wolcott farms"}]}
Explanation
The steps are as follows.
b = File.read(FName).split(/\n{2,}/)
#=> ["::pigs:: \nname, id, owner\npeggy, 1, wolcott farms \nsue, 2, blue moon farms\neddie, 3, sunrise farms",
# "::cows:: \nname, id, owner \ndee, 3, black hat farms \nsunny, 2, blue moon farms \nbess, 4, wolcott farms\n"]
Next we have
enum = b.each_with_object({})
#=> #<Enumerator: ["::pigs:: \nname, id, owner\npeggy, 1, wolcott farms \nsue, 2, blue moon farms\neddie, 3, sunrise farms",
# "::cows:: \nname, id, owner \ndee, 3, black hat farms \nsunny, 2, blue moon farms \nbess, 4, wolcott farms\n"]:each_with_object({})>
We can convert enum
to an array to see the values it will generate and pass to its block:
enum.to_a
#=> [["::pigs:: \nname, id, owner\npeggy, 1, wolcott farms \nsue, 2, blue moon farms\neddie, 3, sunrise farms", {}],
# ["::cows:: \nname, id, owner \ndee, 3, black hat farms \nsunny, 2, blue moon farms \nbess, 4, wolcott farms\n", {}]] The empty arrays above will be populated as the calculations proceed.
We now pass the first element of enum
to the block and assign the block variables
s,h = enum.next
#=> ["::pigs:: \nname, id, owner\npeggy, 1, wolcott farms \nsue, 2, blue moon farms\neddie, 3, sunrise farms", {}]
s #=> "::pigs:: \nname, id, owner\npeggy, 1, wolcott farms \nsue, 2, blue moon farms\neddie, 3, sunrise farms"
h => {}
The value of h
, an empty hash, will be modified by the block, as will be shown.
The block calculations are now performed for these values of s
and h
.
outer_key, inner_key_names, *inner_values = s.split(/\n/)
#=> ["::pigs:: ",
# "name, id, owner",
# "peggy, 1, wolcott farms ",
# "sue, 2, blue moon farms", "eddie, 3, sunrise farms"]
outer_key
#=> "::pigs:: "
inner_key_names
#=> "name, id, owner"
inner_values
#=> ["peggy, 1, wolcott farms ",
# "sue, 2, blue moon farms",
# "eddie, 3, sunrise farms"]
inner_keys = inner_key_names.split(/,\s+/)
#=> ["name", "id", "owner"]
h[outer_key[/(?<=::)[^:]+/]] =
inner_values.each_with_object([]) { |values_str, a|
a << inner_keys.zip(values_str.split(/,\s+/)).to_h }
# => [{"name"=>"peggy", "id"=>"1", "owner"=>"wolcott farms "},
# {"name"=>"sue", "id"=>"2", "owner"=>"blue moon farms"},
# {"name"=>"eddie", "id"=>"3", "owner"=>"sunrise farms"}]
h #=> {"pigs"=>[{"name"=>"peggy", "id"=>"1", "owner"=>"wolcott farms "},
# {"name"=>"sue", "id"=>"2", "owner"=>"blue moon farms"},
# {"name"=>"eddie", "id"=>"3", "owner"=>"sunrise farms"}]}
Let's examine this last calculation more closely.
outer_key[/(?<=::)[^:]+/]
#=> "pigs"
enum1 = inner_values.each_with_object([])
#=> #<Enumerator: ["peggy, 1, wolcott farms ", "sue, 2, blue moon farms",
# "eddie, 3, sunrise farms"]:each_with_object([])>
enum1.to_a
#=> [["peggy, 1, wolcott farms ", []],
# ["sue, 2, blue moon farms", []],
# ["eddie, 3, sunrise farms", []]]
The first element generated by enum1
is passed to the block and the block variables are assigned.
values_str, a = enum1.next
#=> ["peggy, 1, wolcott farms ", []]
values_str
#=> "peggy, 1, wolcott farms "
a #=> []
The block calculations are now performed.
b = inner_keys.zip(values_str.split(/,\s+/))
#=> ["name", "id", "owner"].zip(["peggy", "1", "wolcott farms "])
#=> [["name", "peggy"], ["id", "1"], ["owner", "wolcott farms "]]
c = b.to_h
#=> {"name"=>"peggy", "id"=>"1", "owner"=>"wolcott farms "}
a << c
#=> [{"name"=>"peggy", "id"=>"1", "owner"=>"wolcott farms "}]
The remaining two elements generated by enum1
are treated similarly, resulting in
h #=> {"pigs"=>[{"name"=>"peggy", "id"=>"1", "owner"=>"wolcott farms "},
# {"name"=>"sue", "id"=>"2", "owner"=>"blue moon farms"},
# {"name"=>"eddie", "id"=>"3", "owner"=>"sunrise farms"}]
The remaining calculations (for cows) are similar.
@pigs and @cows
If you insist on having those instance variables it is easy to generate them from the hash constructed above.
h = {"pigs"=>[{"name"=>"peggy", "id"=>"1", "owner"=>"wolcott farms "},
{"name"=>"sue", "id"=>"2", "owner"=>"blue moon farms"},
{"name"=>"eddie", "id"=>"3", "owner"=>"sunrise farms"}],
"cows"=>[{"name"=>"dee", "id"=>"3", "owner "=>"black hat farms "},
{"name"=>"sunny", "id"=>"2", "owner "=>"blue moon farms "},
{"name"=>"bess", "id"=>"4", "owner "=>"wolcott farms"}]}
h.each { |k,v| instance_variable_set("@#{k}", v) }
We now have:
@pigs
#=> [{"name"=>"peggy", "id"=>"1", "owner"=>"wolcott farms "},
# {"name"=>"sue", "id"=>"2", "owner"=>"blue moon farms"},
# {"name"=>"eddie", "id"=>"3", "owner"=>"sunrise farms"}]
@cows
#=> [{"name"=>"dee", "id"=>"3", "owner "=>"black hat farms "},
# {"name"=>"sunny", "id"=>"2", "owner "=>"blue moon farms "},
# {"name"=>"bess", "id"=>"4", "owner "=>"wolcott farms"}]
You can't use local variables, but you can use Object#instance_variable_get and Object#instance_variable_set to do this kind of metaprogramming. For example:
str = File.read '/tmp/livestock.txt'
records = str.split /\n\n+/
records.map! { |r| r.split /\n/ }
records.map do |r|
var = ?@ << r.shift.strip.delete(?:)
fields = r.shift.strip.scan /[^,]+/
hashes = r.map { |e| e.split(?,).flat_map &:strip }.
map { |e| fields.zip e }.
map &:to_h
instance_variable_set var,
instance_variable_get(var).to_a.push(hashes).flatten!
end;
# The data is now stored correctly in the following instance variables.
@pigs
@cows
Note that if @pigs or @cows already exist because you're testing in the REPL, your results may not be what you expect. Make sure you invoke Object#remove_instance_variable , set your variables to nil, or create a new instance of your class between tests.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.