Joining an array of keys to a hash with key value pairs like excel vlookup - ruby

I've got an unsorted array of keys like this:
keys = ["ccc", "ddd", "ggg", "aaa", "bbb"]
and a hash
hash = {"ddd" => 4, "aaa" => 1, "bbb" => 2, "eee" => 5, "fff" => 6}
I'd like to join these two data structures to return a hash in the original order of keys to the first keys:
{"ccc" => nil, "ddd" => 4, "ggg" => nil, "aaa" => 1, "bbb" => 2}
Items NOT in the hash (like "ggg") should return nil.
This is analogous to the "v-lookup" function in excel.
this is in ruby. Thanks!

Cryptic:
Hash[keys.zip(hash.values_at *keys)]
Or a bit longer, a bit less cryptic:
keys.map.with_object({}) {|key, memo| memo[key] = hash[key]}

Related

Filtering when adding elements by id

Tell me how to do this, where you can read about it, because I do not understand at all how to implement it. Thanks.
def initialize
#arr = []
end
def items(init)
arrfinish = init - #arr
#arr = (#arr + init).uniq
yield arrfinish
end
def idefine(find_text)
end
The class has a method(items) that connects arrays by removing duplicate elements. I need to make sure that the idefine
method receives the key by which filtering will be performed when adding new elements, I will give an example below.
app_handler.idefine('id')
app_handler.items([{'id' => 1}, {'id' => 1, 'test_key' => 'Some data'}, {'id' => 2}])
From this example, the second element with id = 1 should be ignored.
Ok, putting aside the class definition, what I understand of the question is:
In an array of hashes, remove the hashes that contain a duplicate value of a given key.
The following function filter the hashes and copy the content of the selected ones in a new array:
require 'set'
def no_dup_val key, arr
previous_values = Set[]
arr.each_with_object([]) do |hash,result|
next unless hash.has_key?(key)
next if previous_values.include?(hash[key])
previous_values << hash[key]
result << hash.dup
end
end
Which gives you:
no_dup_val 'id', [{'id' => 1}, {'id' => 1, 'key' => 'data'}, {'id' => 2}, {'stock' => 3}, {'e-stock'=>0}]
#=> [{"id"=>1}, {"id"=>2}]
Note that the hashes that don't contain the key are also removed, that's my choice, which leads to the following questions:
What happens when the key is not present in a hash?
What happens when the item function is called more than once? Do you take into account the hashes already in #arr?
What happens when you call idefine with a new key? Do you filter the existing elements of #arr with the new key?
As you can see, you need to be a little more specific about what you want to do.
Update
If you don't care about copying the contents of the hashes then these may fit your needs.
Hashes without the id key are removed:
def no_dup_val key, arr
arr.filter{ |h| h.has_key?(key) }.uniq{ |h| h[key] }
end
no_dup_val 'id', [{'id' => 1}, {'id' => 1, 'key' => 'data'}, {'id' => 2}, {'stock' => 3}, {'e-stock'=>0}]
#=> [{"id"=>1}, {"id"=>2}]
Hashes without the id key are treated as having "id" => nil (so the first will be kept):
def no_dup_val key, arr
arr.uniq{ |h| h[key] }
end
no_dup_val 'id', [{'id' => 1}, {'id' => 1, 'key' => 'data'}, {'id' => 2}, {'stock' => 3}, {'e-stock'=>0}]
#=> [{"id"=>1}, {"id"=>2}, {"stock"=>3}]
All the hashes without the id key are kept:

Merge hashes with same key value pair inside array

How to merge hashes within array with same key, value pair? I have following array,
arry = [{"id" => 1, "val1" => 123},
{"id" => 2, "val1" => 234},
{"id" => 1, "val2" => 321},
{"id" => 1, "val3" => 445},
{"id" => 3, "val3" => 334}]
Want to get
arry = [{"id" => 1, "val1" => 123, "val2" => 321, "val3" => 445},
{"id" => 2, "val1" => 234},
{"id" => 3, "val3" => 334}]
is there ruby way to do it? Tried few ways but no success so far.
The arry you have posted is not a valid ruby array in the first place (I fixed it in my edit.)
arry.
group_by { |h| h["id"] }.
values.
map { |a| a.reduce(&:merge) }
#⇒ [{"id"=>1, "val1"=>123, "val2"=>321, "val3"=>445},
# {"id"=>2, "val1"=>234}, {"id"=>3, "val3"=>334}]
If your input might have same keys within the same "id" (like {"id" => 1, "val1" => 123}, {"id" => 1, "val1" => 456},) you are to decide how to merge them. In any case, Hash#merge with a block would be your friend there.
arry.each_with_object({}) { |g,h| h.update(g["id"]=>g) { |_,o,n| o.merge(n) } }.values
#=> [{"id"=>1, "val1"=>123, "val2"=>321, "val3"=>445},
# {"id"=>2, "val1"=>234},
# {"id"=>3, "val3"=>334}]
Note the receiver of values is:
{1=>{"id"=>1, "val1"=>123, "val2"=>321, "val3"=>445},
2=>{"id"=>2, "val1"=>234},
3=>{"id"=>3, "val3"=>334}}
This uses the form of Hash#update (aka merge) that employs the block { |_,o,n| o.merge(n) } to determine the values of keys that are present in both hashes being merged. See the doc for descriptions of the three block variables. (I've used an underscore for the first, the common key, to signify that it is not used in the block calculation.)
This should work too,
arry.group_by { |a| a['id'] }.map{|_, ar| ar.reduce(:merge)}
This would return,
[{"id"=>1, "val1"=>123, "val2"=>321, "val3"=>445}, {"id"=>2, "val1"=>234}, {"id"=>3, "val3"=>334}]

