Converting Setting File to Hash - ruby

I have a settings file and I am reading it with file read and getting a string that I want to convert into a hash for easier usage.
How can I convert the following:
string="key1=value1\nkey2=value2"
Into:
{"key1" => "value1", "key2" => "value2"}

You can do this:
string.split("\n").map{|s| s.split("=")}.to_h
First split around new lines.
string.split("\n")
#=> ["key1=value1", "key2=v vlue2"]
Next, split each strings around =
string.split("\n").map{|s| s.split("=")}
#=> [["key1", "value1"], ["key2", "v vlue2"]]
Next, convert the array of 2-element arrays into Hash by calling to_h method.
string.split("\n").map{|s| s.split("=")}.to_h
#=> {"key1"=>"value1", "key2"=>"v vlue2"}

Hash[*string.split(/[\n=]/)] # => {"key1"=>"value1", "key2"=>"value2"}

Related

Understanding map in Ruby

I'm pretty new to Ruby and am trying to understand an example of the map method that I came across:
{:a => "foo", :b => "bar"}.map{|a, b| "#{a}=#{b}"}.join('&')
which returns:
=> "a=foo&b=bar"
I don't understand how the
b=bar
is returned. The string interpolation is what is confusing me as it seems it would return something like:
=> "a=foo&bbar"
> {:a => "foo", :b => "bar"}.map{|key, value| "#{key}=#{value}"}
#=> ["a=foo", "b=bar"]
map method will fetch each element of hash as key and value pair
"#{key}=#{value}" is a String Interpolation which adds = between your key and value
Using this syntax everything between the opening #{ and closing } bits
is evaluated as Ruby code, and the result of this evaluation will be
embedded into the string surrounding it.
Array#join will returns a string created by converting each element of the array to a string, separated by the given separator.
so here in your case:
> ["a=foo", "b=bar"].join('&')
#=> "a=foo&b=bar"
In Rails you can convert hash to query params using Hash#to_query method, which will return the same result.
> {:a => "foo", :b => "bar"}.to_query
#=> "a=foo&b=bar"
The symbol key :a and the local variable a have nothing in common. The names are only coincidentally the same. Consider this code instead:
{
var1: "value1",
var2: "value2"
}.map do |key, value|
"#{key}=#{value}"
end.join('&')
# => "var1=value1&var2=value2"
Here the variables are different. What map does, like each, is iterate over each key-value pair in the Hash. That means you can do things like this, too, to simplify:
{
var1: "value1",
var2: "value2"
}.map do |pair|
pair.join('=')
end.join('&')
# => "var1=value1&var2=value2"
Normally when iterating over a Hash you should use names like k,v or key,value to be clear on what you're operating on.
If you're ever confused what's going on internally in an iteration loop, you can debug like this:
{
var1: "value1",
var2: "value2"
}.map do |pair|
puts pair.inspect
pair.join('=')
end.join('&')
That gives you this output:
[:var1, "value1"]
[:var2, "value2"]
That technique helps a lot. There's even the short-hand notation for this:
p pair
There are 2 method calls occurring here, the map and the join. One way to make it clearer and easier to understand is to separate the two methods and alter the keywords used in the map method. So instead of
{:a => "foo", :b => "bar"}.map{|a, b| "#{a}=#{b}"}.join('&')
Lets have
{:a => "foo", :b => "bar"}.map{|key, value| "#{key}=#{value}"}
This returns an array. #=> ["a=foo", "b=bar"]
Now:
["a=foo", "b=bar"].join('&')
produces a sting
#=> "a=foo&b=bar"
Map is iterating over the two key/value pairs and creating a string with the '=' between them and returns it in an array. It would iterate over all the key/value pairs in the harsh. Our example just has 2.
Join attaches the two elements of the array together with the '&' symbol between them and returns it as string. It would attach all elements of the array no matter its size.
What helped me to learn map and join is to open up the irb or pry and create a few hashes and arrays and play around with them. I highly recommend using unique names for your values that explain what is going on.
I hope this helps you.

Creating a map from an array of arrays in Ruby

I'm trying to write a function in Ruby which can take in a in an array of arrays, and convert it into a hash. This will be used to simulate sentences and create arbitrary word sequences. The resulting map will be helpful in generating all combination of sentences available with the current sentence rules.
How do I go about achieving this? I'm a bit lost as to where to start.
Try this:
arr = [ ["<start>", "The <object> <verb> tonight."],
["<object>", "waves", "big yellow flowers", "slugs"],
["<verb>", "sigh <adverb>", "portend like <object>", "die <adverb>"],
["<adverb>", "warily", "grumpily"] ]
arr.map { |ar| [ar.shift, ar.map { |str| str.split }] }.to_h
#=>
#{ "<start>" => [["The", "<object>", "<verb>", "tonight."]],
# "<object>" => [["waves"], ["big", "yellow", "flowers"], ["slugs"]],
# "<verb>" => [["sigh", "<adverb>"], ["portend", "like", "<object>"], ["die", "<adverb>"]],
# "<adverb>" => [["warily"], ["grumpily"]] }
ar.shift takes the first element of each subarray. The block used with ar.map splits (on whitespace) the remaining elements into arrays. Finally to_h converts the resulting array into a hash.

