如何在每个循环中创建子数组

时间:2016-04-07 17:11:48

标签: ruby loops hashmap

我有一些像这样的文字行

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

3 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

这是我的看法:

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 :(得分:0)

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

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 ,块变量使用 parallel assignment 分配给该元素(有时称为多次转让):

(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"=>{"age"=>"34", "iq"=>"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,但这是另一个故事。