I have a map where key (a string) can have a very variable number of characters and I'd like to print it intelligently:
MAP = {
"key1" => "value1",
"key2" => "value2",
}
would print:
key1 -> value1
key2 -> value2
and
MAP = {
"key1" => "value1",
"key2" => "value2",
"key3_dam_it_you_are_a_big_one_indeed" => "value3",
}
would print:
key1 -> value1
key2 -> value2
key3_dam_it_you_are_a_big_one_indeed -> value3
he idea is that key1 and key2 would change their print-line according to the size of key3.
Thanks!
What about
class Hash
def nice_print
max_key_length = keys.map(&:length).max
each { |key, value| puts "#{key.ljust(max_key_length)} -> #{value}" }
end
end
and you call
MAP.nice_print
Related
I am trying to find out if it is possible to sort a Hash by a specific value if there are multiple values saved to a key.
Example Code:
{
key1: ["Value1", "Value2", "Value3"],
key2: ["Value1", "Value2", "Value3"],
key3: ["Value1", "Value2", "Value3"]
}
I would like to be able to sort by the values in Value2 or by Value1 or any specific value.
If key1 and key2 have the same Value2 they will be returned as:
{Value2: [key1, key2]}
Example:
Value2 representes pets:
For all the Value2 that have dog, the key will be saved under a new key, dog.
For all the Value2 that have cat, it will gather all keys that have Value2 as cat and group it.
{cat: ["key1", "key2"], dog: ["key3"]}
I am working in Ruby and VScode. I do not want to use an array or nested array because this is not efficient.
Given
{
key1: ["Cop", "dog", "house"],
key2: ["doctor", "cat", "apartment"],
key3: ["Chef", "dog", "house"],
key4: ["Cop", "cat", "apartment"]
}
Expected Output if asked to sort by the values in value2
{
dog: [key1, key3],
cat: [key2, key4]
}
This doesn't appear to be about sorting, but rather grouping.
It's a matter of iterating through each pair and building a new hash of the matching keys and values.
# A Hash where the default value is a new empty array.
# See https://stackoverflow.com/questions/30367487/creating-a-hash-with-values-as-arrays-and-default-value-as-empty-array
grouped = Hash.new { |h, k| h[k] = [] }
# Iterate through the Hash
h.each { |key, things|
# Get the 2nd thing
thing = things[1]
# Add its key to the thing's group
grouped[thing] << key
}
p grouped
Using #each_with_object to build up a hash as we iterate over the keys in the original hash.
data = {
key1: ["Cop", "dog", "house"],
key2: ["doctor", "cat", "apartment"],
key3: ["Chef", "dog", "house"],
key4: ["Cop", "cat", "apartment"]
}
data.keys.each_with_object(Hash.new([])) { |k, h|
h[data[k][1]] += [k]
}
# => {"dog"=>[:key1, :key3], "cat"=>[:key2, :key4]}
Maybe not the optimal solution but it returns the expected result. The code is at least groups by value and includes the key, resulting in:
{"cat"=>[:key2, :key4], "dog"=>[:key1, :key3]}
hash = {
key1: ["Cop", "dog", "house"],
key2: ["doctor", "cat", "apartment"],
key3: ["Chef", "dog", "house"],
key4: ["Cop", "cat", "apartment"]
}
def group_by(animal, hash)
keys = hash.select{|key, value| value[1] == animal }.keys
{animal => keys}
end
a = group_by("cat", hash)
b = group_by("dog", hash)
a.merge(b)
I am sure there is a more "direct" solution.
I have a hash:
test = {
:key1 => "Test",
:key2 => "Test2",
:key3 => REF TO KEY1
}
Is it possible to let key3 refer to the key1 value?
Yes, this is easily possible. The expression for the value can be any arbitrary Ruby expression, including of course accessing a value from a Hash:
test = {
:key1 => "Test",
:key2 => "Test2",
}
test[:key3] = test[:key1]
This is really not recommended and my guess is that there is a better way to solve whatever larger problem you are attempting to solve with this technique.
But one way you could do this is by creating a Hash whose default_proc returns the value of :key1 if it is passed :key3.
> test = Hash.new { |h,k| k == :key3 ? h[:key1] : nil }
> test[:key1] = "Test"
> puts test[:key3]
Test
And this acts as a reference as can be seen if we modify the value of :key1
> test[:key1] = "Test2"
> puts test[:key3]
Test2
I have an hash that looks like this:
{
"key1": [
"value1",
"value2",
"value3"
],
"key2": [
"value1",
"value2",
"value3",
"value4",
"value5"
],
"key3": [
"value1"
],
"key4": [
"value1",
"value2"
]
}
How do I iterate through every keyN, while also looping through all the values in that key?
I have an array with all the keys if that helps.
Thanks
Pretty simple, really:
hash.each do |name, values|
values.each do |value|
# ...
end
end
You can do whatever you want with name and value at the lowest level.
If you are sure about the size of arrays, simply you can do like this,
ha = {:a => [1,2]}
ha.each do |k, (v1, v2)|
p k
p v1
p v2
end
Output
:a
1
2
You can do it like this:
hash.each do |key, array|
array.each do |value|
# do something
end
end
hash.each do |key_N, values_N|
values_N.each so |values|
.
.
#YourCode
.
.
end
end
Say I have the following hash.
my_hash = {
'array1' => %w[
value1
value2
],
'array2' => %w[
value3
value4
]
}
How do I make an array that looks like
my_array = %w[value1 value2 value3 valuu4]
my_array = my_hash.values.flatten
=> ["value1", "value2", "value3", "value4"]
Flatten Hash Values
Use Hash#values to collect the values from your Hash, and then use Array#flatten to turn the result into a single Array rather than one containing nested arrays. For example:
my_hash.values.flatten
#=> ["value1", "value2", "value3", "value4"]
Given I have hash:
Grp1:
key1: value1
key2: value2
key3: value3
Grp2:
key1: value4
key2: value2
key3: value5
and I have another hash:
key1: value4
key2: value2
I want to search in first set of hash and choose Grp2' key1:value4 because Grp2 matches the condition. How should I do that?
Note: I tried using the select command and tried some logic offered by referring documents. My intentions are not to get sample code but just a hint. Sorry if my question sounds like I am asking for code.
What you refer to as your first hash is not hash. In fact, it's not a Ruby object of any kind. If it is to be a hash, you need to write it like this:
g = { :Grp1 => { key1: value1, key2: value2, key3: value3 },
:Grp2 => { key1: value4, key2: value2, key3: value5 } }
Since the keys are all symbols, you could instead use the syntax:
g = { Grp1: { key1: value1, key2: value2, key3: value3 },
Grp2: { key1: value4, key2: value2, key3: value5 } }
value1 (and value2 and so on) must be either a variable or a method, but you have not given us it's value (if it's a variable) or its return value is (if it's a method), so I will replace those variables or methods with literals:
g = { Grp1: { key1: 7, key2: 4, key3: 'cat' },
Grp2: { key1: 1, key2: 3, key3: 'dog' } }
Your second hash:
h = { :key1 => value4, :key2 => value2 }
has the same problem, so I'll replace it with:
h = { :key1 => 1, :key2 => 3 }
which alternatively could be expressed:
h = { key1: 1, key2: 3 }
Assuming what I have written is correct, we can write a method as follows, using the methods Hash#keys, Hash#key?, Enumerable#find and Enumerable#all?:
def doit(g, h)
hkeys = h.keys
puts "hkeys=#{hkeys}"
g.find do |k,v|
puts "k=#{k}"
puts "v=#{v}"
hkeys.all? do |j|
puts " v.key?(#{j})=#{v.key?(j)}"
puts " v[#{j}]==#{h[j]}: #{v[j]==h[j]}"
v.key?(j) && v[j] == h[j]
end
end
end
I've added some puts statements so you can see the results of the calculations. For g and h defined above (with literal values):
doit(g,h)
hkeys=[:key1, :key2]
k=Grp1
v={:key1=>7, :key2=>4, :key3=>"cat"}
v.key?(key1)=true
v[key1]==1: false
k=Grp2
v={:key1=>1, :key2=>3, :key3=>"dog"}
v.key?(key1)=true
v[key1]==1: true
v.key?(key2)=true
v[key2]==3: true
#=> [:Grp2, {:key1=>1, :key2=>3, :key3=>"dog"}]
After stripping out the puts statements and making one small change, I would write the method like this:
def doit(g, h)
hkeys = h.keys
g.find { |_,v| hkeys.all? { |j| v.key?(j) && v[j] == h[j] } }
end
The small change is that I've replaced the block variable k with the variable _ to draw attention to the fact that I'm not using it in the block.
There are many ways to to write this method. Here's another, using the method Hash#values_at:
def doit(g, h)
hkeys = h.keys
hvalues = h.values
g.find { |_,v| v.values_at(*hkeys) == hvalues }
end