Подсчет совпадающих элементов в массиве

Учитывая два массива одинакового размера, как я могу найти количество совпадающих элементов без учета позиции?
Например:

  • [0,0,5] и [0,5,5] возвращают совпадение 2, так как существует один 0 и один 5,
  • [1,0,0,3] и [0,0,1,4] вернет совпадение 3, так как есть два совпадения 0 и одно совпадение 1;
  • [1,2,2,3] и [1,2,3,4] вернет совпадение 3.

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

Ответ 1

Вы можете выполнить это с помощью count:

a.count{|e| index = b.index(e) and b.delete_at index }

Демонстрация

или inject:

a.inject(0){|count, e| count + ((index = b.index(e) and b.delete_at index) ? 1 : 0)}

Демонстрация

или select и length (или это псевдоним - size):

a.select{|e| (index = b.index(e) and b.delete_at index)}.size

Демонстрация

Результаты:

  • a, b = [0,0,5], [0,5,5] вывод: => 2;
  • a, b = [1,2,2,3], [1,2,3,4] вывод: => 3;
  • a, b = [1,0,0,3], [0,0,1,4] вывод => 3.

Ответ 2

(arr1 & arr2).map { |i| [arr1.count(i), arr2.count(i)].min }.inject(0, &:+)

Здесь (arr1 & arr2) возвращает список значений uniq, которые содержат оба массива, arr.count(i) подсчитывает количество элементов i в массиве.

Ответ 3

Другое использование для могущественного (и очень необходимого) Array#difference, который я определил в своем ответе здесь. Этот метод похож на Array#-. Различие между этими двумя способами проиллюстрировано в следующем примере:

a = [1,2,3,4,3,2,4,2]
b = [2,3,4,4,4]
a - b          #=> [1]
a.difference b #=>  [1, 3, 2, 2] 

Для настоящей заявки:

def number_matches(a,b)
  left_in_b = b
  a.reduce(0) do |t,e|
    if left_in_b.include?(e)
      left_in_b = left_in_b.difference [e]
      t+1
    else
      t
    end
  end
end

number_matches [0,0,5],   [0,5,5]   #=> 2
number_matches [1,0,0,3], [0,0,1,4] #=> 3
number_matches [1,0,0,3], [0,0,1,4] #=> 3

Ответ 4

Используя multiset gem:

(Multiset.new(a) & Multiset.new(b)).size

Multiset похож на Set, но позволяет дублировать значения. & - это оператор "set intersection" (возвращает все вещи, находящиеся в обоих наборах).

Ответ 5

Я не думаю, что это идеальный ответ, потому что он немного сложный, но...

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

def matches(a1, a2)
  m = 0
  a1_counts = count(a1)
  a2_counts = count(a2)
  a1_counts.each do |e, c|
    m += [a1_counts, a2_counts].min
  end
  m
end

В принципе, сначала напишите метод, который создает хэш из массива числа раз, когда каждый элемент появляется. Затем используйте их, чтобы суммировать наименьшее количество раз, когда каждый элемент появляется в обоих массивах.