I would like to be able to quickly put the key-value pairs of a hash as arguments into a method.
For example, for the hash:
test_hash = {a: "1", b: "2"}
I would like to be able to pass it into a method like:
method(test_hash.do_something)
To achieve:
method(a: "1", b: "2")
you can use the double spat (**) on a hash in the method
method(**test_hash)
Related
I am currently working on a project with Ruby.
I don't know how to find the set of the value and key that has the same key as I gave with argument.
what I have tried is something like this below.
but it doesn't work.
def find_target(type)
target = type_list.find{|k,v| k == type}
target
end
def type_list
type = {
a: 1,
b: 2,
c: 3,
d: 4
}
type
end
but instead of giving an argument of variable, I gave a string as an argument, and it worked.
def find_target(a)
target = type_list.find{|k,v| k == a}
target
end
Edited
What I really want find_target to do is returning a matched value.
For example, when an argument is a, then it returns 1.
How can I solve this?
I would love you if you could help me.
Thank you .
I think one thing tripping you up is that your type_list hash is keyed with symbols (rather than strings or the value of a variable). The syntax you're using:
{a: 1}
is just shorthand for this:
{:a => 1}
Which means "A Hash with one key: the symbol :a with the value 1". That's distinct from:
{'a' => 1} # Keyed with the string 'a'
and this:
a = 'something'
b = {a => 1} # Keyed with value of 'a' at the time of creating, ie: {'something' => 1}. Note that if you change the value of a, the hash key won't change.
What do you expect as your return value from find_target(:a)? The find method on a Hash will return an Enumerator - mostly equivalent to a two-element Array, with the key and the value: {a: 1}.find{|k,v|k == :a} will return [:a, 1]. Is that what you want?
If you just want to have the value 1 returned, then you're really doing a hash lookup, and you don't need any extra methods at all. A common way to do this would be to define type_list as a constant, and then just refer to it by key:
TYPE_LIST = {
a: 1,
b: 2,
c: 3,
d: 4
}
#Then to find the type:
TYPE_LIST[:a] # Returns '1'
You might want to use a find_type method to handle the case where the key doesn't match a type: a plain Hash lookup will return nil, which you might not want.
Hope this helps put you on the right path, but I'm happy to expand this answer if needed!
I ran into a bit of an road block on a beginner exercise for hashes in Ruby. I have the following problem to solve:
Create a method call read_from_hash that takes in two parameters. The first parameter is a hash, the second is a key. Used together, they will either produce a value on that hash corresponding to the key, or nil by default. Use these two parameters in conjunction to do just that.
Here's my code:
def read_from_hash(hash, key)
hash = {key => "value"}
hash(key)
end
Here's the error:
Failure/Error: expect(read_from_hash({name: 'Steve'}, :name)).to eq('Steve')
ArgumentError:
wrong number of arguments (given 1, expected 0)
What you want is simply:
def read_from_hash(hash, key)
hash[key]
end
h = {a: 1, b: 2}
read_from_hash(h, :a)
#=> 1
read_from_hash(h, :c)
#=> nil
Or for your example:
read_from_hash({name: 'Steve'}, :name)
#=> 'Steve'
Your current code:
hash = {key => "value"}
creates a new hash variable, overwriting the one that's being passed in through the params, while here:
hash(key)
you're trying to access the value of the element with the key key using regular parentheses () instead of brackets []. Because of that, what is is actually happening is that you're calling a #hash method and passing it the key variable as a parameter.
I’m trying to get a better grasp on writing in Ruby and working with Hash tables and their values.
1. Say you have a hash:
‘FOO’= {‘baz’ => [1,2,3,4,5]}
Goal: convert each value into a string in the ‘Ruby’ way.
I’ve come across multiple examples of using .each eg.
FOO.each = { |k,v| FOO[k] = v.to_s }
However this renders an array encapsulated in a string. Eg. "[1,2,3,4,5]" where it should be ["1", "2", "3", "4", "5"].
2. When type casting is performed on a Hash that’s holds an array of values, is the result a new array? Or simply a change in type of value (eg. 1 becomes “1” when .to_s is applied (say the value was placed through a each enumerator like above).
An explanation is greatly appreciated. New to Ruby.
In the each block, k and v are the key value pair. In your case, 'baz' is key and [1,2,3,4,5] is value. Since you're doing v.to_s, it converts the whole array to string and not the individual values.
You can do something like this to achieve what you want.
foo = { 'baz' => [1,2,3,4,5] }
foo.each { |k, v| foo[k] = v.map(&:to_s) }
You can use Hash#transform_values:
foo = { 'baz' => [1, 2, 3, 4, 5] }
foo.transform_values { |v| v.map(&:to_s) } #=> {"baz"=>["1", "2", "3", "4", "5"]}
Question
How do I turn
arg2 = {a: "a", b: "b"}
method(arg1, arg2)
into this:
method(arg1, a: "a", b: "b")
Background
I'm trying to make a double in RSpec, which takes arguments like this:
let(:dummy_obj) do
[ double("my dummy object", name: "Mr. Jo", height: "10ft", etc) ]
end
The first arg is the name of the double object, after that you can add any number of key-value pairs which become the double's methods (e.g. dummy_obj.height() will return "10ft").
I have a huge JSON hash that I want to use, where each first-level field in the JSON becomes a method in the double. So, I was hoping there'd be something like *array that unpacks each item in the hash as it's own hash.
This:
method(arg1, a: "a", b: "b")
Is the same as this:
method(arg1, { a: "a", b: "b" })
Ruby lets you omit the curly braces ({}) when a hash is the last argument.
So your code already does exactly what you want:
arg2 = { a: "a", b: "b" }
method(arg1, arg2)
I found this works:
double("dummy thing", *my_hash.map {|h| {h[0] => h[1]}} )
But it feels a bit hacky, still open to other suggestions.
:)
I have an array:
["Melanie", "149", "Joe", "2", "16", "216", "Sarah"]
I want to create a hash:
{"Melanie"=>[149], "Joe"=>[2, 16, 216] "Sarah"=>nil}
How would I accomplish this when the keys and values are in the same array?
All values would be integers (although they are in string form in the array.) All keys start and end with a letter.
Your expected hash is invalid. Therefore, it is impossible to get what you wrote that you want.
From your issue, it looks reasonable to expect the values to be array. In that case, you can do it like this:
["Melanie", "149", "Joe", "2", "16", "216", "Sarah"]
.slice_before(/[a-z]/i).map{|k, *v| [k, v.map(&:to_i)]}.to_h
# => {"Melanie"=>[149], "Joe"=>[2, 16, 216], "Sarah"=>[]}
With little modification, you can let the value be a number instead of an array when the array length is one, but that is not a good design; it would introduce exceptions.
Try this
def numeric?(x)
x.chars.all? { |y| ('0'..'9').include?(y) }
end
array = ["Melanie", "149", "Joe", "2", "16", "216", "Sarah"]
keys = array.select { |x| not numeric?(x) }
map = {}
keys.each do |k|
from = array.index(k) + 1
to = array.index( keys[keys.index(k) + 1] )
map[k] = to ? array[from...to] : array[from..from]
end
p map
Output:
{"Melanie"=>["149"], "Joe"=>["2", "16", "216"], "Sarah"=>[]}
[Finished in 0.1s]
Here's another way:
arr = ["Melanie", "149", "Joe", "2", "16", "216", "Sarah"]
class String
def integer?
!!(self =~ /^-?\d+$/)
end
end
Hash[*arr.each_with_object([]) { |s,a| s.integer? ? a[-1] << s.to_i : a<<s<<[] }].
tap { |h| h.each_key { |k| h[k] = nil if h[k].empty? } }
#=> {"Melanie"=>[149], "Joe"=>[2, 16, 216], "Sarah"=>nil}
There are three components to your question, and I will try to answer them separately.
Regarding storing a multi-valued mapping, while there are specialized solutions available, the most common recommendation is just to store a hash whose values are arrays. That is, for your use case, your primary data structure is a hash whose keys are strings and whose values are arrays of integers. Depending on your desired behavior for duplicates etc., etc, you may wish to substitute a different data structure for the value structure, possibly a set.
Regarding identifying strings containing numbers and strings not containing numbers, well, that depends on exactly what your non-number-containing strings could instead contain, but a good starting point would be to perform a regular expression match for digits. You didn't specify whether your allowable numeric strings represented integers, floating points, etc. The particular answer to that may affect your overall strategy. Unfortunately, input parsing and validation is a complex and messy topic in the general case.
Regarding the actual conversion process, I would recommend the following strategy. Iterate through your input array. Check each string for whether it is numeric or non-numeric. If it is non-numeric, store that as the current key in a local. Also, in your hash, create a mapping from that key to a new empty array. If, instead, the string is numeric, convert it into a number, and add it to the array under the appropriate key.
I don't know if there's a pretty way to do it. I'd do something like this:
def numeric?(string)
# `!!` converts parsed number to `true`
!!Kernel.Float(string)
rescue TypeError, ArgumentError
false
end
def my_method(input_array)
# associate values with proper key and stores result in output
curr_key = nil
output = {}
input_array.each do |e|
if !numeric?(e)
output[e] = []
curr_key = e
else
# use Float if values may be floating-point
output[curr_key] << Integer(e, 10)
end
end
output.each do |k, v|
output[k] = v.empty? ? nil : v
end
output
end
Source for numeric method.