将字符串值转换为Hash

时间:2014-12-04 14:08:45

标签: ruby string hash

我有一个字符串,我需要转换为哈希。字符串的键始终是一个符号,值始终为整数:

"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识别数字并将它们设置为键/值对中的值。我在网上看过,甚至向我的同事求助,但到目前为止还没找到答案。有人可以帮忙吗?

5 个答案:

答案 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

尽管不是一个单一的人,但这是一个可读的解决方案。