How to convert a string into a nested hash - ruby

I have a string foo.bar.baz with a value 'qux' that I want to convert to a nested hash like so:
{
foo: {
bar: {
baz: 'qux'
}
}
}
How would I do this in a way that would allow for different string lengths? e.g. foo.bar or foo.bar.baz.qux

First of all, you have to split the string. You can then use inject to build the structure. Because the hash is built from the inside out, we have to reverse the initial array:
'foo.bar.baz'.split('.') #=> ["foo", "bar", "baz"]
.reverse #=> ["baz", "bar", "foo"]
.inject('qux') { |memo, key| { key.to_sym => memo } }
#=> {:foo=>{:bar=>{:baz=>"qux"}}}
It might not be obvious how this works. On the first invocation, memo is 'qux' and key is 'baz'. The block turns this into {:baz=>'qux'} which then becomes the new memo.
Step by step:
memo key result
--------------------------------------------------------------
'qux' 'baz' {:baz=>'qux'}
{:baz=>'qux'} 'bar' {:bar=>{:baz=>'qux'}}
{:bar=>{:baz=>'qux'}} 'foo' {:foo=>{:bar=>{:baz=>'qux'}}}

Related

Convert string to symbol/keyword

We can convert strings to symbols in the following way:
"string_to_symbol".to_sym
# => :string_to_symbol
How do I convert strings in the new way of defining keywords? Expected outcome:
# => string_to_symbol:
I call keywords dynamically, and I end up using => to assign a value to it. I prefer not to do it that way to keep my code consistent.
No, there isn't.
It's important to note that these two lines do exactly the same:
{ foo: 'bar' } #=> {:foo=>"bar"}
{ :foo => 'bar' } #=> {:foo=>"bar"}
This is because the first form is only Syntactic sugar for creating a Hash using a Symbol as the Key. (and not "the new way of defining keyword in ruby")
If you want to use other types as the key, you still have to use the "hashrocket" (=>):
{ 'key' => 'val' } #=> {"key"=>"val"}
{ 0 => 'val' } #=> {0=>"val"}
Edit:
As #sawa noted in the comments, the question is about passing keyword arguments, not Hashes. Which is technically correct, but boils down to exactly the same (as long as it's Hashes with Symbols as keys:
def foo(bar: 'baz')
puts bar
end
h = {
:bar => 'Ello!'
}
foo(h)
# >> Ello!

Using ranges as keys in Ruby

