我从评论中看到要分组的连续代码行数是未知的。假设字符串如下:
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"=>{"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,但这是另一回事。