我有一个字符串,我需要转换为哈希。字符串的键始终是一个符号,值始终为整数:
"a=1, b=2, c=3, d=4"
此字符串应返回如下所示的哈希:
{ :a => 1, :b => 2, :c => 3, :d => 4 }
我已经尝试了几种不同的东西,但是我能够到目前为止最接近的是将字符串分割两次,首先是逗号和空格,第二次是等号,然后创建符号:
def str_to_hash(str)
array = str.split(', ').map{|str| str.split('=').map{|k, v| [k.to_sym, v] }}
end
我预计会有以下输出:
{:a=>1, :b=>2, :c=>3, :d=>4}
相反,我得到了:
[[[:a, nil], [:"1", nil]], [[:b, nil], [:"2", nil]], [[:c, nil], [:"3", nil]], [[:d, nil], [:"4", nil]]]
如您所见,它创建了8个单独的字符串,包含4个符号。我无法弄清楚如何让Ruby识别数字并将它们设置为键/值对中的值。我在网上看过,甚至向我的同事求助,但到目前为止还没找到答案。有人可以帮忙吗?
答案 0 :(得分:4)
尝试这个我认为它看起来更清洁
s= "a=1, b=2, c=3, d=4"
Hash[s.scan(/(\w)=(\d)/).map{|a,b| [a.to_sym,b.to_i]}]
这是内部运作
#utilize scan with capture groups to produce a multidimensional Array
s.scan(/(\w)=(\d)/)
#=> [["a", "1"], ["b", "2"], ["c", "3"], ["d", "4"]]
#pass the inner Arrays to #map an replace the first item with a sym and the second to Integer
.map{|a,b| [a.to_sym,b.to_i]}
#=> [[:a, 1], [:b, 2], [:c, 3], [:d, 4]]
#Wrap the whole thing in Hash::[] syntax to convert
Hash[s.scan(/(\w)=(\d)/).map{|a,b| [a.to_sym,b.to_i]}]
#=> {:a=>1, :b=>2, :c=>3, :d=>4}
如果你想避免使用我总是丑陋的Hash::[]
方法,你可以做以下事情
#Ruby >= 2.1 you can use Array#to_h
s.scan(/(\w)=(\d)/).map{|a,b| [a.to_sym,b.to_i]}.to_h
#=> {:a=>1, :b=>2, :c=>3, :d=>4}
#Ruby < 2.1 you can use Enumerable#each_with_object
s.scan(/(\w)=(\d)/).each_with_object({}){|(k,v),obj| obj[k.to_sym] = v.to_i}
#=> {:a=>1, :b=>2, :c=>3, :d=>4}
虽然还有很多其他方法可以解决这个问题,但很多其他答案显而易见,这里只是为了好玩。
Hash[*s.scan(/(\w)=(\d)/).flatten.each_with_index.map{|k,i| i.even? ? k.to_sym : k.to_i}]
答案 1 :(得分:2)
> s = "a=1, b=2, c=3, d=4"
=> "a=1, b=2, c=3, d=4"
> Hash[s.split(",").map(&:strip).map { |p| p.split("=") }.map { |k, v| [ k.to_sym, v.to_i ] }]
=> {:a=>1, :b=>2, :c=>3, :d=>4}
问题的一部分是你试图在一行中完成它并且忘记了中间值是什么。将其分解为每个组件,确保您使用Ruby提供的等等。
您的命名假定您返回一个数组(而不是哈希)。但是,Hash[...]
将根据[key, value]
对数组创建一个哈希值。这使得手动哈希填充消失了。此外,该方法应该返回一个哈希,而不是设置一些东西 - 保持方法很小,而且是纯粹的。
注意我strip
第一组拆分值。这样可以避免使用:" a"
之类的符号,如果不修剪前导/尾随空格,则会得到这些符号。我的代码不将"a = 1"
之类的字符串带入您的帐户中。
首先,让事情变得可读。然后,如果(并且仅 if)它是有意义的,并且仍然清晰,可以打代码高尔夫球。
> s = "a=1, b=2, c=3, d=4"
=> "a=1, b=2, c=3, d=4"
> a1 = s.split(",")
=> ["a=1", " b=2", " c=3", " d=4"]
> a2 = a1.map(&:strip)
=> ["a=1", "b=2", "c=3", "d=4"]
> a3 = a2.map { |s| s.split("=") }
=> [["a", "1"], ["b", "2"], ["c", "3"], ["d", "4"]]
> a4 = a3.map { |k, v| [ k.to_sym, v.to_i ] }
=> [[:a, 1], [:b, 2], [:c, 3], [:d, 4]]
> Hash[a4]
=> {:a=>1, :b=>2, :c=>3, :d=>4}
不相关,但是如果你使用Ruby做很多ETL,特别是在纯文本上,使用mixins可以使代码更清晰,更接近DSL。你也可以玩很糟糕的游戏,比如:
> def splitter(sym, s)
String.send(:define_method, sym) do
split(s).map(&:strip)
end
end
> s = "a=1, b=2, c=3, d=4"
> splitter :split_comma, ","
> splitter :split_eq, "-"
> Hash[s.split_comma.map(&:split_eq).map { |k, v| [ k.to_sym, v.to_i ]}]
=> {:a=>1, :b=>2, :c=>3, :d=>4}
它可能会比这更糟糕,并成为一个成熟的ETL DSL。不过,如果你需要的话,这很棒。
答案 2 :(得分:0)
你试过Hash['a',1,'b',2,'c',3]
??
在你的irb终端上它应该给这个=&gt; {"a"=>1, "b"=>2, "c"=>3}
所以你所能做的就是拆分字符串并将其交给Hash
,这将为你完成工作。
Hash[s.split(",").map(&:strip).map{|p| x = p.split("="); [x[0].to_sym, x[1]]}]
希望有所帮助
答案 3 :(得分:0)
有点hackhish但是它有效,你可以从这里拿走它: - )
h = {}
"a=1, b=2, c=3, d=4".split(',').each do |fo|
k = fo.split('=').first.to_sym
v = fo.split('=').last
h[k] = v
end
puts h.class.name
puts h
答案 4 :(得分:0)
我的解决方案:
string = "a=1, b=2, c=3, d=4"
hash = {}
string.split(',').each do |pair|
key,value = pair.split(/=/)
hash[key] = value
end
puts hash.inspect
尽管不是一个单一的人,但这是一个可读的解决方案。