Exposing key/value pair in code block parameters - ruby

Is it possible to expose the key/value pair from an array of hashes in the parameter of the block?
array = [{:a=>'a'}, {:b=>'b'}] # an array of hashes
array.each {|key, value| puts "#{key},#{value}"}
array.map {|key, value| "(#{key},#{value})"}
array.inject([]) {|accum, (key,value)| key == :a ? value : accum}
Currently the results of the code block parameters |key, value| are (hash, nil)
I would like to get (symbol, string) in the declaration of the |key,value| parameters. Is this possible or am I stuck having to pass in a hash and extracting the key/value pair myself?
I know that passing a hash instead of an array will automatically give access to the key/value pair, but much of Ruby returns arrays.
UPDATE: It seems possible with arrays, but not hashes?
a = [['a','b'],['c','d']]
a.each {|(x,y)| puts "#{x}=>#{y}"} # => a=>b
# => c=>d
a.each {|x| p x} # => ["a", "b"]
# => ["c", "d"]

Ok.. If I catch you wright :
array = [{:a=>'a'}, {:b=>'b'}]
array.map {|k,v| [k,v]}
# => [[{:a=>"a"}, nil], [{:b=>"b"}, nil]]
you are getting above. But you want [[:a,"a"], [:b,"b"]] . No It is not possible. With #each or #map, when you have array of hashes like you given,you can't assign the block parameter k,v as :a,"a" and :b,"b" with each pass.
More simply try on your IRB,you will get to see the effect:
k,v = {:a=>'a'}
p k,v
# >> {:a=>"a"}
# >> nil

Related

How to print only the keys which have values?

I have a hash with keys and values as follows:
hash = {"lili" => [] , "john" => ["a", "b"], "andrew" => [], "megh" => ["b","e"]}
As we can see some of the keys have values as empty arrays.
Some keys have array values where there are actual values in the array.
I want to loop over the hash and generate a new hash that includes only those keys which have values in their arrays (not the ones which have empty arrays). How can I do that?
The title says:
PRINT only the keys, but reading the post you are trying to generate a hash subset given a hash.
The solution for the TITLE of the POST:
hash.each {|k,v| p k unless v.empty? }
If you want to generate a new hash subset give the original hash:
hash.reject { |k,v| v.nil? || v.empty? }
If you want to PRINT the subset generated give the original hash:
hash.reject { |k,v| v.nil? || v.empty? }.each { |k,v| p k }
You could use reject to filter out those elements in your hash where the value is an empty array or the value is nil and then iterate to print their content;
{:lili=>[], :john=>[:a, :b], :andrew=>[], :megh=>[:b, :e], :other=>nil}
.reject { (_2 || []).empty? }
.each { puts "#{_1} has the value(s) #{_2}" }
that prints
john has the value(s) [:a, :b]
megh has the value(s) [:b, :e]
Unsure of your desired output or exact data structure, but you can simply use reject along with empty? to remove any hash value that contains an empty array:
hash = {"lili" => [] , "john" => ["a", "b"], "andrew" => [], "megh" => ["b","e"]}
hash.reject {|k, v| v.empty?}
#=> {"john"=>["a", "b"], "megh"=>["b", "e"]}
It should however be noted that this approach will not work if any hash values are nil. To address that situation, I would recommend either using compact to remove any hash elements with nil values prior to using reject OR by testing for either nil? or empty? (and in that order):
hash = {"lili" => [] , "john" => ["a", "b"], "andrew" => [], "megh" => ["b","e"], "other" => nil}
hash.reject {|k, v| v.empty?}
#=> Evaluation Error: Ruby NoMethodError: undefined method `empty?' for nil:NilClass
hash.compact.reject {|k, v| v.empty?}
#=> {"john"=>["a", "b"], "megh"=>["b", "e"]}
hash.reject {|k, v| v.empty? or v.nil?}
#=> Evaluation Error: Ruby NoMethodError: undefined method `empty?' for nil:NilClass
hash.reject {|k, v| v.nil? or v.empty?}
#=> {"john"=>["a", "b"], "megh"=>["b", "e"]}
hash.reject {|k, v| v.empty? || v.nil?}
#=> Evaluation Error: Ruby NoMethodError: undefined method `empty?' for nil:NilClass
hash.reject {|k, v| v.nil? || v.empty?}
#=> {"john"=>["a", "b"], "megh"=>["b", "e"]}

