I have some boilerplate code similar to rrowland's, but I feel like my algorithm could be a little bit faster. It operates in O(n) by using prime number multiplication to count letters, and is non-branching in the longest-time routine.
Instead of doing ind - 97
I keep 97 empty spots in the array that is accessed.
I think if you were more obsessive you could do the counting using bitwise operations, but this is good enough.
function isAnagram(word1, word2) {
if (!word1 || !word2 || !word1.length || !word2.length) {
throw new Error('isAnagram requires two strings to be passed.')
}
var nword1 = word1.replace(/\s+/g, '').toLowerCase();
var nword2 = word2.replace(/\s+/g, '').toLowerCase();
var length1 = nword1.length;
var length2 = nword2.length;
if (length1 !== length2) {
return false;
}
var word1hash = 1;
var word2hash = 1;
var primes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101];
var ind;
for (var i = 0; i < length1; i++) {
ind = nword1.charCodeAt(i);
word1hash *= primes[ind];
}
for (var i = 0; i < length2; i++) {
ind = nword2.charCodeAt(i);
word2hash *= primes[ind];
}
console.log(word1hash);
console.log(word2hash);
return word1hash == word2hash;
}
Edit 1
I guess we have a speed contest now, and this version is a great deal faster than all its precedents (benchmark).
function isAnagram(word1, word2) {
if (!word1 || !word2 || !word1.length || !word2.length) {
throw new Error('isAnagram requires two strings to be passed.')
}
var nword1 = word1;
var nword2 = word2;
var length1 = nword1.length;
var length2 = nword2.length;
if (length1 !== length2) {
return false;
}
var word1hash = 1;
var word2hash = 1;
var tab = {'q': 2, 'w': 3, 'e': 5, 'r': 7, 't': 11, 'y': 13, 'u': 17, 'i': 19, 'o': 23, 'p': 29, 'a': 31, 's': 37, 'd': 41, 'f': 43, 'g': 47, 'h': 53, 'j': 59, 'k': 61, 'l': 67, 'z': 71, 'x': 73, 'c': 79, 'v': 83, 'b': 89, 'n': 97, 'm': 101, 'Q': 2, 'W': 3, 'E': 5, 'R': 7, 'T': 11, 'Y': 13, 'U': 17, 'I': 19, 'O': 23, 'P': 29, 'A': 31, 'S': 37, 'D': 41, 'F': 43, 'G': 47, 'H': 53, 'J': 59, 'K': 61, 'L': 67, 'Z': 71, 'X': 73, 'C': 79, 'V': 83, 'B': 89, 'N': 97, 'M': 101, ' ': 1}
for (var i = 0; i < length1; i++) {
word1hash *= tab[word1[i]]
}
for (var i = 0; i < length2; i++) {
word2hash *= tab[word2[i]]
}
return word1hash == word2hash;
}
Edit 2
I know this is silly, but adding logarithms of prime numbers is actually a tiny bit faster. (benchmark)
function isAnagram(word1, word2) {
if (!word1 || !word2 || !word1.length || !word2.length) {
throw new Error('isAnagram requires two strings to be passed.')
}
var length1 = word1.length;
var length2 = word2.length;
var word1hash = 1;
var word2hash = 1;
var tab = {'q': 0.6931471805599453, 'w': 1.0986122886681098, 'e': 1.6094379124341003, 'r': 1.9459101490553132, 't': 2.3978952727983707, 'y': 2.5649493574615367, 'u': 2.833213344056216, 'i': 2.9444389791664403, 'o': 3.1354942159291497, 'p': 3.367295829986474, 'a': 3.4339872044851463, 's': 3.6109179126442243, 'd': 3.713572066704308, 'f': 3.7612001156935624, 'g': 3.8501476017100584, 'h': 3.970291913552122, 'j': 4.07753744390572, 'k': 4.110873864173311, 'l': 4.204692619390966, 'z': 4.2626798770413155, 'x': 4.290459441148391, 'c': 4.3694478524670215, 'v': 4.418840607796598, 'b': 4.48863636973214, 'n': 4.574710978503383, 'm': 4.61512051684126, 'Q': 0.6931471805599453, 'W': 1.0986122886681098, 'E': 1.6094379124341003, 'R': 1.9459101490553132, 'T': 2.3978952727983707, 'Y': 2.5649493574615367, 'U': 2.833213344056216, 'I': 2.9444389791664403, 'O': 3.1354942159291497, 'P': 3.367295829986474, 'A': 3.4339872044851463, 'S': 3.6109179126442243, 'D': 3.713572066704308, 'F': 3.7612001156935624, 'G': 3.8501476017100584, 'H': 3.970291913552122, 'J': 4.07753744390572, 'K': 4.110873864173311, 'L': 4.204692619390966, 'Z': 4.2626798770413155, 'X': 4.290459441148391, 'C': 4.3694478524670215, 'V': 4.418840607796598, 'B': 4.48863636973214, 'N': 4.574710978503383, 'M': 4.61512051684126, ' ': 0.0
}
for (var i = 0; i < length1; i++) {
word1hash += tab[word1[i]]
}
for (var i = 0; i < length2; i++) {
word2hash += tab[word2[i]]
}
return word1hash == word2hash;
}
Edit 3: removed equal length check.