Codewars: find the odd int (array_count_values, glossary method, operator XOR (^))
Codewars: find the odd int (array_count_values, glossary method)
Glossary overview

Codewars: find the odd int (array_count_values, glossary method, operator XOR (^))

<?php

function findIt(array $seq) : int
{   
    
    if (count($seq) == 1) return $seq[0];
  
    // make glossary: key - number; value - quantity
    $glossary = [];
  
    foreach($seq as $num) {
      if (isset($glossary[$num])) {
        $glossary[$num] +=1; 
      } else {
        $glossary[$num] = 1;
      }
    }
    
    $result = array_keys(array_filter($glossary, function($value) {
    	return $value % 2 !== 0; 
    }));
    
    return $result[0];
}

var_dump(findIt([20,1,-1,2,-2,3,3,5,5,1,2,4,20,4,-1,-2,5]));

// Output
int(5)

I’ve used method “glossary”. I used a frequency map to count how many times each number appears.

array(8) {
  [20]=>
  int(2)
  [1]=>
  int(2)
  [-1]=>
  int(2)
  [2]=>
  int(2)
  [-2]=>
  int(2)
  [3]=>
  int(2)
  [5]=>
  int(3)
  [4]=>
  int(2)
}

But there is function array_count_values.

function findIt(array $seq) : int
{
    $nums = array_count_values($seq);
    foreach ($nums as $key => $val) {
      if ($val % 2) {
        return $key;
      }
    }
}

It returns array:

array(8) {
  [20]=>
  int(2)
  [1]=>
  int(2)
  [-1]=>
  int(2)
  [2]=>
  int(2)
  [-2]=>
  int(2)
  [3]=>
  int(2)
  [5]=>
  int(3)
  [4]=>
  int(2)
}

Or we can use array_reduce:

function findIt(array $seq) : int
{
    return array_reduce($seq, function($carry, $value) {
          return $carry ^ $value;
    });
}

array_reduce последовательно «сворачивает» массив в одно значение.

На каждом шаге:

carry = результат прошлого шага
value = текущий элемент массива

Функция возвращает новый carry.

То есть происходит цепочка:

((a ^ b) ^ c) ^ d ...

В итоге остаётся одно число.

Магия оператора XOR (^)

Вот где начинается настоящая математика.

Свойства XOR:

x ^ x = 0        одинаковые уничтожаются
x ^ 0 = x        ноль ничего не меняет
XOR коммутативен и ассоциативен

Это значит:

  • порядок не важен
  • пары одинаковых чисел взаимно обнуляются

Пример

[1, 2, 2, 1, 3]

Вот что произойдет:
(1 ^ 1) ^ (2 ^ 2) ^ 3
→ 0 ^ 0 ^ 3
→ 3

Все числа с чётным количеством исчезают.
Остаётся только то, у которого нечётное.