There are some improvements that can be made to the already given answers. Especially as
user11536834 second example was a surprisingly poor performer.
The following function improves upon given answers in terms of performance doing the same operation in 1/3rd the time
function find(nums) {
const a = nums[0], b = nums[2];
return a !== nums[1] ?
nums.find(v => v !== b):
nums.find(v => v !== a);
}
Func A
If we consider he best solution so far
function findA(nums) {
return nums.find(v => nums[0] != nums[1] ? v != nums[2] : v != nums[0])
}
We can double the performance by avoiding the cost of the call stack for each iteration by using a standard loop. This halves the processing time.
Func B
function findB(nums) {
var i = 0, val = nums[i++];
while (i < nums.length - 1) {
if (nums[i++] !== val) {
return val !== nums[i] ? val : nums[i - 1];
}
}
return nums[i];
}
Modifying user11536834 second solution gave a very surprising result being 20 times slower than func A
Func C
function findC(nums) {
return nums.find(nums[0] !== nums[1] ? v => v !== nums[2] : v => v !== nums[0])
}
OMDG that is 20 times slower than the first version. The optimizer obviously does not like the function declarations being conditional. So I moved the functions out of the ternary and added some other minor optimizations to get...
Func D
function findD(nums = data[(d++) % len]) {
const a = nums[0], b = nums[2];
return a !== nums[1] ?
nums.find(v => v !== b):
nums.find(v => v !== a);
}
Now we are talking, it doubled the performance again by nearly 2 on the while loop version.
Performance comparison
Running a comparison performance benchmark on 2000 different arrays with random position of the odd number out and each array 2000 items long. The results as follows.
findA.: 4.239 ±1.723µs OPS 235,919 35% Total 1,170ms 276,000 operations
findB.: 2.225 ±1.172µs OPS 449,504 67% Total 714ms 321,000 operations
findC.: 38.612 ±10.163µs OPS 25,898 4% Total 11,120ms 288,000 operations
findD.: 1.496 ±0.725µs OPS 668,321 *100% Total 471ms 315,000 operations
OPS is Operations per second (Operation is the function being called)
µs is 1 / 1,000,000 second
ms is 1 / 1,000 second
*Best time is 100% of its self
Note that Javascript is seldom linear and that the length of the arrays tested was tested to be greater than the point where all 4 function were giving a linear result as the array size grew.
As tested
The test where conducted on the modified functions as follows. Data was created with 2 util functions listed below the next snippet.
var a = 0, b = 0, c = 0, d = 0;
const len = 2000;
const data = $setOf(len, i => {var b = $setOf(len, k => i); b[$randI(len)] = i + 1; return b});
// test name findA
function findA(nums = data[(a++) % len]) {
return nums.find(v => nums[0] != nums[1] ? v != nums[2] : v != nums[0])
}
// test name findB
function findB(nums = data[(b++) % len]) {
var i = 0, val = nums[i++];
while (i < nums.length - 1) {
if (nums[i++] !== val) {
return val !== nums[i] ? val : nums[i - 1];
}
}
return nums[i];
}
// test name findC
function findC(nums = data[(c++) % len]) {
return nums.find(nums[0] !== nums[1] ? v => v !== nums[2] : v => v !== nums[0])
}
// test name findD
function findD(nums = data[(d++) % len]) {
const a = nums[0], b = nums[2];
return a !== nums[1] ?
nums.find(v => v !== b):
nums.find(v => v !== a);
}
Utils
const $setOf = (count, fn = (i)=>i) => {var a = [],i = 0; while (i < count) { a.push(fn(i ++)) } return a };
const $randI = (min = 2, max = min + (min = 0)) => (Math.random() * (max - min) + min) | 0;