Anagrams (v8)

Revision 8 of this benchmark created on


Setup

const arrayCompare = (a1, a2) => (a1.length === a2.length) && a1.every((v,i) => v === a2[i]);
const dataMock = [
    ['', '', ['']],
    ['I like cat and tac and act', 'act', [ 'cat', 'tac', 'act' ]],
    ['Silent lion lets Inlets stilets enlist Listens', 'listen', [ 'Silent', 'Inlets', 'enlist' ]],
    ['A Aa Aaa aaa aaaa aaaaa aba ', 'aaa', [ 'Aaa', 'aaa' ]],
    ['Aaa ab ba ba ba Ba Ab bab aabaa aba ', 'ab', [ 'ab', 'ba', 'Ba', 'Ab' ] ]

];

Test runner

Ready to run.

Testing in
TestOps/sec
Map
function solution(sentance, targetWord) {
    const lowerTargetWord = targetWord.toLowerCase();
    const words = sentance.split(' ')
            .filter(word => word.toLowerCase() !== lowerTargetWord && targetWord.length === word.length);
    
    const targetWordMap = new Map();
    
    for(let i = 0; i < lowerTargetWord.length; i++) {
        const char = lowerTargetWord[i];
        const num = targetWordMap.get(char) || 0;
        
        targetWordMap.set(char, num + 1);
    }
    
    return words.filter(word => {
        const wordMap = new Map(targetWordMap);
        const lowerWord = word.toLowerCase();        
        
        for (let i = 0; i < word.length; i++) {
            const char = lowerWord[i];
            if (!wordMap.has(char))
            {
                return false;
            }

            let num = wordMap.get(char);
            
            if (--num == 0) {
                wordMap.delete(char);
            } else {
                wordMap.set(char, num);
            }
        }
        
        return wordMap.size === 0;
    });
}



dataMock.forEach((data, index) => {
    const result = solution(data[0], data[1]);
    console.assert(arrayCompare(result, data[2]), `Case #${index}: ${result} expected ${data[2]}`);
});
ready
Optimized Solution
function solution(sentence, word) {
    const words = sentence.split(' ');
    const targetWord = word.toLowerCase();
    const targetWordSize = new Set(targetWord).size;

    const anagrams = words.reduce((result, currentWord) => {
        if (currentWord.length !== word.length) {
            return result;
        }

        const wordSize = new Set(targetWord + currentWord.toLowerCase()).size;

        if (targetWordSize === wordSize) {
            result.add(currentWord);
        }

        return result;
    }, new Set);

    return [...anagrams];
}

dataMock.forEach((data, index) => {
    const result = solution(data[0], data[1]);
    console.assert(arrayCompare(result, data[2]), `Case #${index}: ${result} expected ${data[2]}`);
});
ready
Int
function solution(sentence, word) {
    const words = sentence.split(' ');
    const targetWord = word.toLowerCase();
    const targetWordInt = [...targetWord].reduce((sum, c) => sum + c.charCodeAt(0), 0);

    const anagrams = words.reduce((result, currentWord) => {
        if (currentWord.length !== word.length || targetWord === currentWord.toLowerCase()) {
            return result;
        }

        const wordInt = [...currentWord.toLowerCase()].reduce((sum, c) => sum + c.charCodeAt(0), 0);

        if (targetWordInt === wordInt) {
            result.add(currentWord);
        }

        return result;
    }, new Set);

    return [...anagrams];
}

dataMock.forEach((data, index) => {
    const result = solution(data[0], data[1]);
    console.assert(arrayCompare(result, data[2]), `Case #${index}: ${result} expected ${data[2]}`);
});
ready
ChatGPT
function solution(string, targetWord) {
    // Helper function to check if two strings are anagrams
    function isAnagram(str1, str2) {
      const normalize = (str) => str.toLowerCase().split('').sort().join('');
      return normalize(str1) === normalize(str2);
    }
  
    // Split the string into an array of words
    const words = string.split(' ');
  
    // Filter out anagrams of the target word
    return words.filter(word => isAnagram(word, targetWord) && word.toLowerCase() !== targetWord.toLowerCase());

}


