Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Humanize a string in JavaScript

How do I humanize a string? Based on the following criteria:

  • Deletes leading underscores, if any.
  • Replaces underscores with spaces, if any.
  • Capitalizes the first word.

For example:

this is a test -> This is a test
foo Bar Baz    -> Foo bar baz
foo_bar        -> Foo bar
foo_bar_baz    -> Foo bar baz
foo-bar        -> Foo-bar
fooBarBaz      -> FooBarBaz
like image 823
Christian Fazzini Avatar asked Feb 05 '15 04:02

Christian Fazzini


Video Answer


4 Answers

Lodash has _.startCase which is good for humanising object keys. Transforming underscores dashes and camel case into spaces.

In your case you want to capitalise but maintain camel case. This question was asked a while ago. My preference currently would be to create a class that handles the mutations. Its easier to test & maintain. So if in the future you need to support transformations like "1Item" into "First item" you can write one function with a single responsibility.

The below is more computationally expensive but more maintainable. There is one clear function toHumanString which is can easily be understood and modified.

export class HumanizableString extends String {
  capitalizeFirstLetter() => {
    const transformed = this.charAt(0).toUpperCase() + this.slice(1);
    return new HumanizableString(transformed);
  };

  lowerCaseExceptFirst() => {
    const transformed = this.charAt(0) + this.slice(1).toLowerCase();
    return new HumanizableString(transformed);
  };

  camelCaseToSpaces() => {
    const camelMatch = /([A-Z])/g;
    return new HumanizableString(this.replace(camelMatch, " $1"));
  };

  underscoresToSpaces() => {
    const camelMatch = /_/g;
    return new HumanizableString(this.replace(camelMatch, " "));
  };

  toHumanString() => {
    return this.camelCaseToSpaces()
      .underscoresToSpaces()
      .capitalizeFirstLetter()
      .lowerCaseExceptFirst()
      .toString();
  };
}

At the very least you should name your regular expressions to make them more readable.

export const humanise = (value) => {
  const camelMatch = /([A-Z])/g;
  const underscoreMatch = /_/g;

  const camelCaseToSpaces = value.replace(camelMatch, " $1");
  const underscoresToSpaces = camelCaseToSpaces.replace(underscoreMatch, " ");
  const caseCorrected =
    underscoresToSpaces.charAt(0).toUpperCase() +
    underscoresToSpaces.slice(1).toLowerCase();

  return caseCorrected;
};
like image 143
Lex Avatar answered Nov 02 '22 16:11

Lex


Best is indeed to use some regexes:

^[\s_]+|[\s_]+$ catches 1 or more white-space characters or underscores either at the very beginning (^) or at the very end ($) of the string. Note that this also catches new-line characters. Replace them with an empty string.

[_\s]+ Again catches 1 or more white-space characters or underscores, since the ones at the beginning/end of the string are gone, replace with 1 space.

^[a-z] Catch a lowercase letter at the beginning of the string. Replace with the uppercase version of the match (you need a callback function for that).

Combined:

function humanize(str) {
  return str
      .replace(/^[\s_]+|[\s_]+$/g, '')
      .replace(/[_\s]+/g, ' ')
      .replace(/^[a-z]/, function(m) { return m.toUpperCase(); });
}

document.getElementById('out').value = [
  '    this is a test',
  'foo Bar Baz',
  'foo_bar',
  'foo-bar',
  'fooBarBaz',
  '_fooBarBaz____',
  '_alpha',
  'hello_ _world,   how    are________you?  '
].map(humanize).join('\n');
textarea { width:100%; }
<textarea id="out" rows="10"></textarea>
like image 36
asontu Avatar answered Nov 02 '22 16:11

asontu


This covers all your cases:

var tests = [
  'this is a test',
  'foo Bar Baz',
  ...
]

var res = tests.map(function(test) {
  return test
    .replace(/_/g, ' ')
    .trim()
    .replace(/\b[A-Z][a-z]+\b/g, function(word) {
      return word.toLowerCase()
    })
    .replace(/^[a-z]/g, function(first) {
      return first.toUpperCase()
    })
})

console.log(res)
/*
[ 'This is a test',
  'Foo bar baz',
  'Foo bar',
  'Foo-bar',
  'FooBarBaz' ]
*/
like image 26
elclanrs Avatar answered Nov 02 '22 17:11

elclanrs


Although I think a regex expert would be able to do something like this in a one-liner, personally I would do something like this.

function humanize(str) {
  return str.trim().split(/\s+/).map(function(str) {
    return str.replace(/_/g, ' ').replace(/\s+/, ' ').trim();
  }).join(' ').toLowerCase().replace(/^./, function(m) {
    return m.toUpperCase();
  });
}

Tests:

[
  '    this is a test',
  'foo Bar Baz',
  'foo_bar',
  'foo-bar',
  'fooBarBaz',
  '_fooBarBaz____',
  '_alpha',
  'hello_ _world,   how    are________you?  '
].map(humanize);

/* Result:
   [
     "This is a test", 
     "Foo bar baz", 
     "Foo bar", 
     "Foo-bar", 
     "Foobarbaz", 
     "Foobarbaz", 
     "Alpha", 
     "Hello world, how are you?"
   ]
 */
like image 21
istos Avatar answered Nov 02 '22 16:11

istos