how can i verify if any or all elements are empty? - ruby-on-rails

I have a hash, i want to know if any param does not exist or is empty
how is does not exist? in this case does not exist the param 'b'
{"a"=>"first", "c"=>"5"}
and empty element is like this: "a"=>""
{"a"=>"", "b"=>"b", "c"=>"5"}
this is my attempt:
array.any?{|_,i| p i.blank?}
output:
if there is (all or any empty elements) then return true
[true, false] output true
[true, false,true] output true
[true, true] output true
[true, true, true] output true
[false, false] output false
[false, false, false] output false

h = { "a" => "", "c" => "5" }
keys = h.keys
missed_keys = [*keys.first..keys.last] - keys
puts 'Missed Keys'
p missed_keys
puts 'Keys which are having empty values'
p h.filter_map { |k, v| k if v.empty? }
Output
Missed Keys
["b"]
Keys which are having empty values
["a"]
update:
To only return true or false.
p h.any? { |k, v| v.empty? }
output
true

I think Ruby has blank? function to do this
hash = {"a": 1, "c": ""}
def is_missing(hash, key)
hash[key.to_sym].blank?
end
is_missing(hash, "a") => false
is_missing(hash, "b") => true
is_missing(hash, "c") => true

Related

How to search nested hash of arrays and arrays of hash and only return matching object from the parent node?

Say I have the below ruby hash nested
hash_or_array = [{
"book1" => "buyer1",
"book2" => {
"book21" => "buyer21", "book22" => ["buyer23", "buyer24", true]
},
"book3" => {
"0" => "buyer31", "1" => "buyer32", "2" => "buyer33",
"3" => [{
"4" => "buyer34",
"5" => [10, 11],
"6" => [{
"7" => "buyer35"
}]
}]
},
"book4" => ["buyer41", "buyer42", "buyer43"],
"book5" => {
"book5,1" => "buyer5"
}
}]
And I want to search for a string that matches buyer35. On match, I want it to return the following result
"book3" => {
"3" => [{
"6" => [{
"7" => "buyer35"
}]
}]
}]
All, other non matching keys,values, arrays should be omitted. I have the following example, but it doesn't quite work
def search(hash)
hash.each_with_object({}) do |(key, value), obj|
if value.is_a?(Hash)
returned_hash = search(value)
obj[key] = returned_hash unless returned_hash.empty?
elsif value.is_a?(Array)
obj[key] = value if value.any? { |v| matches(v) }
elsif matches(key) || matches(value)
obj[key] = value
end
end
end
def matches(str)
match_criteria = /#{Regexp.escape("buyer35")}/i
(str.is_a?(String) || str == true || str == false) && str.to_s.match?(match_criteria)
end
....
=> search(hash_or_array)
Any help is appreciated. I realize, I need to use recursion, but can't quite figure how to build/keep track of the matched node from the parent node.
You can use the following recursive method.
def recurse(obj, target)
case obj
when Array
obj.each do |e|
case e
when Array, Hash
rv = recurse(e, target)
return [rv] unless rv.nil?
when target
return e
end
end
when Hash
obj.each do |k,v|
case v
when Array, Hash
rv = recurse(v, target)
return {k=>rv} unless rv.nil?
when target
return {k=>v}
end
end
end
nil
end
recurse(hash_or_array, "buyer35")
#=> [{"book3"=>{"3"=>[{"6"=>[{"7"=>"buyer35"}]}]}}]
recurse(hash_or_array, "buyer24")
#=>[{"book2"=>{"book22"=>"buyer24"}}]
recurse(hash_or_array, "buyer33")
#=> [{"book3"=>{"2"=>"buyer33"}}]
recurse(hash_or_array, 11)
#=> [{"book3"=>{"3"=>[{"5"=>11}]}}]
recurse(hash_or_array, "buyer5")
#=>[{"book5"=>{"book5,1"=>"buyer5"}}]
If desired, one may write, for example,
recurse(hash_or_array, "buyer35").first
#=> {"book3"=>{"3"=>[{"6"=>[{"7"=>"buyer35"}]}]}}

