diff --git a/README.md b/README.md index 9d81b22..8f93c4b 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,6 @@ const count = stringzy.analyze.wordCount('Hello world'); // 2 - [isMacAddress](#ismacaddress)- Checks if a given string is a valid MAC address. - [isPanagram](#ispanagram)- Checks if a given string is a pangram (contains every letter of the English alphabet at least once). - ### Analysis - [wordCount](#wordcount) - Counts the number of words in a string @@ -116,7 +115,7 @@ const count = stringzy.analyze.wordCount('Hello world'); // 2 - [vowelConsonantCount](#vowelconsonantcount) - Counts the number of vowels and consonants in a given string - [checkMultiplePatterns](#checkmultiplepatterns) - Finds occurrences of multiple patterns within a given text using Rabin–Karp algorithm (case sensitive) - [checkSubsequence](#checksubsequence) - Checks whether the second string is a subsequence of the first string (case sensitive) - +- [stringRotation](#stringrotation) - Checks if one string is a rotation of another (case sensitive). ### Formatting - [capitalize](#capitalize) - Capitalizes the first letter of each word @@ -1000,9 +999,6 @@ functionWordCount("Can you see the stars tonight?"); - `count` (number): Total number of function words in the string - - - #### `patternCount(text, pattern)` Counts the number of times a substring (pattern) occurs in a string, including overlapping occurrences. @@ -1096,6 +1092,38 @@ isSubsequence('a b c', 'abc'); | str | string | required | The main string to check within. | | sub | string | required | The subsequence string to verify against `str`. | +#### checkStringRotations(str1, str2) +Checks whether a given string `str2` is a rotation of another string `str1`. +Case-sensitive comparison is performed. Both strings must be of equal length to be considered rotations. +Spaces and all characters are treated literally. + +```javascript +isRotation('waterbottle', 'erbottlewat'); +// true → rotation at position 3 + +isRotation('abcde', 'cdeab'); +// true → rotation at position 2 + +isRotation('abc', 'abc'); +// true → no rotation, identical strings + +isRotation('abc', 'cab'); +// true → rotation at position 2 + +isRotation('abc', 'bac'); +// false → not a valid rotation + +isRotation('ArB', 'Bar'); +// false → case-sensitive mismatch + +isRotation('abcd', 'abc'); +// false → lengths differ +``` + +| Parameter | Type | Default | Description | +| --------- | ------ | -------- | --------------------------------------------------- | +| str1 | string | required | The original string. | +| str2 | string | required | The string to verify if it is a rotation of `str1`. | --- diff --git a/src/analyzing/index.ts b/src/analyzing/index.ts index 141ed05..9130acd 100644 --- a/src/analyzing/index.ts +++ b/src/analyzing/index.ts @@ -8,10 +8,9 @@ export { patternCount } from './patternCount'; export { vowelConsonantCount } from './vowelConsonantCount'; export { checkMultiplePatterns } from './checkMultiplePatterns'; export { checkSubsequence } from './checkSubsequence'; - export { functionWordCount } from './functionWordCount'; export { contentWordCount } from './contentWordCount'; - +export { checkStringRotations } from './stringRotation'; import { characterCount } from './characterCount'; import { characterFrequency } from './characterFrequency'; @@ -23,8 +22,7 @@ import { patternCount } from './patternCount'; import { vowelConsonantCount } from './vowelConsonantCount'; import { checkMultiplePatterns } from './checkMultiplePatterns'; import { checkSubsequence } from './checkSubsequence'; - - +import { checkStringRotations } from './stringRotation'; export const analyzing = { characterCount, @@ -36,5 +34,6 @@ export const analyzing = { patternCount, vowelConsonantCount, checkMultiplePatterns, - checkSubsequence + checkSubsequence, + checkStringRotations }; diff --git a/src/analyzing/stringRotation.ts b/src/analyzing/stringRotation.ts new file mode 100644 index 0000000..6ed0617 --- /dev/null +++ b/src/analyzing/stringRotation.ts @@ -0,0 +1,42 @@ +/** + * Checks if one string is a rotation of another. + * + * A string `str2` is a rotation of `str1` if it can be obtained by shifting + * the characters of `str1` in a circular fashion. + * + * The check is case-sensitive and supports special characters and numbers. + * + * @param {string} str1 - The original string. + * @param {string} str2 - The string to check if it is a rotation of str1. + * @returns {boolean} True if str2 is a rotation of str1, otherwise false. + * @throws {TypeError} If either input is not a string. + * + * @example + * checkStringRotations("abcd", "cdab"); // true + * + * @example + * checkStringRotations("abc", "acb"); // false + * + * @example + * checkStringRotations("hello", "ohell"); // true + * + * @example + * checkStringRotations("", ""); // true + * + * @example + * checkStringRotations("abc", "ab"); // false + */ +export function checkStringRotations(str1: string, str2: string): boolean { + if (typeof str1 !== 'string' || typeof str2 !== 'string') { + throw new TypeError('Both inputs must be strings'); + } + + // Edge case: both empty strings + if (str1 === '' && str2 === '') return true; + + // If lengths differ, they cannot be rotations + if (str1.length !== str2.length) return false; + + // Concatenate str1 with itself and check if str2 is a substring + return (str1 + str1).includes(str2); +} diff --git a/src/tests/analyzing/stringRotation.test.ts b/src/tests/analyzing/stringRotation.test.ts new file mode 100644 index 0000000..561572a --- /dev/null +++ b/src/tests/analyzing/stringRotation.test.ts @@ -0,0 +1,44 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert'; +import { checkStringRotations } from '../../analyzing/stringRotation'; + +describe('checkStringRotations', () => { + it('returns true for valid rotations', () => { + assert.strictEqual(checkStringRotations('abcd', 'cdab'), true); + assert.strictEqual(checkStringRotations('hello', 'ohell'), true); + assert.strictEqual(checkStringRotations('12345', '45123'), true); + assert.strictEqual(checkStringRotations('!@#$', '#$!@'), true); + }); + + it('returns false for invalid rotations', () => { + assert.strictEqual(checkStringRotations('abc', 'acb'), false); + assert.strictEqual(checkStringRotations('hello', 'helol'), false); + assert.strictEqual(checkStringRotations('12345', '54321'), false); + }); + + it('is case-sensitive', () => { + assert.strictEqual(checkStringRotations('ArB', 'Bar'), false); + assert.strictEqual(checkStringRotations('Case', 'case'), false); + assert.strictEqual(checkStringRotations('XYZ', 'yzx'), false); + }); + + it('returns true for empty strings', () => { + assert.strictEqual(checkStringRotations('', ''), true); + }); + + it('returns false for strings of different lengths', () => { + assert.strictEqual(checkStringRotations('abc', 'ab'), false); + assert.strictEqual(checkStringRotations('abcd', ''), false); + }); + + it('handles special characters correctly', () => { + assert.strictEqual(checkStringRotations('a@b$c', 'b$ca@'), true); + assert.strictEqual(checkStringRotations('a@b$c', 'c$a@b'), false); + }); + + it('throws an error if inputs are not strings', () => { + assert.throws(() => checkStringRotations(123 as any, '123'), /Both inputs must be strings/); + assert.throws(() => checkStringRotations('abc', null as any), /Both inputs must be strings/); + assert.throws(() => checkStringRotations(undefined as any, 'abc'), /Both inputs must be strings/); + }); +});