如何将字符串转换为数组的名称?

时间:2016-09-18 18:46:21

标签: arrays ruby string metaprogramming local-variables

我认为我已经从字符串创建了多个数组,但如果我尝试检查数组,则会收到错误。

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>'

理想情况下我想创建pig = [] 然后将哈希添加到此数组,例如:

pigs = [{"name"=>"peggy", "id"=>1, "owner"=>"wolcott farms"},
{"name"=>"sue", "id"=>2, "owner"=>"blue moon farms"},
{"name"=>"eddie", "id"=>3, "owner"=>"sunrise farms"}
]

和奶牛等相同

我的文本文件animals.txt是

::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

2 个答案:

答案 0 :(得分:0)

由于Ruby v1.8,因此无法创建局部变量。实例变量,是(使用Object#instance_variable_set),局部变量,编号

@CodeGnome提供了一种使用给定名称创建实例变量的方法。我不认为实例变量的集合是适当的数据结构。我建议你考虑使用哈希代替。你可以这样做。

<强>代码

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

示例

首先让我们创建你的文件。

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

现在在此文件上执行方法。

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"}]} 

<强>解释

步骤如下。

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"]

接下来我们有

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({})> 

我们可以将enum转换为数组,以查看它将生成的值并传递给它的块:

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.

我们现在将enum的第一个元素传递给块并分配块变量

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 => {} 

空格式h的值将被块修改,如下所示。

现在,对sh的这些值执行块计算。

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"}]} 

让我们更仔细地研究这最后的计算。

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", []]]

enum1生成的第一个元素被传递给块,块变量被分配。

values_str, a = enum1.next
  #=> ["peggy, 1, wolcott farms ", []] 
values_str
  #=> "peggy, 1, wolcott farms "
a #=> []

现在执行块计算。

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 "}]

enum1生成的其余两个元素的处理方式相似,结果为

h #=> {"pigs"=>[{"name"=>"peggy", "id"=>"1", "owner"=>"wolcott farms "},
  #             {"name"=>"sue", "id"=>"2", "owner"=>"blue moon farms"},
  #             {"name"=>"eddie", "id"=>"3", "owner"=>"sunrise farms"}] 

剩下的计算(针对奶牛)是相似的。

@pigs和@cows

如果你坚持使用那些实例变量,很容易从上面构造的哈希生成它们。

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

我们现在有:

@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"}] 

答案 1 :(得分:0)

解析文本,然后使用实例变量分配

您无法使用局部变量,但您可以使用Object#instance_variable_getObject#instance_variable_set来执行此类元编程。例如:

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

买者

请注意,如果因为您在REPL中进行测试而 @pigs @cows 已经存在,那么您的结果可能与您的预期不符。确保调用Object#remove_instance_variable,将变量设置为nil,或者在测试之间创建类的新实例。