How to search nested hash of arrays and arrays of hash and return multiple matching objects from the parent node?

Say I have the below ruby hash nested
hash_or_array = [{
"book1" => "buyer1",
"book2" => {
"book21" => "buyer21", "book22" => ["buyer23", "buyer24", true]
},
"book3" => {
"0" => "buyer31", "1" => "buyer32", "2" => "buyer33",
"3" => [{
"4" => "buyer34",
"5" => [10, 11],
"6" => [{
"7" => "buyer35"
}]
}]
},
"book4" => ["buyer41", "buyer42", "buyer43", "buyer35"],
"book5" => {
"book5,1" => "buyer5"
}
}]
And I want to look for the string buyer35. Upon match, it should return the following
[
{
"book3" => {
"0" => "buyer31", "1" => "buyer32", "2" => "buyer33",
"3" => [{
"4" => "buyer34",
"5" => [10, 11],
"6" => [{
"7" => "buyer35"
}]
}]
}
},
{
"book4" => ["buyer41", "buyer42", "buyer43", "buyer35"]
}
]
The solution below (from another SO question, link below), returns the first match, but I am trying to figure out how to return multiple matches
def recurse(obj, target)
case obj
when Array
obj.each do |e|
case e
when Array, Hash
rv = recurse(e, target)
return [rv] unless rv.nil?
when target
return e
end
end
when Hash
obj.each do |k,v|
case v
when Array, Hash
rv = recurse(v, target)
return {k=>rv} unless rv.nil?
when target
return {k=>v}
end
end
end
nil
end
This is the original question and answer: How to search nested hash of arrays and arrays of hash and only return matching object from the parent node?
UPDATE: The correct return format should be
[
{
"book3" => {
"3" => [{
"6" => [{
"7" => "buyer35"
}]
}]
}
},
{
"book4" => ["buyer41", "buyer42", "buyer43", "buyer35"]
}
]
Here is a function that recursively searches for the target value in any nested arrays or hashes. The function is then used to select the entries in your top level hash_or_array for entries that contain the target and adds them to an array.
require 'pp'
def find_value(obj, target, found = false)
found = true if obj == target
return found unless obj.is_a?(Array) || obj.is_a?(Hash)
case obj
when Hash
obj.each { |k, v| found = find_value(v, target, found) }
when Array
obj.each { |v| found = find_value(v, target, found) }
end
found
end
found_entries = hash_or_array.inject([]) {|entries, obj| entries << obj.select { |k, v| find_value({ k => v }, "buyer35") }}
pp found_entries
=>
[{"book3"=>
{"0"=>"buyer31",
"1"=>"buyer32",
"2"=>"buyer33",
"3"=>[{"4"=>"buyer34", "5"=>[10, 11], "6"=>[{"7"=>"buyer35"}]}]},
"book4"=>["buyer41", "buyer42", "buyer43", "buyer35"]}]
Here is a recursive solution to your "real" question. Since it mutates the original object, I used a "trick" to make a deep copy first. The keep_entries_with produces an object with the original shape as your input, but since your output shape is different the second step just transforms the filtered result into the shape of your desired output.
deep_copy = Marshal.load(Marshal.dump(hash_or_array))
def keep_entries_with(target, obj)
return unless obj.is_a?(Hash) || obj.is_a?(Array)
case obj
when Hash
keep_entries_with(target, obj.values)
obj.keep_if do |k, v|
v == target ||
v.is_a?(Hash) && v.values.any? { _1 == target || _1.is_a?(Hash) || _1.is_a?(Array) } ||
v.is_a?(Array) && v.any? { _1 == target || _1.is_a?(Hash) || _1.is_a?(Array) }
end
when Array
obj.each do |v|
keep_entries_with(target, v)
end
end
end
filtered = keep_entries_with("buyer35", deep_copy)
final_result = filtered.first.map { |k, v| { k => v } }
pp final_result
which produces:
[{"book3"=>{"3"=>[{"6"=>[{"7"=>"buyer35"}]}]}},
{"book4"=>["buyer41", "buyer42", "buyer43", "buyer35"]}]
The code below appears to generate the desired output for this specific case:
hash_or_array.inject([]) do |result, x|
x.keep_if { |k, v| v.to_s =~ /buyer35/ }
result << x
end

Check that array only contains string objects

Is there a more concise way than the below to check if an array only contains String class objects?
a = ['one','twon','three'] # => ["one", "twon", "three"]
b = ['one','twon', 1 ] # => ["one", "twon", 1]
c = ['one','twon', {one: 1} ] # => ["one", "twon", {:one=>1}]
def contains_only_string_class(object)
uniq_a= object.map{ |i| i.class }.uniq # => [String], [String, Integer], [String, Hash]
uniq_a.include?(String) & (uniq_a.length == 1) # => true, false, false
end # => :contains_only_string_class
contains_only_string_class(a) # => true
contains_only_string_class(b) # => false
contains_only_string_class(c) # => false
What about using all?
def contains_only_string_class(collection)
collection.all? { |obj| obj.is_a?(String) }
end

How to recursively check for presence of specific key

Given a Ruby hash of parameters that are infinitely nested, I want to write a function that returns true if a given key is in those parameters.
This is the function I have so far, but it's not quite right, and I'm at a loss as to why:
def has_key(hash, key)
hash.each do |k, v|
if k == key
return true
elsif v.class.to_s == "Array"
v.each do |inner_hash|
return has_key(inner_hash,key)
end
else
return false
end
end
end
The method should return the following results:
# all check for presence of "refund" key
has_key({
"refund" => "2"
}, "refund")
=> true
has_key({
"whatever" => "3"
}, "refund")
=> false
has_key({
"whatever" => "3",
"child_attributes" => [{
"refund" => "1"
}]
}, "refund")
=> true
has_key({
"whatever" => "3",
"child_attributes" => [{
"nope" => "4"
}]
}, "refund")
=> false
has_key({
"whatever" => "3",
"child_attributes" => [{
"a" => "1",
"refund" => "2"
}]
}, "refund")
=> true
has_key({
"whatever" => "3",
"child_attributes" => [
{"a" => "1", "b" => "2"},
{"aa" => "1", "refund" => "2"}
]
}, "refund")
=> true
has_key({
"whatever" => "3",
"child_attributes" => [
{"a" => "1", "b" => "2"},
{"grand_child_attributes" => [
{"test" => "3"}
]}
]
}, "refund")
=> false
has_key({
"whatever" => "3",
"child_attributes" => [
{"a" => "1", "b" => "2"},
{"grand_child_attributes" => [
{"test" => "3"}, {"refund" => "5"}
]}
]
}, "refund")
=> true
has_key({
"whatever" => "3",
"child_attributes" => [
{"a" => "1", "b" => "2"},
{"grand_child_attributes" => [
{"test" => "3", "refund" => "5"}
]}
]
}, "refund")
=> true
The problem with your code seems to be in here:
elsif v.class.to_s == "Array"
v.each do |inner_hash|
return has_key(inner_hash,key)
end
else
This would always return has_key(inner_array[0]) without checking subsequent values. The fix is to return only if it's true, else continue checking, like this:
elsif v.class.to_s == "Array"
v.each do |inner_hash|
if(has_key(inner_hash,key))
return true
end
end
else
return false
The following will work.
def has_key(hash, key)
hash.each do |k, v|
return true if k == key
if v.is_a? Array
v.each do |h|
rv = has_key(h, key)
return rv if rv
end
end
end
false
end
This passes all your tests. One more:
h = { "a" => 1,
"b" => [{ "c" => 2, "d" => 3 },
{"e"=> [{ "f" => "4" },
{ "g" => [{ "h" => 5 },
{ "i" => 6, "refund" => 7 }
]
}
]
}
]
}
has_key h, "refund"
#=> true
h["b"][1]["e"][1]["g"] = [{ "h"=>5 }]
h
#=> {"a"=>1, "b"=>[{"c"=>2, "d"=>3}, {"e"=>[{"f"=>"4"}, {"g"=>[{"h"=>5}]}]}]}
has_key h, "refund"
#=> false
Inspired by #Wand's answer, for
h = {"a"=>"3", "b"=>[{"c"=>"1", "d"=>"2"}, {"e"=>[{"test"=>"3", "refund"=>"5"}]}]}
you don't have to load JSON:
str = h.to_s
#=> "{\"a\"=>\"3\", \"b\"=>[{\"c\"=>\"1\", \"d\"=>\"2\"}, {\"e\"=>[{\"test\"=>\"3\", \"refund\"=>\"5\"}]}]}"
str =~ /\"refund\"=>/
#=> 60 (truthy)
I confess to being a little uncomfortable with any approach that converts the hash to a string, and then parsing the string, for fear that string formats may change in future.
I'd do something like:
class Hash
def key_exists?(key)
self.keys.include?(key) ||
self.values.any?{ |v|
Hash === v &&
v.key_exists?(key)
}
end
end
{'a' => 1}.key_exists?('a') # => true
{'b' => 1}.key_exists?('a') # => false
{'b' => {}}.key_exists?('a') # => false
{'b' => {'a' => {}}}.key_exists?('a') # => true
{'b' => {'a' => 1}}.key_exists?('a') # => true
{'b' => {'b' => {}}}.key_exists?('a') # => false
{'b' => {'b' => {'a' => 1}}}.key_exists?('a') # => true
Insert all the usual warnings about extending core classes and recommendations to use the alternative ways of doing it here.
Note: Similarly, "iterate over every key in nested hash" could be used to easily determine a true/false value and it demonstrates the safe way to extend a core class.
You could convert the hash to JSON and then check whether the JSON has "refund": present in it, as any key will be serialised to JSON in the form "key":.
require "json"
hash.to_json.include?('"refund":')
Give this a shot, I tried it, seems to work fine for me
def has_key(hash, key)
if hash.keys.include?(key)
true
else
# get the values of the current hash, flatten out to not include arrays
new_hash = hash.values.flatten
# then filter out any element that's not a hash
new_hash = new_hash.select {|b| b.is_a?(Hash)}
# merge all the hashes into single hash
new_hash = new_hash.inject {|first, second| first.merge(second)}
if new_hash
has_key(new_hash, key)
else
false
end
end
end

