8

Saw an interesting piece of code to find a lonely number in a list of duplicate numbers (where every number in the list occurs twice except for one).

function findNonPaired(listOfNumbers) {
  let nonPairedNumber = 0

  listOfNumbers.forEach((n) => {
      nonPairedNumber ^= n
  })

  return nonPairedNumber
}

const x = [1,5,4,3,9,2,3,1,4,5,9]
console.log(findNonPaired(x))

This solution looks very elegant, but I'm curious at to what the ^= operator is actually doing here?

Cumulo Nimbus
  • 6,542
  • 7
  • 43
  • 61
  • @CumuloNimbus Those are carrots (different word). Just to note, `^=` also has a meaning in CSS, to [match the beginning of an attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors). The meaning is similar to the ["start of string" anchor](http://www.regular-expressions.info/anchors.html) from regular expressions. You can apply CSS's `^=` to JavaScript by using `document.querySelectorAll('[name^=form1-]')` which finds all elements with a "name" attribute that begins with "form1-" – Pluto May 24 '17 at 17:20
  • 4
    As a side note, this function can be a one-liner: `findUnique = list => list.reduce((out, n) => out ^ n, 0)` – Joe Frambach May 24 '17 at 19:01
  • 8
    @CumuloNimbus _Carets_. Carrots are the things in the ground that Bugs Bunny likes. – David Given May 24 '17 at 21:03
  • Have you considered consulting a reference manual? https://www.w3schools.com/js/js_assignment.asp – Hot Licks May 24 '17 at 22:09

2 Answers2

19

a ^= b is the same as a = a ^ b where ^ is the bitwise XOR operator.

0 ^ 0 === 0
1 ^ 0 === 1
0 ^ 1 === 1
1 ^ 1 === 0

This is a neat algorithm. Let's trace one execution with the list [8, 12, 8] for example:

0 ^ 8 = 0000 ^ 1000 = 1000
        1000 ^ 1100 = 0100
        0100 ^ 1000 = 1100 = 12

The word "duplicate" isn't correct. This algorithm tests for parity. A simple but somewhat wrong definition is "when everything has a pair". pair...parity.

[2,2,2,3,3,5,5,5,5] will return 2 because everything else has a pair.

[3,4,5] will actually return 2 (011^100^101 -> 010) because that is the xor of all the unpaired items.

mbomb007
  • 3,077
  • 2
  • 30
  • 51
Joe Frambach
  • 25,568
  • 9
  • 65
  • 95
  • Cool stuff, thanks for the explanation and examples – Cumulo Nimbus May 24 '17 at 18:37
  • 2
    Therefore, the algorithm isn't "neat" at all, because it pretends to find "unique number", while in your example [2,2,2,3,3,5,5,5,5] it falsely finds 2 despite 2 being part of a triplet, and therefore not unique, and in your [3,4,5] finding 2, which isn't even in the list, and failing to find the three unique numbers that are actually there. This is one of those things a programmer can "get away with" until those conditions no longer apply, at which point it fails spectacularly, as your last example demonstrates. – Monty Harder May 24 '17 at 21:35
  • @MontyHarder Usually people make an argument before saying "Therefore"... Also thanks for your opinion on this algorithm's neatness. However, it correctly finds 2 in this example b/c it is the only unpaired number. Admittedly "unique" was a bad word choice by me, and "non-paired" would be a better choice of words, as pointed out by JoeFrambach – Cumulo Nimbus May 24 '17 at 22:09
  • 1
    @MontyHarder You could say the same thing about any algorithm if you fail to satisfy its preconditions. The question was quite clear that it operates on a list of numbers where each number appears twice except for one. While you have certainly shown that it doesn't work outside of that, that's a strawman argument; no one was claiming that it would. – Chris Hayes May 24 '17 at 22:41
  • @MontyHarder Did I say something wrong in my answer? – Joe Frambach May 24 '17 at 22:58
4

Like other answers say - it is a bitwise XOR.

About the algorythm - it is cool if you are sure that the duplicates are even count. When a number is XOR-ed with x and later again XOR-ed with x it will return to it's previuos value. If one number is seen 3 times in this chain, the 3-rd occurence will fool this algorythm. Also if there is one more value that is single in the chain, like:

a, b, c, X, a, c, b, Y

the result will be (X ^ Y) and you can't be sure if you have one unique value or more.

Todor Simeonov
  • 796
  • 1
  • 5
  • 11
  • 1
    No sane person wants to inherit responsibility for code like this, which doesn't document the assumptions (and we know what happens when we ASS|U|ME) it makes. – Monty Harder May 24 '17 at 21:39