dataMock.forEach((data, index) => {
    const result = solution(data[0], data[1]);
    console.assert(arrayCompare(result, data[2]), `Case #${index}: ${result} expected ${data[2]}`);
});
ready
Optimized 2
function solution(sentence, targetWord) {
    const anagrams = [];
    const targetWordLen = targetWord.length;
    const targetWordLower = targetWord.toLowerCase();
    const words = sentence.split(' ').filter(w => w.length === targetWordLen);
    
    const targetWordSymbols = Object.create(null);
    
    for (let i = 0; i < targetWordLen; i++) {
        const char = targetWordLower[i];

        targetWordSymbols[char] = (targetWordSymbols[char] || 0) + 1;
    }
    
    for (const word of words) {
        const wordLower = word.toLowerCase()
        const wordSymbols = {...targetWordSymbols};
        
        for (let i = 0; i < word.length; i++) {
            const char = wordLower[i];
            
            if (!wordSymbols[char] || wordSymbols[char] < 0) {
                return false;
            }

            wordSymbols[char]--;
        }
        
        if (!Object.values(wordSymbols).find(i => i > 0)) {
            anagrams.push(word);
        }
    }
    return anagrams;        
}

dataMock.forEach((data, index) => {
    const result = solution(data[0], data[1]);
    console.assert(arrayCompare(result, data[2]), `Case #${index}: ${result} expected ${data[2]}`);
});
ready
Optimized 3
function solution(sentence, targetWord) {
    const targetWordLen = targetWord.length;
    const targetWordLower = targetWord.toLowerCase();
    const targetWordSymbols = Object.create(null);

    for (let i = 0; i < targetWordLen; i++) {
        const char = targetWordLower[i];

        targetWordSymbols[char] = (targetWordSymbols[char] || 0) + 1;
    }
    
    return sentence
        .split(' ')
        .filter(word => {            
            if (word.length !== targetWordLen || word.toLowerCase() === targetWordLower) {
                return false;
            }
            
            const wordLower = word.toLowerCase()
            const wordSymbols = {...targetWordSymbols};
            
            for (let i = 0; i < word.length; i++) {
                const char = wordLower[i];
                
                if (!wordSymbols[char] || wordSymbols[char] < 0) {
                    return false;
                }

                wordSymbols[char]--;
            }
            
            return Object.values(wordSymbols).find(i => i > 0) ? false : word;
        }); 
}

dataMock.forEach((data, index) => {
    const result = solution(data[0], data[1]);
    console.assert(arrayCompare(result, data[2]), `Case #${index}: ${result} expected ${data[2]}`);
});
ready
Sort
function solution(sentence, targetWord) {
    const targetWordSorted = targetWord.toLowerCase().split('').sort().join('');
    
    return sentence
        .split(' ')
        .filter(word => {
            if (word.length !== targetWord.length) {
                return false;
            }

            return word.toLowerCase().split('').sort().join('') === targetWordSorted;
        }); 
}

dataMock.forEach((data, index) => {
    const result = solution(data[0], data[1]);
    console.assert(arrayCompare(result, data[2]), `Case #${index}: ${result} expected ${data[2]}`);
});
ready
Interview Solution
function solution(sentence, word) {
    const result = [];
    const targetWord = sortChars(word.toLowerCase());
    const words = sentence.toLowerCase().split(' ').filter(w => w.length === word.length);

    for (let i=0; i< words.length; i++) {
        const current = sortChars(words[i]);

        if (current === targetWord) {
            result.push(words[i]);
        }
    }

    return result;
}

function sortChars(word) {
    return word.split('').sort().join('');
}

dataMock.forEach((data, index) => {
    const result = solution(data[0], data[1]);
    console.assert(arrayCompare(result, data[2]), `Case #${index}: ${result} expected ${data[2]}`);
});
ready
Super optimized
const primeTableMap = new Map([
    [97,2],
    [98,5],
    [99,11],
    [100,17],
    [101,23],
    [102,31],
    [103,41],
    [104,47],
    [105,59],
    [106,67],
    [107,73],
    [108,83],
    [109,97],
    [110,103],
    [111,109],
    [112,127],
    [113,137],
    [114,149],
    [115,157],
    [116,167],
    [117,179],
    [118,191],
    [119,197],
    [120,211],
    [121,227],
    [122,233],
]);

const hash = (s) => {
    let prod = 0;

    for (let i = 0; i < s.length; i++) {
        prod |= primeTableMap.get(s.charCodeAt(i));
    }

    return prod;
};

function solution(sentence, targetWord) {
    const targetWordLen = targetWord.length;
    const targetWordLower = targetWord.toLowerCase();
    const targetWordHash = hash(targetWordLower);
   
    return sentence
        .split(' ')
        .filter(word => {
            
            if (word.length !== targetWordLen) {
                return false;
            }

            return hash(word.toLowerCase()) == targetWordHash;
        });
}

dataMock.forEach((data, index) => {
    const result = solution(data[0], data[1]);
    console.assert(arrayCompare(result, data[2]), `Case #${index}: ${result} expected ${data[2]}`);
});
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.