Insert in multiple arrays, in hash with key values in ruby

array1 = [[a,b,c],[1,2,3],[x,y,z]]
array2 = [[1,2,1],[2,2,2],[a,a,a]]
array3 = [[d,d,d], {a=>1,b=>2}]
#keys = key1,key2,key3
i need to to show the one single hash in below format
output = {"key1" => [[a,b,c],[1,2,3],[x,y,z]], "key2" => [[1,2,1],[2,2,2],[a,a,a]], "key3" => [[d,d,d], {a=>1,b=>2}] }
i am tried to in this code
Hash[#key.zip(array1)]
Multiple variables can't be manipulated as easily as elements in, say, an array. The easiest way to produce desired output in your case is to just write it out manually.
output = {
key1: array1,
key2: array2,
key3: array3,
}
If you want to be more dynamic, put your arrays into a usable data structure. Then you can use .zip or whatever.
keys = [:key1, :key2, :key3]
usable_arrays = [array1, array2, array3]
Hash[keys.zip(usable_arrays)]

How to "zip" two arrays into hash

I want to "zip" two arrays into a Hash.
From:
['BO','BR']
['BOLIVIA','BRAZIL']
To:
{BO: 'BOLIVIA', BR:'BRAZIL'}
How can I do it?
I would do it this way:
keys = ['BO','BR']
values = ['BOLIVIA','BRAZIL']
Hash[keys.zip(values)]
# => {"BO"=>"BOLIVIA", "BR"=>"BRAZIL"}
If you want symbols for keys, then:
Hash[keys.map(&:to_sym).zip(values)]
# => {:BO=>"BOLIVIA", :BR=>"BRAZIL"}
In Ruby 2.1.0 or higher, you could write these as:
keys.zip(values).to_h
keys.map(&:to_sym).zip(values).to_h
As of Ruby 2.5 you can use .transform_keys:
Hash[keys.zip(values)].transform_keys { |k| k.to_sym }
Just use the single Array of the twos, and then transpose it, and generate Hash:
keys = ['BO','BR']
values = ['BOLIVIA','BRAZIL']
Hash[[keys,values].transpose]
# => {"BO"=>"BOLIVIA", "BR"=>"BRAZIL"}
or for newer ruby version:
[keys,values].transpose.to_h
Ironically, if you just sprinkle some dots and underscores into your question, it just works:
I want to "zip" two arrays into_hash
ary1.zip(ary2).to_h
# => { 'BO' => 'BOLIVIA', 'BR' => 'BRAZIL' }
Actually, you specified in your output hash that the keys should be Symbols not Strings, so we need to convert them first:
ary1.map(&:to_sym).zip(ary2).to_h
# => { BO: 'BOLIVIA', BR: 'BRAZIL' }
Quite readable version would be:
keys = ['BO','BR']
values = ['BOLIVIA','BRAZIL']
keys.zip(values).each_with_object({}) do |(key, value), hash|
hash[key.to_sym] = value
end
You can make a zipped array and then convert the array into hash like so :
keys = ['BO','BR']
values = ['BOLIVIA','BRAZIL']
array = key.zip(values) # => [['BO','BOLIVIA'],['BR','BRAZIL']]
hash = array.to_h # => {'BO' => 'BOLIVIA','BR' => 'BRAZIL'}

How to collect only string instances from a collection?

I have an array, which is comprising of different kind of objects. But I would like to get only the string instances. What i wrote as below :
ary = ["11",1,2,"hi",[11]]
ary.select{|e| e.instance_of? String } # => ["11", "hi"]
I am looking for an elegant way of doing this, if any.
I would do as below using Enumerable#grep :
Returns an array of every element in enum for which Pattern === element. If the optional block is supplied, each matching element is passed to it, and the block’s result is stored in the output array.
ary = ["11",1,2,"hi",[11]]
ary.grep(String) # => ["11", "hi"]
You may want to try Object#is_a? method:
ary = ["11", 1, 2, "hi", [11]]
ary.select{|e| e.is_a? String }
# Output
=> ["11", "hi"]
Can't do better than grep, but here's another:
ary.group_by(&:class)[String] # => ["11", "hi"]

Resources