mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-05 16:26:47 +08:00
merge: Upgrade checkAnagram
function & Fixes (#902)
* pref: optimize the algo via reduce & replace method * feat: add TypeError for invalid types * test: upgrade test case for invalid types * docs: add js doc * test: modify the case-sensitive test case * pref: Optimize algo & add case-insensitive mode * style: format with standard style * docs: fix the js doc * docs: rename function name & add comments * feat: add chackAnagramViaMap function * test: add test case for checkAnagramViaMap func * fix: remove **Via** from functions name * style: fix alignment of js doc * chore: grammar fix Co-authored-by: Rak Laptudirm <raklaptudirm@gmail.com>
This commit is contained in:
@ -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.
|
// 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;
|
||||||
// Anagram check is case sensitive; i.e. Aba and aba is not a anagram.
|
/**
|
||||||
// inputs are strings i.e. str1 and str2
|
* @function checkAnagramRegex
|
||||||
const checkAnagram = (str1, str2) => {
|
* @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.
|
// check that inputs are strings.
|
||||||
if (typeof str1 !== 'string' || typeof str2 !== 'string') {
|
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.
|
// If both strings have not same lengths then they can not be anagram.
|
||||||
@ -12,36 +19,58 @@ const checkAnagram = (str1, str2) => {
|
|||||||
return false
|
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++) {
|
// If both strings have not same lengths then they can not be anagram.
|
||||||
let previousCount = 0
|
if (str1.length !== str2.length) {
|
||||||
if (str1CharCount.has(str1[i])) {
|
return false
|
||||||
previousCount = str1CharCount.get(str1[i])
|
}
|
||||||
|
|
||||||
|
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++) {
|
getCharCount === 0 && str1Occurs.delete(char)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
export { checkAnagram }
|
export { checkAnagramRegex, checkAnagramMap }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { checkAnagram } from '../CheckAnagram'
|
import { checkAnagramMap, checkAnagramRegex } from '../CheckAnagram'
|
||||||
|
|
||||||
describe('checkAnagram', () => {
|
describe('Testing checkAnagramRegex', () => {
|
||||||
it.each`
|
it.each`
|
||||||
inputOne | inputTwo
|
inputOne | inputTwo
|
||||||
${123456} | ${'abcd'}
|
${123456} | ${'abcd'}
|
||||||
@ -10,79 +10,172 @@ describe('checkAnagram', () => {
|
|||||||
${'abcd'} | ${[1, 2, 3, 4, 5, 6]}
|
${'abcd'} | ${[1, 2, 3, 4, 5, 6]}
|
||||||
${'abcd'} | ${{ test: 'test' }}
|
${'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 }) => {
|
({ inputOne, inputTwo }) => {
|
||||||
const SUT = checkAnagram(inputOne, inputTwo)
|
expect(
|
||||||
expect(SUT).toBe('Not string(s)')
|
() => checkAnagramRegex(inputOne, inputTwo)
|
||||||
|
).toThrowError()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
it('expects to return false if the arguments have different lengths', () => {
|
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)
|
expect(SUT).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('expects to return false if the arguments are not anagrams', () => {
|
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)
|
expect(SUT).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('expects to return true if the arguments are anagrams', () => {
|
it('expects to return true if the arguments are anagrams', () => {
|
||||||
const SUT = checkAnagram('abcd', 'bcad')
|
const SUT = checkAnagramRegex('abcd', 'bcad')
|
||||||
expect(SUT).toBe(true)
|
expect(SUT).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('expects to return true if the arguments of length 1 and are the same letter', () => {
|
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)
|
expect(SUT).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('expects to return true if the arguments of are both empty strings', () => {
|
it('expects to return true if the arguments of are both empty strings', () => {
|
||||||
const SUT = checkAnagram('', '')
|
const SUT = checkAnagramRegex('', '')
|
||||||
expect(SUT).toBe(true)
|
expect(SUT).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('expects to return true if the arguments are anagrams with an odd length', () => {
|
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)
|
expect(SUT).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('expects to return true if the arguments are anagrams with an even length', () => {
|
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)
|
expect(SUT).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('expects to return false if either argument is an empty string while the other is not', () => {
|
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)
|
expect(SUT).toBe(false)
|
||||||
const SUT2 = checkAnagram('edcab', '')
|
const SUT2 = checkAnagramRegex('edcab', '')
|
||||||
expect(SUT2).toBe(false)
|
expect(SUT2).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('expects to return false if the arguments contain the same letters but have unequal case', () => {
|
it('expects to return true if the arguments contain the same letters but have unequal case', () => {
|
||||||
const SUT = checkAnagram('ABDCE', 'abcde')
|
const SUT = checkAnagramRegex('ABDCE', 'abcde')
|
||||||
expect(SUT).toBe(false)
|
expect(SUT).toBe(true)
|
||||||
const SUT2 = checkAnagram('AbCdE', 'aBCdE')
|
const SUT2 = checkAnagramRegex('AbCdE', 'aBCdE')
|
||||||
expect(SUT2).toBe(false)
|
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', () => {
|
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)
|
expect(SUT).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('expects to return true if the arguments are anagrams and contain space characters', () => {
|
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)
|
expect(SUT).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('expects to return true if the arguments are anagrams and contain punctuation characters', () => {
|
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)
|
expect(SUT).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('expects to return false if the arguments contain the same letters but contain a different amount of space characters', () => {
|
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)
|
expect(SUT).toBe(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user