【问题标题】:How to make sub array within a each loop如何在每个循环中制作子数组
【发布时间】:2016-04-07 17:11:48
【问题描述】:

我有一些这样的文本行

name carl
age 34
name sean
age 15

我有这个代码

lines.each_with_object({}) do |l,r|
     key, value = l.split(' ', 2)
     r[key] = value
end

哪个输出:

{"name"=>"sean", "age"=>"15"}

carl 没有被添加,因为被 sean 覆盖了

我正在寻找的是:带有name 的每一行都创建一个新元素并将它们的值放在那里

{ "carl" => { "age" => "34" }, "sean" => { "age" => 15 } }

【问题讨论】:

    标签: ruby loops hashmap


    【解决方案1】:

    这是我的看法:

    hash, current_hash = {}, {}
    
    lines.each do |line|
      key, value = line.split(' ', 2)
      if key == 'name'
        hash[value] = current_hash = {}
      else
        current_hash[key] = value
      end
    end
    

    hash 是结果。

    reduce 的另一种方式:

    current_hash = {}
    
    lines.reduce({}) do |hash, line|
      key, value = line.split(' ', 2)
      if key == 'name'
        hash[value] = current_hash = {}
      else
        current_hash[key] = value
      end
      hash
    end
    

    【讨论】:

      【解决方案2】:
      lines.each_slice(2).with_object({}) do |pair, hsh|
        _key, name, attribute, value = pair.flat_map(&:split)
        hsh[name] = { attribute => value }
      end
      

      这可能是一种方法。

      编辑。如果我们可以在name whoever 行下定义多个属性,我们可以这样:

      lines.slice_before(/name/).with_object({}) do |clump, hsh|
        _key, name, *rest = *clump.flat_map(&:split)
        hsh[name] = rest.each_slice(2).to_h
      end
      

      【讨论】:

      • 这适用于两对,但我真正想要的是,对于像 age 34 这样的每一对在 name carl 之后在 carl 数组中,当到达 name sean 之后的所有内容在肖恩数组下......有什么想法吗?
      • @greenbandit 我添加了第二个解决方案。当每个名称下至少有一个属性时,这应该有效。
      【解决方案3】:

      我从评论中看到要分组的连续代码行数是未知的。假设字符串如下:

      str = <<BITTER_END
      name carl
      age 34
      iq 95
      name sean
      age 15
      iq 166
      BITTER_END
        #=> "name carl\nage 34\niq 95\nname sean\nage 15\niq 166\n" 
      

      第一步是确定组数。我们可以这样做:

      first = str[/\w+/]
        #=> "name"
      nbr_groups = str.scan(/\b#{first}\b/).size
        #=> 2
      

      \b 是一个“分词”以避免匹配,例如“重命名”。

      现在让我们将str 转换为一个数组,并在处理过程中去掉那些讨厌的换行符:

      arr = str.lines.map(&:chomp!)
        #=> ["name carl", "age 34", "iq 95",
        #    "name sean", "age 15", "iq 166"] 
      

      每组的元素数等于:

      arr.size/nbr_groups
        #=> 3
      

      我们现在准备申请Enumerable#each_slice

      enum = arr.each_slice(arr.size/nbr_groups)
        #=> #<Enumerator: ["name carl", "age 34", "iq 95", "name sean", "age 15",
        #    "iq 166"]:each_slice(3)> 
      

      我们可以将此枚举器转换为数组以查看它将生成的(两个)元素:

      enum.to_a
        #=> [["name carl", "age 34", "iq 95"],
        #    ["name sean", "age 15", "iq 166"]]
      

      我们现在可以形成所需的哈希。

      enum.each_with_object({}) do |(key_str, *val_arr), h|
        h[key_str.split.last] = val_arr.each_with_object({}) do |key_val_str, g|
          key, value = key_val_str.split
          g[key] = value
        end
      end
        #=> {"carl"=>{"age"=>"34", "iq"=>"95"}, "sean"=>{"age"=>"15", "iq"=>"166"}} 
      

      要查看这里发生了什么,首先请注意正在创建另一个枚举器:

      enum1 = enum.each_with_object({})
        #=> #<Enumerator: #<Enumerator: ["name carl", "age 34", "iq 95", "name sean",
        #     "age 15", "iq 166"]:each_slice(3)>:each_with_object({})> 
      enum1.to_a
        #=> [[["name carl", "age 34", "iq 95"], {}],
        #    [["name sean", "age 15", "iq 166"], {}]] 
      

      如果您仔细查看第一个返回值,您会发现enum1 可以被认为是一个“复合枚举器”。

      enum1 的第一个元素被传递给块1,并且块变量使用并行赋值(有时称为多重赋值)分配给该元素):

      (key_str, *val_arr), h = enum1.next
        #=> [["name carl", "age 34", "iq 95"], {}] 
      key_str
        #=> "name carl" 
      val_arr
        #=> ["age 34", "iq 95"] 
      h #=> {} 
      

      因此块计算为:

      h["carl"] = ["age 34", "iq 95"].each_with_object({}) do |key_val_str, g|
        key, value = key_val_str.split
        g[key] = value
      end
        #=> {"age"=>"34", "iq"=>"95"}
      

      我将把它留给读者来解码这个表达式的右侧。 sean 得到相同的处理(除了块变量 h 将等于 {"carl"=&gt;{"age"=&gt;"34", "iq"=&gt;"95"}})。

      综合起来,我们有:

      arr = str.lines.map(&:chomp!)
      arr.each_slice(arr.size/str.scan(/\b#{str[/\w+/]}\b/).size).
          each_with_object({}) do |(key_str, *val_arr), h|
            h[key_str.split.last] = val_arr.each_with_object({}) do |key_val_str, g|
            key, value = key_val_str.split
            g[key] = value
        end
      end 
        #=> {"carl"=>{"age"=>"34", "iq"=>"95"}, "sean"=>{"age"=>"15", "iq"=>"166"}} 
      

      虽然保留一些中间变量可能更清楚。

      1 这是由Enumerator#each 完成的,它调用Array#each,但这是另一回事。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-25
        • 1970-01-01
        • 1970-01-01
        • 2011-07-05
        相关资源
        最近更新 更多