Сравните 2 Hash со значениями, состоящими из массива - PullRequest
0 голосов
/ 16 февраля 2019

У меня есть 2 хэша, скажем, A, B

A: { 'key1' => [a, b], 'key2' => 'c' }
B: { 'key1' => [b, a], 'key2' => 'c' }

Какой самый лучший способ сравнить эти 2 хэша.Порядок содержимого массива не имеет значения.Так что в моем случае хэш A и B равны

Ответы [ 4 ]

0 голосов
/ 16 февраля 2019

Я придумал это решение:

def are_equals?(a, b)
  (a.keys.sort == b.keys.sort) &&
  a.merge(b) { |k, o_val, n_val| [o_val, n_val].all? { |e| e.kind_of? Array} ? o_val.sort == n_val.sort : o_val == n_val }.values.all?
end


Как это работает.

Первая часть тестирует на равенство ключей, используя Hash#keys, который возвращает массив ключей, отсортированный, конечно:

a.keys.sort == b.keys.sort

Для второй части я использовал Hash#merge для сравнения значений, связанных с тем же ключом, и может быть расширен следующим образом:

res = a.merge(b) do |k, o_val, n_val|
  if [o_val, n_val].all? { |e| e.kind_of? Array}
    o_val.sort == n_val.sort
  else
    o_val == n_val
  end
end

#=> {"key1"=>true, "key2"=>true}

Возвращает хэш, где значения являются истинными или ложными, затем проверяет, являются ли все значения истинными, используя Enumerable#all?:

res.values.all?
#=> [true, true].all? => true
0 голосов
/ 16 февраля 2019

Можно сортировать массивы, но это может быть дорогостоящей операцией, если массивы большие.Если n равен размеру массива, временная сложность heapsort, например, равна O(n log(n)).Быстрее заменить массивы счетными хэшами, конструкция которых имеет временную сложность O(n).

h1 = { 'k1' => [1, 2, 1, 3, 2, 1], 'k2' => 'c' }
h2 = { 'k1' => [3, 2, 1, 2, 1, 1], 'k2' => 'c' }

def same?(h1, h2)
  return false unless h1.size == h2.size
  h1.all? do |k,v|
    if h2.key?(k)
      vo = h2[k]
      if v.is_a?(Array)
        if vo.is_a?(Array) 
          convert(v) == convert(vo)
        end
      else
        v == vo
      end
    end
  end
end           

def convert(arr)
  arr.each_with_object(Hash.new(0)) { |e,g| g[e] += 1 }
end

same?(h1, h2)
  #=> true

Здесь

convert([1, 2, 1, 3, 2, 1])
  #=> {1=>3, 2=>2, 3=>1} 
convert([3, 2, 1, 2, 1, 1])
  #=> {3=>1, 2=>2, 1=>3}

и

{1=>3, 2=>2, 3=>1} == {3=>1, 2=>2, 1=>3}
  #=> true

См. Hash :: new , в частности, в случае, когда метод принимает аргумент, равный значению по умолчанию .

Защитное предложение return false unless h1.size == h2.size должно гарантировать, чтоh2 не имеет ключей, которых нет в h1.Обратите внимание, что следующее возвращает ложное значение nil:

if false
  #...
end
  #=> nil

В нескольких местах я написал это, а не более многословное, но эквивалентное выражение

if false
  #...
else
  nil
end
0 голосов
/ 16 февраля 2019

Я бы определенно согласился с Иваном, что это не так просто, как кажется на первый взгляд, но я решил, что попробую сделать это с помощью рекурсии.Это дает дополнительное преимущество возможности сравнивать не только хэши.

hash_a = {'key1' => ['a', 'b'], 'key2' => 'c'}
hash_b = {'key1' => ['b', 'a'], 'key2' => 'c'}
hash_c = {'key1' => ['a', 'c'], 'key2' => 'c'}
hash_d = {'key1' => ['a', 'b'], 'key2' => 'd'}
hash_e = {'key1' => ['a', 'b'], 'key2' => ['a', 'b']}
hash_f = {'key1' => ['a', 'b'], 'key2' => 'c', 'key3' => 'd'}


def recursive_compare(one, two)
  unless one.class == two.class
    return false
  end

  match = false

  # If it's not an Array or Hash...
  unless one.class == Hash || one.class == Array
    return one == two
  end

  # If they're both Hashes...
  if one.class == Hash
    one.each_key do |k|
      match = two.key? k
      break unless match
    end

    two.each_key do |k|
      match = one.key? k
      break unless match
    end

    if match
      one.each do |k, v|
        match = recursive_compare(v, two[k])
        break unless match
      end
    end
  end

  # If they're both Arrays...
  if one.class == Array
    one.each do |v|
      match = two.include? v
      break unless match
    end
    two.each do |v|
      match = one.include? v
      break unless match
    end
  end

  match
end

puts recursive_compare(hash_a, hash_b) #=> true
puts recursive_compare(hash_a, hash_c) #=> false
puts recursive_compare(hash_a, hash_d) #=> false
puts recursive_compare(hash_a, hash_e) #=> false
puts recursive_compare(hash_a, hash_f) #=> false
0 голосов
/ 16 февраля 2019

Это не так просто, как кажется на первый взгляд.
Необходимо учитывать несколько нюансов:

  • количество элементов в хешах может не совпадать;
  • элементы с одинаковым ключом в двух хешах могут быть разных типов.

Относительно универсальным решением может быть следующее:

def hashes_comp(hash1, hash2)
  return false if hash1.size != hash2.size
  hash1.each do |key, value|
    if value.class == Array
      return false if hash2[key].class != Array || value.sort != hash2[key].sort
    else
      return false if value != hash2[key]
    end
  end
  true
end

hash_a = {'key1' => ['a', 'b'], 'key2' => 'c'}
hash_b = {'key1' => ['b', 'a'], 'key2' => 'c'}
hash_c = {'key1' => ['a', 'c'], 'key2' => 'c'}
hash_d = {'key1' => ['a', 'b'], 'key2' => 'd'}
hash_e = {'key1' => ['a', 'b'], 'key2' => ['a', 'b']}
hash_f = {'key1' => ['a', 'b'], 'key2' => 'c', 'key3' => 'd'}

hashes_comp(hash_a, hash_b) #=> true
hashes_comp(hash_a, hash_c) #=> false
hashes_comp(hash_a, hash_d) #=> false
hashes_comp(hash_a, hash_e) #=> false
hashes_comp(hash_a, hash_f) #=> false
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...