diff --git a/String/CheckAnagram.js b/String/CheckAnagram.js index 21ca1458d..1fc585429 100644 --- a/String/CheckAnagram.js +++ b/String/CheckAnagram.js @@ -1,10 +1,17 @@ -// 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 case sensitive; i.e. Aba and aba is not a anagram. -// inputs are strings i.e. str1 and str2 -const checkAnagram = (str1, str2) => { +// 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 + */ +const checkAnagramRegex = (str1, str2) => { // check that inputs are strings. if (typeof str1 !== 'string' || typeof str2 !== 'string') { - return 'Not string(s)' + throw new TypeError('Both arguments should be strings.') } // If both strings have not same lengths then they can not be anagram. @@ -12,36 +19,58 @@ const checkAnagram = (str1, str2) => { return false } - // Use hashmap to keep count of characters in str1 + /** + * str1 converted to an array and traverse each letter of str1 by reduce method + * reduce method return string which is empty or not. + * if it returns empty string '' -> falsy, with Logical !(NOT) Operator, it's will be converted to boolean and return true else false + */ + return ![...str1].reduce( + (str2Acc, cur) => str2Acc.replace(new RegExp(cur, 'i'), ''), // remove the similar letter from str2Acc in case-insensitive + str2 + ) +} - const str1CharCount = new Map() +/** + * @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.') + } - for (let i = 0; i < str1.length; i++) { - let previousCount = 0 - if (str1CharCount.has(str1[i])) { - previousCount = str1CharCount.get(str1[i]) + // 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 } - str1CharCount.set(str1[i], previousCount + 1) - } - // Now check if second string has same characters? + let getCharCount = str1Occurs.get(char) + str1Occurs.set(char, --getCharCount) - for (let i = 0; i < str2.length; i++) { - let previousCount = 0 - // if str1CharCount has no key for str2[i] then not anagram. - if (!str1CharCount.has(str2[i])) return false - - previousCount = str1CharCount.get(str2[i]) - str1CharCount.set(str2[i], previousCount - 1) - } - - // Now check if all entries in hashmap has zeros. - - for (const key in str1CharCount) { - if (str1CharCount[key] !== 0) return false + getCharCount === 0 && str1Occurs.delete(char) } return true } -export { checkAnagram } +export { checkAnagramRegex, checkAnagramMap } diff --git a/String/test/CheckAnagram.test.js b/String/test/CheckAnagram.test.js index ae1a3a593..44adc196b 100644 --- a/String/test/CheckAnagram.test.js +++ b/String/test/CheckAnagram.test.js @@ -1,6 +1,6 @@ -import { checkAnagram } from '../CheckAnagram' +import { checkAnagramMap, checkAnagramRegex } from '../CheckAnagram' -describe('checkAnagram', () => { +describe('Testing checkAnagramRegex', () => { it.each` inputOne | inputTwo ${123456} | ${'abcd'} @@ -10,79 +10,172 @@ describe('checkAnagram', () => { ${'abcd'} | ${[1, 2, 3, 4, 5, 6]} ${'abcd'} | ${{ test: 'test' }} `( - 'expects to return "Not string(s)" given values $inputOne and $inputTwo', + 'expects to throw the type Error given values $inputOne and $inputTwo', ({ inputOne, inputTwo }) => { - const SUT = checkAnagram(inputOne, inputTwo) - expect(SUT).toBe('Not string(s)') + expect( + () => checkAnagramRegex(inputOne, inputTwo) + ).toThrowError() } ) it('expects to return false if the arguments have different lengths', () => { - const SUT = checkAnagram('abs', 'abds') + const SUT = checkAnagramRegex('abs', 'abds') expect(SUT).toBe(false) }) it('expects to return false if the arguments are not anagrams', () => { - const SUT = checkAnagram('abcs', 'abds') + const SUT = checkAnagramRegex('abcs', 'abds') expect(SUT).toBe(false) }) it('expects to return true if the arguments are anagrams', () => { - const SUT = checkAnagram('abcd', 'bcad') + 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 = checkAnagram('a', 'a') + const SUT = checkAnagramRegex('a', 'a') expect(SUT).toBe(true) }) it('expects to return true if the arguments of are both empty strings', () => { - const SUT = checkAnagram('', '') + const SUT = checkAnagramRegex('', '') expect(SUT).toBe(true) }) it('expects to return true if the arguments are anagrams with an odd length', () => { - const SUT = checkAnagram('abcde', 'edcab') + 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 = checkAnagram('abcdef', 'fedcab') + 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 = checkAnagram('', 'edcab') + const SUT = checkAnagramRegex('', 'edcab') expect(SUT).toBe(false) - const SUT2 = checkAnagram('edcab', '') + const SUT2 = checkAnagramRegex('edcab', '') expect(SUT2).toBe(false) }) - it('expects to return false if the arguments contain the same letters but have unequal case', () => { - const SUT = checkAnagram('ABDCE', 'abcde') - expect(SUT).toBe(false) - const SUT2 = checkAnagram('AbCdE', 'aBCdE') - 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 = checkAnagram('a1b2', '12ba') + 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 = checkAnagram('a1 b2', '1 2ba') + 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 = checkAnagram('a!1b@2', '1@2ba!') + 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 = checkAnagram('ea cb', 'e cba') + 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) + }) + + it('expects to return true if the arguments of are both empty strings', () => { + const SUT = checkAnagramMap('', '') + expect(SUT).toBe(true) + }) + + it('expects to return true if the arguments are anagrams with an odd length', () => { + const SUT = checkAnagramMap('abcde', 'edcab') + expect(SUT).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) + }) + + 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) + }) + + 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) + }) + + it('expects to return true if the arguments are anagrams and contain number characters', () => { + const SUT = checkAnagramMap('a1b2', '12ba') + expect(SUT).toBe(true) + }) + + 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) + }) + + 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) + }) + + 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) }) })