diff --git a/String/CheckAnagram.js b/String/CheckAnagram.js index 4a7c521b65..6a6bdd830c 100644 --- a/String/CheckAnagram.js +++ b/String/CheckAnagram.js @@ -1,75 +1,23 @@ -// An [Anagram](https://en.wikipedia.org/wiki/Anagram) is a string that is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once. Anagram check is not case-sensitive; /** - * @function checkAnagramRegex - * @param {string} str1 - * @param {string} str2 - * @returns {boolean} - * @description - check anagram with the help of Regex - * @example - checkAnagramRegex('node', 'deno') => true - * @example - checkAnagramRegex('Eleven plus two', 'Twelve plus one') => true + * @function checkAnagram + * @description An anagram is a word or phrase formed by rearranging the + * letters of another word or phrase. This function checks if two strings + * are anagrams of each other, ignoring case and spaces. + * @param {string} str1 - The first input string + * @param {string} str2 - The second input string + * @returns {boolean} - True if the strings are anagrams, false otherwise + * @see https://en.wikipedia.org/wiki/Anagram + * @example checkAnagram("listen", "silent") => true + * @example checkAnagram("hello", "world") => false + * @example checkAnagram("Astronomer", "Moon starer") => true */ -const checkAnagramRegex = (str1, str2) => { - // check that inputs are strings. +const checkAnagram = (str1, str2) => { if (typeof str1 !== 'string' || typeof str2 !== 'string') { - throw new TypeError('Both arguments should be strings.') + throw new TypeError('Both inputs must be strings') } - - // If both strings have not same lengths then they can not be anagram. - if (str1.length !== str2.length) { - return false - } - - /** - * str1 converted to an array and traverse each letter of str1 by reduce method - * reduce method return string which is empty or not. - */ - return ![...str1].reduce( - (str2Acc, cur) => str2Acc.replace(new RegExp(cur, 'i'), ''), // remove the similar letter from str2Acc in case-insensitive - str2 - ) -} - -/** - * @function checkAnagramMap - * @description - check anagram via using HashMap - * @param {string} str1 - * @param {string} str2 - * @returns {boolean} - * @example - checkAnagramMap('node', 'deno') => true - * @example - checkAnagramMap('Eleven plus two', 'Twelve plus one') => true - */ -const checkAnagramMap = (str1, str2) => { - // check that inputs are strings. - if (typeof str1 !== 'string' || typeof str2 !== 'string') { - throw new TypeError('Both arguments should be strings.') - } - - // If both strings have not same lengths then they can not be anagram. - if (str1.length !== str2.length) { - return false - } - - const str1List = Array.from(str1.toUpperCase()) // str1 to array - - // get the occurrences of str1 characters by using HashMap - const str1Occurs = str1List.reduce( - (map, char) => map.set(char, map.get(char) + 1 || 1), - new Map() - ) - - for (const char of str2.toUpperCase()) { - // if char has not exist to the map it's return false - if (!str1Occurs.has(char)) { - return false - } - - let getCharCount = str1Occurs.get(char) - str1Occurs.set(char, --getCharCount) - - getCharCount === 0 && str1Occurs.delete(char) - } - - return true + const normalize = (str) => + str.toLowerCase().replace(/\s/g, '').split('').sort().join('') + return normalize(str1) === normalize(str2) } -export { checkAnagramRegex, checkAnagramMap } +export { checkAnagram } diff --git a/String/test/CheckAnagram.test.js b/String/test/CheckAnagram.test.js index f90bc922f6..da34e6e724 100644 --- a/String/test/CheckAnagram.test.js +++ b/String/test/CheckAnagram.test.js @@ -1,177 +1,43 @@ -import { checkAnagramMap, checkAnagramRegex } from '../CheckAnagram' +import { checkAnagram } from '../CheckAnagram.js' -describe('Testing checkAnagramRegex', () => { - it.each` - inputOne | inputTwo - ${123456} | ${'abcd'} - ${[1, 2, 3, 4, 5, 6]} | ${'abcd'} - ${{ test: 'test' }} | ${'abcd'} - ${'abcd'} | ${123456} - ${'abcd'} | ${[1, 2, 3, 4, 5, 6]} - ${'abcd'} | ${{ test: 'test' }} - `( - 'expects to throw the type Error given values $inputOne and $inputTwo', - ({ inputOne, inputTwo }) => { - expect(() => checkAnagramRegex(inputOne, inputTwo)).toThrowError() - } - ) - - it('expects to return false if the arguments have different lengths', () => { - const SUT = checkAnagramRegex('abs', 'abds') - expect(SUT).toBe(false) - }) - - it('expects to return false if the arguments are not anagrams', () => { - const SUT = checkAnagramRegex('abcs', 'abds') - expect(SUT).toBe(false) - }) - - it('expects to return true if the arguments are anagrams', () => { - const SUT = checkAnagramRegex('abcd', 'bcad') - expect(SUT).toBe(true) - }) - - it('expects to return true if the arguments of length 1 and are the same letter', () => { - const SUT = checkAnagramRegex('a', 'a') - expect(SUT).toBe(true) - }) - - it('expects to return true if the arguments of are both empty strings', () => { - const SUT = checkAnagramRegex('', '') - expect(SUT).toBe(true) - }) - - it('expects to return true if the arguments are anagrams with an odd length', () => { - const SUT = checkAnagramRegex('abcde', 'edcab') - expect(SUT).toBe(true) - }) - - it('expects to return true if the arguments are anagrams with an even length', () => { - const SUT = checkAnagramRegex('abcdef', 'fedcab') - expect(SUT).toBe(true) - }) - - it('expects to return false if either argument is an empty string while the other is not', () => { - const SUT = checkAnagramRegex('', 'edcab') - expect(SUT).toBe(false) - const SUT2 = checkAnagramRegex('edcab', '') - expect(SUT2).toBe(false) - }) - - it('expects to return true if the arguments contain the same letters but have unequal case', () => { - const SUT = checkAnagramRegex('ABDCE', 'abcde') - expect(SUT).toBe(true) - const SUT2 = checkAnagramRegex('AbCdE', 'aBCdE') - expect(SUT2).toBe(true) - const SUT3 = checkAnagramRegex('Eleven plus two', 'Twelve plus one') - expect(SUT3).toBe(true) - }) - - it('expects to return true if the arguments are anagrams and contain number characters', () => { - const SUT = checkAnagramRegex('a1b2', '12ba') - expect(SUT).toBe(true) - }) - - it('expects to return true if the arguments are anagrams and contain space characters', () => { - const SUT = checkAnagramRegex('a1 b2', '1 2ba') - expect(SUT).toBe(true) - }) - - it('expects to return true if the arguments are anagrams and contain punctuation characters', () => { - const SUT = checkAnagramRegex('a!1b@2', '1@2ba!') - expect(SUT).toBe(true) - }) - - it('expects to return false if the arguments contain the same letters but contain a different amount of space characters', () => { - const SUT = checkAnagramRegex('ea cb', 'e cba') - expect(SUT).toBe(false) - }) -}) - -describe('Testing checkAnagramMap', () => { - it.each` - inputOne | inputTwo - ${123456} | ${'abcd'} - ${[1, 2, 3, 4, 5, 6]} | ${'abcd'} - ${{ test: 'test' }} | ${'abcd'} - ${'abcd'} | ${123456} - ${'abcd'} | ${[1, 2, 3, 4, 5, 6]} - ${'abcd'} | ${{ test: 'test' }} - `( - 'expects to throw the type Error given values $inputOne and $inputTwo', - ({ inputOne, inputTwo }) => { - expect(() => checkAnagramMap(inputOne, inputTwo)).toThrowError() - } - ) - - it('expects to return false if the arguments have different lengths', () => { - const SUT = checkAnagramMap('abs', 'abds') - expect(SUT).toBe(false) - }) - - it('expects to return false if the arguments are not anagrams', () => { - const SUT = checkAnagramMap('abcs', 'abds') - expect(SUT).toBe(false) - }) - - it('expects to return true if the arguments are anagrams', () => { - const SUT = checkAnagramMap('abcd', 'bcad') - expect(SUT).toBe(true) - }) - - it('expects to return true if the arguments of length 1 and are the same letter', () => { - const SUT = checkAnagramMap('a', 'a') - expect(SUT).toBe(true) +describe('checkAnagram', () => { + test("'listen' and 'silent' are anagrams", () => { + expect(checkAnagram('listen', 'silent')).toBe(true) }) - it('expects to return true if the arguments of are both empty strings', () => { - const SUT = checkAnagramMap('', '') - expect(SUT).toBe(true) + test("'hello' and 'world' are NOT anagrams", () => { + expect(checkAnagram('hello', 'world')).toBe(false) }) - it('expects to return true if the arguments are anagrams with an odd length', () => { - const SUT = checkAnagramMap('abcde', 'edcab') - expect(SUT).toBe(true) + test("'Astronomer' and 'Moon starer' are anagrams", () => { + expect(checkAnagram('Astronomer', 'Moon starer')).toBe(true) }) - it('expects to return true if the arguments are anagrams with an even length', () => { - const SUT = checkAnagramMap('abcdef', 'fedcab') - expect(SUT).toBe(true) + test("'triangle' and 'integral' are anagrams", () => { + expect(checkAnagram('triangle', 'integral')).toBe(true) }) - it('expects to return false if either argument is an empty string while the other is not', () => { - const SUT = checkAnagramMap('', 'edcab') - expect(SUT).toBe(false) - const SUT2 = checkAnagramMap('edcab', '') - expect(SUT2).toBe(false) + test('empty strings are anagrams', () => { + expect(checkAnagram('', '')).toBe(true) }) - it('expects to return true if the arguments contain the same letters but have unequal case', () => { - const SUT = checkAnagramMap('ABDCE', 'abcde') - expect(SUT).toBe(true) - const SUT2 = checkAnagramMap('AbCdE', 'aBCdE') - expect(SUT2).toBe(true) - const SUT3 = checkAnagramMap('Eleven plus two', 'Twelve plus one') - expect(SUT3).toBe(true) + test('case insensitive check', () => { + expect(checkAnagram('Listen', 'Silent')).toBe(true) }) - it('expects to return true if the arguments are anagrams and contain number characters', () => { - const SUT = checkAnagramMap('a1b2', '12ba') - expect(SUT).toBe(true) + test('different lengths are NOT anagrams', () => { + expect(checkAnagram('abc', 'ab')).toBe(false) }) - it('expects to return true if the arguments are anagrams and contain space characters', () => { - const SUT = checkAnagramMap('a1 b2', '1 2ba') - expect(SUT).toBe(true) + test("'abc' and 'cba' are anagrams", () => { + expect(checkAnagram('abc', 'cba')).toBe(true) }) - it('expects to return true if the arguments are anagrams and contain punctuation characters', () => { - const SUT = checkAnagramMap('a!1b@2', '1@2ba!') - expect(SUT).toBe(true) + test('throws TypeError for non-string first input', () => { + expect(() => checkAnagram(123, 'silent')).toThrow(TypeError) }) - it('expects to return false if the arguments contain the same letters but contain a different amount of space characters', () => { - const SUT = checkAnagramMap('ea cb', 'e cba') - expect(SUT).toBe(false) + test('throws TypeError for non-string second input', () => { + expect(() => checkAnagram('listen', 123)).toThrow(TypeError) }) })