Skip to content

splitWords function ​

Splits a string into words, whatever its case style (camelCase, kebab-case, snake_case, spaces…), in one pass. Acronyms and numbers are words of their own.

ts
import { splitWords } from 'utils';

Signature ​

ts
export function splitWords(input: string): string[]

Parameters ​

NameDescription
inputThe identifier or sentence to split, in any case style.

Returns ​

The words, with their original case; empty when there is none.

Example ​

ts
splitWords('XMLHttpRequest'); // ['XML', 'Http', 'Request']
splitWords('user_id-v2 Name'); // ['user', 'id', 'v', '2', 'Name']

Tests ​

Line coverage: 100 %.

  • ✅ splits "helloWorld"
  • ✅ splits "HelloWorld"
  • ✅ splits "hello-world"
  • ✅ splits "hello_world"
  • ✅ splits "HELLO_WORLD"
  • ✅ splits " hello world "
  • ✅ splits "XMLHttpRequest"
  • ✅ splits "getHTTPStatus"
  • ✅ splits "speed2Knots"
  • ✅ splits "élémentÉtat"
  • ✅ splits "a.b/c"
  • ✅ returns no word for ""
  • ✅ returns no word for " "
  • ✅ returns no word for "-_./"

Source ​

src/string/split-words.ts (on GitHub)

Used by ​

  • camelCase: Converts a string to camelCase; acronyms are words (XMLHttpRequest → xmlHttpRequest).
  • constantCase: Converts a string to CONSTANT_CASE: uppercase words joined with _, as in constants and environment variables.
  • dotCase: Converts a string to dot.case: lower-case words joined with ., as in translation keys and property paths.
  • kebabCase: Converts a string to kebab-case: lowercase words joined with -, as in file names, CSS classes and URLs.
  • lowerCase: Converts a string to lower-case words separated by spaces, splitting identifiers such as camelCase.
  • pascalCase: Converts a string to PascalCase: words joined, each capitalized, the rest in lower case.
  • sentenceCase: Converts an identifier to a sentence, to turn a key into a label: the first word capitalized, the others in lower case, acronyms kept.
  • snakeCase: Converts a string to snake_case: lowercase words joined with _, as in database columns and JSON keys.
  • titleCase: Converts a string to Title Case: words separated by spaces, each capitalized, acronyms kept.
  • trainCase: Converts a string to Train-Case: capitalized words joined with -, as in HTTP header names.
  • upperCase: Converts a string to upper-case words separated by spaces, splitting identifiers such as camelCase.
Source code
ts
const DIGIT_PATTERN = /^\p{N}$/v;

type CharacterKind = 'digit' | 'lower' | 'separator' | 'upper';

function isBoundary(previous: CharacterKind, current: CharacterKind): boolean {
  const isLetterToDigit = (previous === 'digit') !== (current === 'digit');
  return previous !== 'separator' && ((previous === 'lower' && current === 'upper') || isLetterToDigit);
}

function kindOf(character: string): CharacterKind {
  if (character !== character.toLowerCase()) {
    return 'upper';
  }
  if (character !== character.toUpperCase()) {
    return 'lower';
  }
  return DIGIT_PATTERN.test(character) ? 'digit' : 'separator';
}

/**
 * Splits a string into words, whatever its case style (camelCase, kebab-case, snake_case, spaces…), in one
 * pass. Acronyms and numbers are words of their own.
 *
 * @param input - The identifier or sentence to split, in any case style.
 * @returns The words, with their original case; empty when there is none.
 * @example
 * splitWords('XMLHttpRequest'); // ['XML', 'Http', 'Request']
 * splitWords('user_id-v2 Name'); // ['user', 'id', 'v', '2', 'Name']
 */
export function splitWords(input: string): string[] {
  const result: string[] = [];
  let word = '';
  let previous: CharacterKind = 'separator';
  for (const character of input) {
    const kind = kindOf(character);
    if (kind === 'separator') {
      if (word !== '') {
        result.push(word);
      }
      word = '';
    } else if (isBoundary(previous, kind)) {
      result.push(word);
      word = character;
    } else if (previous === 'upper' && kind === 'lower' && word.length > 1) {
      const lastCapital = word.slice(-1);
      result.push(word.slice(0, -1));
      word = lastCapital + character;
    } else {
      word += character;
    }
    previous = kind;
  }
  if (word !== '') {
    result.push(word);
  }
  return result;
}
Test file
ts
import { splitWords } from './split-words';

describe(splitWords, () => {
  it.for([
    ['helloWorld', ['hello', 'World']],
    ['HelloWorld', ['Hello', 'World']],
    ['hello-world', ['hello', 'world']],
    ['hello_world', ['hello', 'world']],
    ['HELLO_WORLD', ['HELLO', 'WORLD']],
    ['  hello   world  ', ['hello', 'world']],
    ['XMLHttpRequest', ['XML', 'Http', 'Request']],
    ['getHTTPStatus', ['get', 'HTTP', 'Status']],
    ['speed2Knots', ['speed', '2', 'Knots']],
    ['élémentÉtat', ['élément', 'État']],
    ['a.b/c', ['a', 'b', 'c']],
  ] as const)('splits %j', ([input, expected]) => {
    expect(splitWords(input)).toStrictEqual(expected);
  });

  it.for(['', ' '.repeat(3), '-_./'])('returns no word for %j', (input) => {
    expect(splitWords(input)).toStrictEqual([]);
  });
});

Keywords: split, words, strings, function

Generated from the JSDoc of the sources.