ponyfoo.com

ECMAScript String Padding

Fix
A relevant ad will be displayed here soon. These ads help pay for my hosting.
Please consider disabling your ad blocker on Pony Foo. These ads help pay for my hosting.
You can support Pony Foo directly through Patreon or via PayPal.

A string padding proposal for ECMAScript is at stage 3. Let’s dig into what it entails.

There’s an ECMAScript proposal to standarize String.prototype.padStart and String.prototype.padEnd. String padding is something that we’ve always had come across or implement ourselves one way or another.

Care to take a look?

Cat pads
Cat pads

(No, that’s not the kind of pads I was referring to. Yes, that’s my cat. Yes, she’s on top of my fridge. No, I never question her authority. Fine, I’ll get back on topic.)

Rationale

Before getting into the implementation, it might be useful to read about the rationale behind this addition to the language, which was put together by the people who drafted the proposal.

Without a reasonable way to pad a string using native methods, working with JavaScript strings today is more painful than it should be. Without these functions, the language feels incomplete, and is a paper cut to what could be a very polished experience.

Due to common use, string padding functions exist in a majority of websites and frameworks. For example, nearly every app in FirefoxOS had implemented a left pad function, because they all needed some generic string padding operation.

It is highly probable that the majority of current string padding implementations are inefficient. Bringing this into the platform will improve performance of the web, and developer productivity as they no longer have to implement these common functions.

Let’s look into .padStart first.

String.prototype.padStart(max, fillString)

In its simplest incarnation, .padStart will add enough spaces to the start of a string so that it arrives at the requested length.

'abc'.padStart(6);
// <- '   abc'

When a string’s length is at – or over – the specified max length, no padding characters will be inserted.

'abc'.padStart(3);
// <- 'abc'
'abc'.padStart(2);
// <- 'abc'

Naturally you can change the padding character to something that’s not an space.

'abc'.padStart(5, 'x');
// <- 'xxabc'

The padding doesn’t need to be a single character either. The padding string is repeated as long as needed but it will be sliced to meet the character length requirement.

'abc'.padStart(7, 'xo');
// <- 'xoxoabc'
'abc'.padStart(6, 'xo');
// <- 'xoxabc'

An interesting use case for padStart might be for providing input masks. The example below shows how you could implement such an input mask for ten-digit strings.

'1'.padStart(10, '0');
// <- '0000000001'
'12'.padStart(10, '0');
// <- '0000000012'
'123456'.padStart(10, '0');
// <- '0000123456'

How about dates?

'12'.padStart(10, 'YYYY-MM-DD');
// <- 'YYYY-MM-12'
'09-12'.padStart(10, 'YYYY-MM-DD');
// <- 'YYYY-09-12'

Here’s an adaptation of the reference polyfill stripped off sanity checks and all the usual spec-babble that makes these code snippets harder to read. If you want to use the method in an application today, I suggest you install the string.prototype.padstart module, instead of copying and pasting the polyfill.

if (!String.prototype.padStart) {
  String.prototype.padStart = function (max, fillString) {
    return padStart(this, max, fillString);
  };
}

function padStart (text, max, mask) {
  const cur = text.length;
  if (max <= cur) {
    return text;
  }
  const masked = max - cur;
  let filler = String(mask) || ' ';
  while (filler.length < masked) {
    filler += filler;
  }
  const fillerSlice = filler.slice(0, masked);
  return fillerSlice + text;
}

Time to move onto .padEnd.

String.prototype.padEnd(max, fillString)

This method is equivalent to .padStart except for the fact that padding is appended to the string rather than prepended to it.

'123'.padEnd(6);
// <- '123   '

You can specify the padding character with .padEnd as well.

'123'.padEnd(6, 'x');
// <- '123xxx'

Note that masking preserves the original padding, which may lead to confusion. For example, you may expect the following to produce '123456'.

'123'.padEnd(6, '123456');
// <- '123123'

Similarly, you may want the following to produce '2016-MM-DD'.

'2016'.padEnd(10, 'YYYY-MM-DD');
// <- '2016YYYY-M'

One work-around to get the desired result in these cases could be to just .slice the mask against the length of the original string.

const year = '2016';
year.padEnd(10, 'YYYY-MM-DD'.slice(year.length));
// <- '2016-MM-DD'

Here’s an adaptation of the reference polyfill stripped off sanity checks and all the usual spec-babble that makes these code snippets harder to read. If you want to use the method in an application today, I suggest you install the string.prototype.padend module, instead of copying and pasting the polyfill.

if (!String.prototype.padEnd) {
  String.prototype.padEnd = function (max, fillString) {
    return padEnd(this, max, fillString);
  };
}

function padEnd (text, max, mask) {
  const cur = text.length;
  if (max <= cur) {
    return text;
  }
  const masked = max - cur;
  let filler = String(mask) || ' ';
  while (filler.length < masked) {
    filler += filler;
  }
  const fillerSlice = filler.slice(0, masked);
  return text + fillerSlice;
}

As you can see above, the only difference between the polyfill for .padStart and the one for .padEnd is what side of text the fillerSlice portion of padded fill mask gets concatenated.

If you like this kind of article let me know and I’ll see what I can do to write more like it! 😉

Liked the article? Subscribe below to get an email when new articles come out! Also, follow @ponyfoo on Twitter and @ponyfoo on Facebook.
One-click unsubscribe, anytime. Learn more.

Comments