【问题标题】:Converting an array to hash in ruby在ruby中将数组转换为哈希
【发布时间】:2018-03-20 12:24:05
【问题描述】:

假设我有一个看起来像这样的数组

testarr = [["Actor", "Morgan", "33", ["A","B"]],
  ["Movie", "Titanic", "44", ["A","A"]],
  ["Actor", "Jack Black", "333", ["A","A"]]]

我想把它转换成一个哈希,最终会转换成一个json。

我希望它看起来像

{

    "Actor" => { 
           {   "name" : "Morgan",
               "Age" : 33",
               "Films: { "A", "B" }} ,

           {   "name" : "Jack Black",
               "Age" : 44",
               "Films: { "A", "A" }}
           }
    "Movie" => {
           {    "Title" : "Titanic"
                "Gross" : "44"
                "Actors" : { "A", "A" }
           }
     }

不确定确切的格式,但无论如何都有意义。

我试过了

def hashing(arr)
 hash = Hash.new

 arr.each do |item|

     if item[0] == "Movie"
       item.delete("Movie")
       hash["Movie"] = item
       item["Title"] = item[1]
       item["Movie"]["Box Office"] = item[2]
       item["Movie"]["Actors"] = item[3]

     else

        item.delete("Actor")
        hash["Actor"] = item

        item["Actor"]["Name"] == item[1]
        item["Actor"]["Age"] == item[2]
        item["Actor"]["Filmography"] == item[3]

     end

   end

  return hash

end

testarr = [["Actor", "Morgan", "33", ["dsfds","dsfdsf"]],
  ["Movie", "Titanic", "44", ["dsfds","dfdsf"]],
  ["Actor", "Jack Black", "333", ["ssdsfds","dsfdsf"]]]

puts hashing(testarr)

但是将数组项放入“电影”和“演员”然后尝试创建“姓名”和“年龄”等键时会出错。

我怎样才能按照自己的意愿制作?

【问题讨论】:

    标签: arrays json ruby hash


    【解决方案1】:

    请尝试以下代码,

    v = [["Actor", "Morgan", "33", ["A", "B"]], ["Movie", "Titanic", "44", ["A", "A"]], ["Actor", "Jack Black", "333", ["A", "A"]]]
    
    v.inject({}) do |ot, arr|
      item = {name: arr[1], age: arr[2], films: arr[3]}
      if ot[arr[0]].present?
        ot[arr[0]] << item
      else
        ot[arr[0]] = []
        ot[arr[0]] << item
      end
      ot
    end
    

    而o/p如下所示,

    # => {"Actor"=>[{:name=>"Morgan", :age=>"33", :films=>["A", "B"]}, {:name=>"Jack Black", :age=>"333", :films=>["A", "A"]}], "Movie"=>[{:name=>"Titanic", :age=>"44", :films=>["A", "A"]}]}
    

    请注意这里 Actor 不是散列的散列,它是散列数组,这是保存集合的标准方法,如果需要,可以使用 to_json 将其转换为 json strong> 方法。

    【讨论】:

    • 谢谢!但是我得到了一个未定义的方法“存在吗?”对于 nil:NilClass (NoMethodError)。这是一个有效的方法吗?
    • 是的,它是 rails 标准方法。您是否使用 Rails?
    • 我使用的是普通红宝石
    【解决方案2】:
    testarr = [["Actor", "Morgan", "33", ["A","B"]],
      ["Movie", "Titanic", "44", ["A","A"]],
      ["Actor", "Jack Black", "333", ["A","A"]]]
    
      a = Hash.new{ |h,k| h[k] = [] }
    
      testarr.each do |arr|
        b = {name: arr[1], age: arr[2], films: arr[3]}
        a[arr[0]] << b
      end
    

    这会产生

    {"Actor"=>[{"name"=>"Morgan", "age"=>"33", "films"=>["A", "B"]}, {"name"=>"Jack Black", "age"=>"333", "films"=>["A", "A"]}], "Movie"=>[{"name"=>"Titanic", "age"=>"44", "films"=>["A", "A"]}]}
    

    【讨论】:

    • 谢谢。我怎样才能真正将演员的年龄和电影摄影放入名称键中? Like Name => Morgan => { Age: 33, Filmo = [...] }
    【解决方案3】:

    您需要遍历数组并解析每个项目,将其附加到结果哈希中。

    testarr = [["Actor", "Morgan", "33", ["A", "B"]],
               ["Movie", "Titanic", "44", ["A", "A"]],
               ["Actor", "Jack Black", "333", ["A", "A"]]]
    
    results = {}
    
    testarr.each do |item|
      key, a, b, c = item
      r = if key == 'Actor'
            { name: a, age: b, movies: c }
          elsif key == 'Movie'
            { title: a, gross: b, actors: c }
          end
      results[key] = [] unless results[key]
      results[key] << r
    end
    
    puts results
    

    这将产生:

    {"Actor"=>[{:name=>"Morgan", :age=>"33", :movies=>["A", "B"]}, {:name=>"Jack Black", :age=>"333", :movies=>["A", "A"]}], "Movie"=>[{:title=>"Titanic", :gross=>"44", :actors=>["A", "A"]}]}
    

    【讨论】:

      【解决方案4】:

      :actor 中的值包含没有键的哈希。你能做的最好的事情就是把它放到一个数组中。

      这会奏效。可能有更清洁的方法,但我目前不确定如何:

      h = Hash.new { |hash, key| hash[key] = [] }
      testarr = [["Actor", "Morgan", "33", ["A", "B"]], ["Movie", "Titanic", "44", ["A", "A"]], ["Actor", "Jack Black", "333", ["A", "A"]]]
      
      testarr.each do |t|
        if t[0] == 'Movie'
          h[t[0]] << {title: t[1], gross: t[2], actors: t[3]}
        else
          h[t[0]] << {name: t[1], age: t[2], films: t[3]}
        end
      end
      
      puts h
      

      输出:

      {"Actor"=>[{:name=>"Morgan", :age=>"33", :films=>["A", "B"]}, {:name=>"Jack Black", :age=>"333", :films=>["A", "A"]}], "Movie"=>[{:title=>"Titanic", :gross=>"44", :actors=>["A", "A"]}]}
      

      【讨论】:

      • 谢谢。我怎样才能真正将演员的年龄和电影摄影放入名称键中? Like Name => Morgan => { Age: 33, Filmo = [...] }
      【解决方案5】:

      我试图保留你写的例子。

      首先,必须为Array(如[a, b])而不是Hash({a, b})的列表项整形

      # You may want result like this ...
      {
          "Actor": [    # not '{' but '['
              {
                  "name": "Morgan",
                  "Age": "33",
                  "Films": ["A", "B"]    # not '{' but '[' also
              },
              {
                  "name": "Jack Black",
                  "Age": "44",
                  "Films": ["A", "A"]
              }
          ],
          "Movie": [
              {
                  "Title": "Titanic",
                  "Gross": "44",
                  "Actors": ["A", "A"]
              }
          ]
      }
      

      然后你的函数应该是这样的......

      def hashing(arr)
          hash = Hash.new
          hash["Movie"], hash["Actor"] = [], []
      
          arr.each do |item|
      
              if item[0] == "Movie"
                  movie = {}
                  movie["Title"]      = item[1]
                  movie["Box Office"] = item[2]
                  movie["Actors"]     = item[3]
      
                  item.delete("Movie")         # optional
                  hash["Movie"] << movie
      
              else
                  actor = {}
                  actor["Name"]           = item[1]
                  actor["Age"]            = item[2]
                  actor["Filmography"]    = item[3]
      
                  item.delete("Actor")         # optional
                  hash["Actor"] << actor
              end
      
          end
      
          return hash
      end
      

      那么是时候测试了! 作为您的代码,

      testarr = [
          ["Actor", "Morgan", "33", ["dsfds","dsfdsf"]],
          ["Movie", "Titanic", "44", ["dsfds","dfdsf"]],
          ["Actor", "Jack Black", "333", ["ssdsfds","dsfdsf"]]
      ]
      
      puts hashing(testarr)
      

      它会返回这个:

      {
        "Movie"=>
          [
            {"Title"=>"Titanic", "Box Office"=>"44", "Actors"=>["dsfds", "dfdsf"]}
          ],
        "Actor"=>
          [
            {"Name"=>"Morgan", "Age"=>"33", "Filmography"=>["dsfds", "dsfdsf"]},
            {"Name"=>"Jack Black", "Age"=>"333", "Filmography"=>["ssdsfds", "dsfdsf"]}
          ]
      }
      

      【讨论】:

      • 谢谢。我怎样才能真正将演员的年龄和电影摄影放入名称键中? Like Name => Morgan => { Age: 33, Filmo = [...] }
      • 简单!再次包裹你的结构。在您的表达中,Name =&gt; Morgan =&gt; {...} Name =&gt; { Morgan =&gt; {...} } 之类的包装器一起使用。您在结构中的所有键,都必须具有完整的结构以获取价值,例如我包装的内容。顺便说一句,为了更清楚地保护和定义对象,我建议使用原始的而不是新的。
      【解决方案6】:

      代码

      def convert(arr, keys)
        arr.group_by(&:first).transform_values do |a|
          a.map { |key, *values| keys[key].zip(values).to_h }
        end
      end
      

      示例(使用问题中定义的testarr

      keys = { "Actor"=>[:name, :Age, :Films], "Movie"=>[:Title, :Gross, :Actors] }
      
      convert(testarr, keys)
        #=> { "Actor"=>[
        #       {:name=>"Morgan", :Age=>"33", :Films=>["A", "B"]},
        #       {:name=>"Jack Black", :Age=>"333", :Films=>["A", "A"]}
        #     ],
        #     "Movie"=>[
        #      {:Title=>"Titanic", :Gross=>"44", :Actors=>["A", "A"]}
        #     ]
        #   }
      

      说明

      请参阅Enumerable#group_byHash#transform_valuesArray#zipArray#to_h

      步骤如下。

      h = testarr.group_by(&:first)
        #=> { "Actor"=>[
        #       ["Actor", "Morgan", "33", ["A", "B"]],
        #       ["Actor", "Jack Black", "333", ["A", "A"]]
        #     ],
        #     "Movie"=>[
        #       ["Movie", "Titanic", "44", ["A", "A"]]
        #     ]
        #   }
      

      虽然不完全等同,但您可以将testarr.group_by(&amp;:first) 视为testarr.group_by { |a| a.first } 的“简写”。继续,

      e0 = h.transform_values
        #=> #<Enumerator:
        #   {"Actor"=>[["Actor", "Morgan", "33", ["A", "B"]],
        #              ["Actor", "Jack Black", "333", ["A", "A"]]],
        #    "Movie"=>[["Movie", "Titanic", "44", ["A", "A"]]]}
        #  :transform_values>
      

      第一个元素由枚举器e0 生成,传递给块,块变量设置为等于该值。

      a = e0.next
        #=> [["Actor", "Morgan", "33", ["A", "B"]],
        #    ["Actor", "Jack Black", "333", ["A", "A"]]]
      

      现在创建了第二个枚举器。

      e1 = a.map
        #=> #<Enumerator: [["Actor", "Morgan", "33", ["A", "B"]],
        #                  ["Actor", "Jack Black", "333", ["A", "A"]]]:map>
      

      第一个值由e1 生成,传递给内部块,并为块变量赋值(使用消歧)。

      key, *values = e1.next
        #=> ["Actor", "Morgan", "33", ["A", "B"]]
      key
        #=> "Actor"
      values
        #=> ["Morgan", "33", ["A", "B"]]
      

      现在执行内部块计算。

      b = keys[key].zip(values)
        #=> keys["Actor"].zip(["Morgan", "33", ["A", "B"]])
        #=> [:name, :Age, :Films].zip(["Morgan", "33", ["A", "B"]])
        #=> [[:name, "Morgan"], [:Age, "33"], [:Films, ["A", "B"]]]
      b.to_h
        #=> {:name=>"Morgan", :Age=>"33", :Films=>["A", "B"]}
      

      现在第二个和最后一个元素由e1 生成并执行相同的计算。

      key, *values = e1.next
        #=> ["Actor", "Jack Black", "333", ["A", "A"]]
      b = keys[key].zip(values)
        #=> [[:name, "Jack Black"], [:Age, "333"], [:Films, ["A", "A"]]]
      b.to_h
        #=> {:name=>"Jack Black", :Age=>"333", :Films=>["A", "A"]}
      

      当从e1 中寻找另一个值时,我们会得到以下结果。

      e1.next
        #=> StopIteration: iteration reached an end
      

      这个异常被捕获,导致e1返回到外部块。此时e0 生成下一个(也是最后一个值)。

      a = e0.next
        #=> [["Movie", "Titanic", "44", ["A", "A"]]]
      

      其余的计算类似。

      【讨论】:

        猜你喜欢
        • 2010-12-11
        • 1970-01-01
        • 1970-01-01
        • 2021-01-17
        • 1970-01-01
        • 1970-01-01
        • 2021-10-16
        • 2017-07-06
        • 1970-01-01
        相关资源
        最近更新 更多