我有一些像这样的文字行
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 } }
答案 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,但这是另一个故事。