I have a hash table that uses ranges as keys.
hash = {
1..10 => "Foo",
11..20 => "Bar",
21..30 => "Baz",
31..40 => "Quux",
}
hash.find {|key, value| key == 5} # => `nil`
Why doesn't it return Foo?
EDIT:
As pointed out below, changed Hash to hash
With == you check for a real equality and 5 is no range. But you may use === or include?. You may also try select instead find.
Example:
hash = {
1..10 => "Foo",
11..20 => "Bar",
21..30 => "Baz",
31..40 => "Quux",
}
p hash.find {|key, value| key === 5} #[1..10, "Foo"]
p hash.find {|key, value| key.include?(5)} #[1..10, "Foo"]
p hash.select{|key, value| key === 5} #{1..10=>"Foo"}
p hash.select{|key, value| key.include?(5)}#{1..10=>"Foo"}
Please see the different results. find returns an array, `select a Hash.
A closing remark: You used Hash = .... I hope this a a typo and you wanted to use hash.
case when constructions are designed to do this.
x = 5
p case x
when 1..10 then "Foo"
when 11..20 then "Bar"
when 21..30 then "Baz"
when 31..40 then "Quux"
end
# => "Foo"
For your specific example, you could use
Hash[(k-1)/10]
For example, if k = 15:
Hash[(15-1)/10] => Hash[1] => "Bar"
For the general case, if speed is important, first construct another hash:
H=Hash.flat_map { |r,v| r.to_a.product([v]) }.to_h
#=> { 1=>"Foo" , 2=>"Foo" ,..., 10=>"Foo",
# 11=>"Bar" , 12=>"Bar" ,..., 20=>"Bar",
# ...
# 31=>"Quux", 32=>"Quux",..., 40=>"Quux"}
so you can then just lookup values:
H[15] #=> "Bar"
H[35] #=> "Quux"

How to get index of value in anonymous array inside of iteration

I would like to be able to take an anonymous array, iterate through it and inside of the iterator block find out what the index is of the current element.
For instance, I am trying to output only every third element.
["foo", "bar", "baz", "bang", "bamph", "foobar", "Hello, Sailor!"].each do |elem|
if index_of(elem) % 3 == 0 then
puts elem
end
end
(where index_of is a nonexistent method being used as a placeholder here to demonstrate what I'm trying to do)
In theory the output should be:
foo
bang
Hello, Sailor!
This is pretty straightforward when I'm naming the array. But when it is anonymous, I can't very well refer to the array by name. I've tried using self.find_index(elem) as well as self.index(elem) but both fail with the error: NoMethodError: undefined method '(find_)index' for main:Object
What is the proper way to do this?
Use each_with_index:
arr = ["foo", "bar", "baz", "bang", "bamph", "foobar", "Hello, Sailor!"]
arr.each_with_index do |elem, index|
puts elem if index % 3 == 0
end
Another way:
arr = ["foo", "bar", "baz", "bang", "bamph", "foobar", "Hello, Sailor!"]
arr.each_slice(3) { |a| puts a.first }
#=> foo
# bang
# Hello, Sailor!

Is there a quick and easy way to create a checksum from Ruby's basic data structures?

I have a data structure (Hash) that looks something like this:
{
foo: "Test string",
bar: [475934759, 5619827847]
}
I'm trying to create a checksum from that Hash to check for equality in the future. I tried using the hash method of the Hash, which resulted in a satisfyingly nice-looking hash, but it turns out that the same Hash will produce a different hash after the interpreter has been restarted.
I really just want to be able to create a ~128 bit checksum from a Hash, String or Array instance.
Is this possible?
You could calculate your own hash based on the object's Marshal dump or JSON representation.
This calculates the MD5 hash of a Marshal dump:
require 'digest/md5'
hash = {
foo: "Test string",
bar: [475934759, 5619827847]
}
Marshal::dump(hash)
#=> "\x04\b{\a:\bfooI\"\x10Test string\x06:\x06ET:\bbar[\ai\x04'0^\x1Cl+\b\x87\xC4\xF7N\x01\x00"
Digest::MD5.hexdigest(Marshal::dump(hash))
#=> "1b6308abdd8f5f6290e2825a078a1a02"
Update
You can implement your own strategy, although I would not recommend to change core functionality:
class Hash
def _dump(depth)
# this doesn't cause a recursion because sort returns an array
Marshal::dump(self.sort, depth)
end
def self._load(marshaled_hash)
Hash[Marshal::load(marshaled_hash)]
end
end
Marshal::dump({foo:1, bar:2})
#=> "\x04\bu:\tHash\e\x04\b[\a[\a:\bbari\a[\a:\bfooi\x06"
Marshal::dump({bar:2, foo:1})
#=> "\x04\bu:\tHash\e\x04\b[\a[\a:\bbari\a[\a:\bfooi\x06"
Marshal::load(Marshal::dump({foo:1, bar:2}))
#=> {:bar=>2, :foo=>1}
To build on #Stefan's answer above, if order of the hash is important, sort the output before pushing it through Mashall.
require 'digest/md5'
hash = {
'foo'=> "Test string",
'bar'=> [475934759, 5619827847]
}
puts Digest::MD5.hexdigest(Marshal::dump(hash.collect{|k,v| [k,v]}.sort{|a,b| a[0] <=> b[0]}))
# 8509c564c0ae8dcb6c2b9b564ba6a03f
hash = {
'bar'=> [475934759, 5619827847],
'foo'=> "Test string"
}
puts Digest::MD5.hexdigest(Marshal::dump(hash.collect{|k,v| [k,v]}.sort{|a,b| a[0] <=> b[0]}))
# 8509c564c0ae8dcb6c2b9b564ba6a03f
If you need to generate the checksum for the content of the hash, whatever the order of the data, using Marshal or sort or other techniques won't work.
The only solid way I found so far is the following:
require 'digest/md5'
hash1 = { "a" => 1, "b" => "2", c: { d: "3" } }
hash2 = { c: { d: "3" }, "a" => 1, "b" => "2" }
Digest::MD5.hexdigest(Marshal.dump(hash1)) # => "5def3b2cbdddd3aa6730b6d0527c2d79"
Digest::MD5.hexdigest(Marshal.dump(hash2)) # => "8155698ccfb05b8db01490e9b9634fd9"
Digest::MD5.hexdigest(hash1.to_s.chars.sort.join) # => "812bb65d65380fc1e620a9596806cc35"
Digest::MD5.hexdigest(hash2.to_s.chars.sort.join) # => "812bb65d65380fc1e620a9596806cc35"

What are :+ and &:+ in Ruby?

I've seen these several times but I can't figure out how to use them. The pickaxe says that these are special shortcuts but I wasn't able to find the syntactical description.
I've seen them in such contexts:
[1,2,3].inject(:+)
to calculate sum for example.
Let's start with an easier example.
Say we have an array of strings we want to have in caps:
['foo', 'bar', 'blah'].map { |e| e.upcase }
# => ['FOO', 'BAR', 'BLAH']
Also, you can create so called Proc objects (closures):
block = proc { |e| e.upcase }
block.call("foo") # => "FOO"
You can pass such a proc to a method with the & syntax:
block = proc { |e| e.upcase }
['foo', 'bar', 'blah'].map(&block)
# => ['FOO', 'BAR', 'BLAH']
What this does, is call to_proc on block and then calls that for every block:
some_object = Object.new
def some_object.to_proc
proc { |e| e.upcase }
end
['foo', 'bar', 'blah'].map(&some_object)
# => ['FOO', 'BAR', 'BLAH']
Now, Rails first added the to_proc method to Symbol, which later has been added to the ruby core library:
:whatever.to_proc # => proc { |e| e.whatever }
Therefore you can do this:
['foo', 'bar', 'blah'].map(&:upcase)
# => ['FOO', 'BAR', 'BLAH']
Also, Symbol#to_proc is even smarter, as it actually does the following:
:whatever.to_proc # => proc { |obj, *args| obj.send(:whatever, *args) }
This means that
[1, 2, 3].inject(&:+)
equals
[1, 2, 3].inject { |a, b| a + b }
inject accepts a symbol as a parameter, this symbol must be the name of a method or operator, which is this case is :+
so [1,2,3].inject(:+) is passing each value to the method specified by the symbol, hence summing all elements in the array.
source: https://ruby-doc.org/core-2.5.1/Enumerable.html

Resources