Rob
Rob

Reputation: 1855

Check whether the values of hashes are either both blank or both have a value

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 [""]).

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

Answers (5)

Cary Swoveland
Cary Swoveland

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

peak
peak

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

sawa
sawa

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

Aleksei Matiushkin
Aleksei Matiushkin

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

Damon
Damon

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

Related Questions