Delete element from nested hash in Ruby

I have a two dimensional hashes in Ruby.
h = { "a" => {"v1" => 0, "v2" => 1}, "c" => {"v1" => 2, "v2" => 3} }
I would like to delete those elements from the hash, where value 1 (v1) is 0, for example, so my result would be:
{ "c" => {"v1" => 2, "v2" => 3} }
I wanted to achieve this by iterating trough the hash with delete_if, but I'm not sure how to handle the nested parts with it.
Is that what you're looking for?
h.delete_if { |_, v| v['v1'].zero? }
#=> {"c" => {"v1" => 2, "v2" => 3}}
As #TomLord says, it also may be variant, when v1 can be not defined or equal to nil, in this case, it would be better to use v['v1'] == 0
You can use Hash#value? in your block to check if any of the values in the nested hashes equal 0:
hash.delete_if { |k,v| v.value? 0 } #=> { "c" => { "v1" => 2, "v2" => 3 } }

Merging keys and values from hash in Ruby

I have a hash with the number of occurrences of cities from a model and I want to merge nil or blank keys and values into one and rename the new single key
for example
#raw_data = {nil => 2, "" => 2, "Berlin" => 1, "Paris" => 1}
# to
#corrected_data = {"undefined" => 4, "Berlin" => 1, "Paris" => 1}
I have looked into the merge method from the Rubydoc but it only merges if two keys are similar, but in this case nil and "" are not the same.
I could also create a new hash and add if nil or blank statements and add the values to a new undefined key, but it seems tedious.
I'd do :
#raw_data = {nil => 2, "" => 2, "Berlin" => 1, "Paris" => 1}
key_to_merge = [nil, ""]
#correct_data = #raw_data.each_with_object(Hash.new(0)) do |(k,v), out_hash|
next out_hash['undefined'] += v if key_to_merge.include? k
out_hash[k] = v
end
#correct_data # => {"undefined"=>4, "Berlin"=>1, "Paris"=>1}
Ruby cannot automatically determine what do you want to do with values represented by two "similar" keys. You have to define the mapping yourself. One approach would be:
def undefined?(key)
# write your definition of undefined here
key.nil? || key.length == 0
end
def merge_value(existing_value, new_value)
return new_value if existing_value.nil?
# your example seemed to add values
existing_value + new_value
end
def corrected_key(key)
return "undefined" if undefined?(key)
key
end
#raw_data = {nil => 2, "" => 2, "Berlin" => 1, "Paris" => 1}
#raw_data.reduce({}) do |acc, h|
k, v = h
acc[corrected_key(k)] = merge_value(acc[corrected_key(k)], v)
acc
end
# {"undefined" => 4, "Berlin" => 1, "Paris" => 1}

How to add new item to hash

I don't know how to add new item to already existing hash. For example, first I construct hash:
hash = {item1: 1}
After that, I want to add item2, so after this I have hash like this:
{item1: 1, item2: 2}
I don't know what method to do on hash. Could someone help me?
Create the hash:
hash = {:item1 => 1}
Add a new item to it:
hash[:item2] = 2
If you want to add new items from another hash - use merge method:
hash = {:item1 => 1}
another_hash = {:item2 => 2, :item3 => 3}
hash.merge(another_hash) # {:item1=>1, :item2=>2, :item3=>3}
In your specific case it could be:
hash = {:item1 => 1}
hash.merge({:item2 => 2}) # {:item1=>1, :item2=>2}
but it's not wise to use it when you should to add just one element more.
Pay attention that merge will replace the values with the existing keys:
hash = {:item1 => 1}
hash.merge({:item1 => 2}) # {:item1=>2}
exactly like hash[:item1] = 2
Also you should pay attention that merge method (of course) doesn't effect the original value of hash variable - it returns a new merged hash. If you want to replace the value of the hash variable then use merge! instead:
hash = {:item1 => 1}
hash.merge!({:item2 => 2})
# now hash == {:item1=>1, :item2=>2}
hash.store(key, value) - Stores a key-value pair in hash.
Example:
hash #=> {"a"=>9, "b"=>200, "c"=>4}
hash.store("d", 42) #=> 42
hash #=> {"a"=>9, "b"=>200, "c"=>4, "d"=>42}
Documentation
It's as simple as:
irb(main):001:0> hash = {:item1 => 1}
=> {:item1=>1}
irb(main):002:0> hash[:item2] = 2
=> 2
irb(main):003:0> hash
=> {:item1=>1, :item2=>2}
hash[key]=value
Associates the value given by value with the key given by key.
hash[:newKey] = "newValue"
From Ruby documentation:
http://www.tutorialspoint.com/ruby/ruby_hashes.htm
hash_items = {:item => 1}
puts hash_items
#hash_items will give you {:item => 1}
hash_items.merge!({:item => 2})
puts hash_items
#hash_items will give you {:item => 1, :item => 2}
hash_items.merge({:item => 2})
puts hash_items
#hash_items will give you {:item => 1, :item => 2}, but the original variable will be the same old one.
Create hash as:
h = Hash.new
=> {}
Now insert into hash as:
h = Hash["one" => 1]

Resources