The proposed solution by the questioner is the fastest, 2n tops if i'm correct, so O(n).
But error handling is problematic, consider the input [2,3,3,2,-1].
Is -1 the correct result or does it indicate an error?
Maybe, instead of returning only the first unique element, just return all unique elements
function uniques(xs) {
var result, tmp, i, x;
result = [];
tmp = [];
for (i in xs) {
x = xs[i];
tmp[x] = tmp[x] ? tmp[x]+1 : 1;
}
for (x in tmp) {
if (tmp[x] === 1) {
result[result.length] = x;
}
}
return result;
}
console.log(uniques([22, 25, 3, 3, 1, 2, 0, 0,100,22,25,1,2,-1,null,NaN,[]]))
By returning an array you can still get the first unique by applying [0] to the result and indicate no unique entry with an empty array.
It also does not change your complexity: avg. n+k with k being the count for different values, but still 2n tops.
OLD ANSWER:
Seems to me, the more optimal concept is to remove multiple occurences once you can.
Given the worst-case scenario, that the unique element is at the ende of the array,
filter and find would have to loop over all elements.
Then, if indexOf and lastIndexOf are supposed to be O(n), you would be stuck with O(n^2)
(function(){
function remove (x,xs) {
ys = [];
j = 0;
for (i in xs) {
if (x !== xs[i]) {
ys[j] = xs[i];
j++;
}
}
return ys;
}
function firstUnique (xs) {
console.log(xs.length);
if (xs.length == 0) {
return -1;
}
ys = remove(xs[0],xs);
if (ys.length < xs.length-1) {
return firstUnique(ys);
}
return xs[0];
}
console.log(firstUnique([22, 25, 3, 3, 1, 2, 0, 0,22,25,1,2, 100]));
}());
This way, you have only n iterations for the first application of remove.
Then you reduce the size of the remaining array to at least n-2 for the next recurrence, depending on the distribution of multiple occurences.
This should sum up to O(n log n), but i'm not sure when it comes to the exact math.
This solution might be worse space-compexity-wise.