简体   繁体   English

将字符串值转换为Hash

[英]Convert string values to Hash

I have a string that I need to convert into a hash. 我有一个字符串,我需要转换为哈希。 The string's key will always be a symbol and the value will always be an integer: 字符串的键始终是一个符号,值始终为整数:

"a=1, b=2, c=3, d=4"

This string should return a hash that looks like: 此字符串应返回如下所示的哈希:

{ :a => 1, :b => 2, :c => 3, :d => 4 }

I've tried several different things, but the closest I've been able to come so far is to split the string twice, first for the comma and space, second for the equal sign, and create symbols: 我已经尝试了几种不同的东西,但是我能够到目前为止最接近的是将字符串分割两次,首先是逗号和空格,第二次是等号,然后创建符号:

def str_to_hash(str)
  array = str.split(', ').map{|str| str.split('=').map{|k, v| [k.to_sym, v] }}
end

I'd expected the following output: 我期待以下输出:

{:a=>1, :b=>2, :c=>3, :d=>4}

Instead I got: 相反,我得到了:

[[[:a, nil], [:"1", nil]], [[:b, nil], [:"2", nil]], [[:c, nil], [:"3", nil]], [[:d, nil], [:"4", nil]]]

As you can see, it is creating 8 separate strings with 4 symbols. 如您所见,它创建了8个单独的字符串,包含4个符号。 I can't figure out how to make Ruby recognize the numbers and set them as the values in the key/value pair. 我无法弄清楚如何让Ruby识别数字并将它们设置为键/值对中的值。 I've looked online and even asked my coworkers for help, but haven't found an answer so far. 我上网了,甚至向我的同事求助,但到目前为止还没有找到答案。 Can anybody help? 有人可以帮忙吗?

Try this I think it looks a little cleaner 尝试这个我认为它看起来更清洁

s= "a=1, b=2, c=3, d=4"
Hash[s.scan(/(\w)=(\d)/).map{|a,b| [a.to_sym,b.to_i]}]

Here is the inner workings 这是内部运作

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

If you want to avoid the Hash::[] method which I have always though was ugly you can do the following 如果你想避免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}

Although there are a ton of other ways to handle this issue as is evident by the many other answers here is one more just for fun. 虽然还有很多其他方法可以解决这个问题,但很多其他答案显而易见,这里只是为了好玩。

Hash[*s.scan(/(\w)=(\d)/).flatten.each_with_index.map{|k,i| i.even? ? k.to_sym : k.to_i}]
> 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}

Part of the problem is that you're trying to do it in a single line and losing track of what the intermediate values are. 问题的一部分是你试图在一行中完成它并且忘记了中间值是什么。 Break it down into each component, make sure you're using what Ruby gives you, etc. 将其分解为每个组件,确保您使用Ruby提供的内容等。

Your naming assumes you get an array back (not a hash). 您的命名假定您返回一个数组(而不是哈希)。 Hash[...] , however, will create a hash based on an array of [key, value] pairs. 然而, Hash[...]将基于[key, value]对的数组创建散列。 This makes manual hash stuffing go away. 这使得手动哈希填充消失了。 Also, that method should return a hash, not set something–keep methods small, and pure. 此外,该方法应该返回一个哈希,而不是设置一些东西 - 保持方法小,而且纯。

Note I strip the first set of split values. 注意我strip了第一组拆分值。 This avoids symbols like :" a" , which you get if you don't trim leading/trailing spaces. 这样可以避免使用符号:" a" ,如果不修剪前导/尾随空格,则会得到这些符号。 My code does not take strings like "a = 1" into account–yours should. 我的代码并不需要像琴弦"a = 1"到应该考虑-你的。

First, make things readable. 首先,让事情变得可读。 Then, if (and only if) it makes sense, and remains legible, play code golf. 然后,如果(并且当)它有意义并且保持清晰,则打代码高尔夫。

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

Unrelated, but if you're doing a lot of ETL with Ruby, especially on plain text, using mixins can make code much cleaner, closer to a DSL. 不相关,但是如果你使用Ruby做很多ETL,特别是在纯文本上,使用mixins可以使代码更清晰,更接近DSL。 You can play horrible games, too, like: 你也可以玩可怕的游戏,比如:

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

It can get significantly worse than this and become a full-fledged ETL DSL. 它可能会比这更糟糕,并成为一个成熟的ETL DSL。 It's great if you need it, though. 不过,如果你需要的话,它会很棒。

Have you tried Hash['a',1,'b',2,'c',3] ?? 你试过Hash['a',1,'b',2,'c',3] ??

On your irb terminal it should give this => {"a"=>1, "b"=>2, "c"=>3} 在你的irb终端上它应该给这个=> {"a"=>1, "b"=>2, "c"=>3}

So all that you can do is split the string and give it to Hash which will do the job for you. 所以你所能做的就是分割字符串并将其交给Hash ,它将为你完成这项工作。

Hash[s.split(",").map(&:strip).map{|p| x = p.split("="); [x[0].to_sym, x[1]]}]

Hope that helps 希望有所帮助

a little hackhish but it works, you can take it from here :-) 有点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

My solution: 我的解决方案

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

Despite from not being a one-linner it's a readable solution. 尽管不是一个单一的人,但它是一个可读的解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM