Reputation: 1855
I have two hashes of the same length:
hash1 = {"1"=>"val", "2"=>"val", "3"=>"", "4"=>""}
hash2 = {"1"=>[""], "2"=>["value"], "3"=>["val1", "val2"], "4"=>[""]}
I need to compare them. The corresponding keys need to either both have a value (for hash1
, this means non-blank, and for hash2
, this means there must be a non-blank value in the array) or both have a blank value (for hash2
, this means the value is [""]).
"1"
fails (array has one value and that value is blank)"2"
passes (both have values)"3"
fails (hash1
is blank)"4"
passes (hash1
is blank and hash2
has one value in the array and that value is blank)If one of these comparisons fails, then I should get false
returned. I'm not sure how to do a comparison like this.
Upvotes: 0
Views: 180
Reputation: 110725
Edit: better, I think:
hash1.all? { |k,v| !(v.empty? ^ (hash2[k]==[""])) }
#=> false
Original answer:
keys = hash1.keys
#=> ["1", "2", "3", "4"]
hash1.values_at(*keys).zip(hash2.values_at(*keys)).all? do |v1,v2|
!(v1.empty? ^ (v2==[""]))
end
#=> false
^
is Ruby's XOR operator.
Upvotes: 1
Reputation: 116870
The following is more-or-less a direct translation of the requirements.(*)
# similar(h1,h2) assumes that h1 is a string-valued hash,
# and that h2 is a hash with values that are all arrays of strings.
#
def similar(h1,h2)
return false if h1.length != h2.length
h1.all? {|key, v1|
v2=h2[key]
v2 != nil and
((v1=="" and v2==[""]) or
(v1 != "" and !v2.all?{|x| x.length==0} ))
}
end
(*) The OP stipulated that the two hashes have the same number of keys, so the check that that's the case could perhaps be omitted, but there is no harm in checking, and the method as written is likely to be a tiny bit more useful (or at least more realistic) with the check included. Feel free to omit it.
Upvotes: 0
Reputation: 168199
hash1.merge(hash2){|_, v1, v2| v2.dup.push(v1)}
.all?{|_, v| v.all?(&:empty?) or v.none?(&:empty?)}
Or following @mudasobwa's suggestion:
hash2.merge(hash1){|_, v2, *v1| v1 + v2}
.all?{|_, v| v.all?(&:empty?) or v.none?(&:empty?)}
Upvotes: 1
Reputation: 121010
Assuming that hashes are already ordered:
hash1 = {"1"=>"val", "2"=>"val", "3"=>"", "4"=>""}
hash2 = {"1"=>[""], "2"=>["value"], "3"=>["val1", "val2"], "4"=>[""]}
hash1.zip(hash2).all? do |(_, fv), (_, lv)|
fv.empty? ^ !lv.all?(&:empty?)
end
Here we take a benefit of using XOR
. Whether hashes are not ordered, the preprocessing (ordering) required.
According to @sawa’s and @CarySwoveland’s comments, for not sorted hashes:
hash1.sort.zip(hash2.sort).all? do |(fk, fv), (lk, lv)|
# ⇓ consistency ⇓ true if one operand is truthy, other is falsey
(fk == lk) && (fv.empty? ^ !lv.all?(&:empty?))
end
Upvotes: 2
Reputation: 4346
Another approach:
def compare_hashes(hash1, hash2)
(1..hash1.length).each do |n|
n = n.to_s # can easily swap this if you want to use integers or symbols
return false if hash1[n].empty? || hash2[n].empty? || hash2[n].all?(&:empty?)
end
true
end
Upvotes: 0