What determines the return value of `present?`?

Some valid ActiveRecord objects return false for present?:
object.nil? # => false
object.valid? # => true
object.present? # => false
object.blank? # => true
I prefer object.present? over not object.nil?. What determines the return value of present?/blank??
EDIT: Found the answer: I had redefined the empty? method on this class, not the blank? or present? method; along the lines of #Simone's answer, blank? is using empty? behind the scenes.
present? is the opposite of blank?. blank? implementation depends on the type of object. Generally speaking, it returns true when the value is empty or like-empty.
You can get an idea looking at the tests for the method:
BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", ' ', "\u00a0", [], {} ]
NOT = [ EmptyFalse.new, Object.new, true, 0, 1, 'a', [nil], { nil => 0 } ]
def test_blank
BLANK.each { |v| assert_equal true, v.blank?, "#{v.inspect} should be blank" }
NOT.each { |v| assert_equal false, v.blank?, "#{v.inspect} should not be blank" }
end
def test_present
BLANK.each { |v| assert_equal false, v.present?, "#{v.inspect} should not be present" }
NOT.each { |v| assert_equal true, v.present?, "#{v.inspect} should be present" }
end
An object can define its own interpretation of blank?. For example
class Foo
def initialize(value)
#value = value
end
def blank?
#value != "foo"
end
end
Foo.new("bar").blank?
# => true
Foo.new("foo").blank?
# => false
If not specified, it will fallback to the closest implementation (e.g. Object).
From the documentation for Object#present?
An object is present if it's not blank.
In your case, object is blank, so present? will return false. Validity of the object does not really matter.

Resources