Immutable alternative to `delete` in Ruby

Is there a version of Hash#delete as below:
hash = {a: 1}
hash.delete(:a) # => 1
hash # => {}
that returns a hash without :a, without mutating the original hash so that it would have its original value?
Use Hash#reject.
hash.reject { |k,_| k == :a }
#=> {}
hash
#=> {:a=>1}
This of course does not depend on the hash having a single key-value pair.

Hash with array as key

I'm defining a hash with an array as a key and another array as its value. For example:
for_example = {[0,1] => [:a, :b, :c]}
Everything is as expected below.
my_hash = Hash.new([])
an_array_as_key = [4,2]
my_hash[an_array_as_key] #=> []
my_hash[an_array_as_key] << "the" #=> ["the"]
my_hash[an_array_as_key] << "universal" #=> ["the", "universal"]
my_hash[an_array_as_key] << "answer" #=> ["the", "universal", "answer"]
But if I try to access the keys:
my_hash #=> {}
my_hash.keys #=> []
my_hash.count #=> 0
my_hash.values #=> []
my_hash.fetch(an_array_as_key) # KeyError: key not found: [4, 2]
my_hash.has_key?(an_array_as_key) #=> false
Rehash doesn't help:
my_hash #=> {}
my_hash.rehash #=> {}
my_hash.keys #=> []
But the values are saved:
my_hash[an_array_as_key] #=> ["the", "universal", "answer"]
Am I missing something?
To understand this, You need to understand the difference between Hash::new and Hash::new(ob). Suppose you define a hash object using Hash::new or hash literal {}. Now whenever you will write a code hsh[any_key], there is two kind of output may be seen, if any_key don't exist, then default value nil will be returned,otherwise whatever value is associated with the key will be returned. The same explanation will be applicable if you create any Hash object using Hash.new.
Now Hash.new(ob) is same as Hash.new, with one difference is, you can set any default value you want, for non existent keys of that hash object.
my_hash = Hash.new([])
my_hash[2] # => []
my_hash[2].object_id # => 83664630
my_hash[4] # => []
my_hash[4].object_id # => 83664630
my_hash[3] << 4 # => [4]
my_hash[3] # => [4]
my_hash[3].object_id # => 83664630
my_hash[5] << 8 # => [4, 8]
my_hash[5] # => [4, 8]
my_hash[5].object_id # => 83664630
Now see in the above example my_hash has no keys like 2,3 and 4. But the object_id proved that, all key access results in to return the same array object. my_hash[2] is not adding the key to the hash my_hash, rather trying to access the value of the key 2 if that key exist, otherwise it is returning the default value of my_hash. Remember all lines like my_hash[2],my_hash[3] etc is nothing but a call to Hash#[] method.
But there is a third way to go, may be you are looking for, which is Hash::new {|hash, key| block }.With this style you can add key to the hash object if that key doesn't exist, with a default value of same class instance,but not the same instance., while you are doing actually Hash#[] method call.
my_hash = Hash.new { |hash, key| hash[key] = []}
my_hash[2] # => []
my_hash[2].object_id # => 76312700
my_hash[3] # => []
my_hash[3].object_id # => 76312060
my_hash.keys # => [2, 3]

How to merge array index values and create a hash

I'm trying to convert an array into a hash by using some matching. Before converting the array into a hash, I want to merge the values like this
"Desc,X1XXSC,C,CCCC4524,xxxs,xswd"
and create a hash from it. The rule is that, first value of the array is the key in Hash, in array there are repeating keys, for those keys I need to merge values and place it under one key. "Desc:" are keys. My program looks like this.
p 'test sample application'
str = "Desc:X1:C:CCCC:Desc:XXSC:xxxs:xswd:C:4524"
arr = Array.new
arr = str.split(":")
p arr
test_hash = Hash[*arr]
p test_hash
I could not find a way to figure it out. If any one can guide me, It will be thankful.
Functional approach with Facets:
require 'facets'
str.split(":").each_slice(2).map_by { |k, v| [k, v] }.mash { |k, vs| [k, vs.join] }
#=> {"Desc"=>"X1XXSC", "C"=>"CCCC4524", "xxxs"=>"xswd"}
Not that you cannot do it without Facets, but it's longer because of some basic abstractions missing in the core:
Hash[str.split(":").each_slice(2).group_by(&:first).map { |k, gs| [k, gs.map(&:last).join] }]
#=> {"Desc"=>"X1XXSC", "C"=>"CCCC4524", "xxxs"=>"xswd"}
A small variation on #Sergio Tulentsev's solution:
str = "Desc:X1:C:CCCC:Desc:XXSC:xxxs:xswd:C:4524"
str.split(':').each_slice(2).each_with_object(Hash.new{""}){|(k,v),h| h[k] += v}
# => {"Desc"=>"X1XXSC", "C"=>"CCCC4524", "xxxs"=>"xswd"}
str.split(':') results in an array; there is no need for initializing with arr = Array.new
each_slice(2) feeds the elements of this array two by two to a block or to the method following it, like in this case.
each_with_object takes those two elements (as an array) and passes them on to a block, together with an object, specified by:
(Hash.new{""}) This object is an empty Hash with special behaviour: when a key is not found then it will respond with a value of "" (instead of the usual nil).
{|(k,v),h| h[k] += v} This is the block of code which does all the work. It takes the array with the two elements and deconstructs it into two strings, assigned to k and v; the special hash is assigned to h. h[k] asks the hash for the value of key "Desc". It responds with "", to which "X1" is added. This is repeated until all elements are processed.
I believe you're looking for each_slice and each_with_object here
str = "Desc:X1:C:CCCC:Desc:XXSC:xxxs:xswd:C:4524"
hash = str.split(':').each_slice(2).each_with_object({}) do |(key, value), memo|
memo[key] ||= ''
memo[key] += value
end
hash # => {"Desc"=>"X1XXSC", "C"=>"CCCC4524", "xxxs"=>"xswd"}
Enumerable#slice_before is a good way to go.
str = "Desc:X1:C:CCCC:Desc:XXSC:xxxs:xswd:C:4524"
a = ["Desc","C","xxxs"] # collect the keys in a separate collection.
str.split(":").slice_before(""){|i| a.include? i}
# => [["Desc", "X1"], ["C", "CCCC"], ["Desc", "XXSC"], ["xxxs", "xswd"], ["C", "4524"]]
hsh = str.split(":").slice_before(""){|i| a.include? i}.each_with_object(Hash.new("")) do |i,h|
h[i[0]] += i[1]
end
hsh
# => {"Desc"=>"X1XXSC", "C"=>"CCCC4524", "xxxs"=>"xswd"}

Iterate hash for specific range

How to pass range in hash to iterate from index 1 to last?
h = {}
h[1..-1].each_pair do |key,value|
puts "#{key} = #{value}
end
This code returning error. how may i pass range in hash ??
EDIT:
I want to print first key and value without any calculations.
From second key and value i want to do some calculation on my hash.
For that i have written this code ...
store_content_in_hash containing key and values.
first_key_value = store_content_in_hash.shift
f.puts first_key_value[1]
f.puts
store_content_in_hash.each_pair do |key,value|
store_content_in_hash[key].sort.each {|v| f.puts v }
f.puts
end
Any better way to solve out this problem ??
In Ruby 1.9 only:
Given a hash:
h = { :a => :b, :c => :d, :e => :f }
Go Like this:
Hash[Array(h)[1..-1]].each_pair do |key, value|
# ...
end
This will iterate through the following hash { :c => :d, :e => f } as the first key/value pair is excluded by the range.
Hashes have no concept of order. There is no such thing as the first or second element in a hash.
So you can't do what you want with hashes.
Hash is not about the ranges. It's about key value pairs. In ruby 1.8 hash is unordered hence you can't be sure in which order the keys and values will be iterated over which makes "range" thing obsolete. And I believe that you're doing something wrong (tm) in this situation. Can you elaborate on your problem?
On the other note you're getting an error because square brackets in Hash instance accepts keys. So if your hash does not contain 1..-1 as a key - you will get nil value and nil does not respond to each_pair. Try this to get a hold on this:
h = {(1..-1) => {:foo => :bar}}
h[1..-1].each_pair do |key,value|
puts "#{key} = #{value}"
end
As others have pointed out, Hashes are not about order. It's true that 1.9 hashes are ordered, but that's just a convenience, not their primary feature.
If the order is important to you, just use arrays instead of hashes. In your case, an array of pairs (two-element arrays) seems to fit the purpose. And if you need to access it by key, you can always easily convert it to a hash using Hash#[] method.

Resources