225

So, my question has been asked by someone else in it's Java form here: Java - Create a new String instance with specified length and filled with specific character. Best solution?

. . . but I'm looking for its JavaScript equivalent.

Basically, I'm wanting to dynamically fill text fields with "#" characters, based on the "maxlength" attribute of each field. So, if an input has has maxlength="3", then the field would be filled with "###".

Ideally there would be something like the Java StringUtils.repeat("#", 10);, but, so far, the best option that I can think of is to loop through and append the "#" characters, one at a time, until the max length is reached. I can't shake the feeling that there is a more efficient way to do it than that.

Any ideas?

FYI - I can't simply set a default value in the input, because the "#" characters need to clear on focus, and, if the user didn't enter a value, will need to be "refilled" on blur. It's the "refill" step that I'm concerned with

Community
  • 1
  • 1
talemyn
  • 7,822
  • 4
  • 31
  • 52
  • 1
    Are you doing this to mask the text of an input field? – Matthew Cox Jan 15 '13 at 17:58
  • @MatthewCox: No, it's more to provide a visual display of maxlength. In this case, it's for a set of phone number fields that are split into the 3 parts of the number. It would show that the first 2 fields need 3 digits and the last needs 4. – talemyn Jan 15 '13 at 18:00
  • possible duplicate of [Repeat String - Javascript](http://stackoverflow.com/questions/202605/repeat-string-javascript) – Felix Kling Jun 02 '13 at 17:58
  • Today, [`placeholder`](//developer.mozilla.org/docs/Web/HTML/Element/input#attr-placeholder) is probably recommended. Applying this to all ``s that have a `maxlength` looks like `document.querySelectorAll("[maxlength]").forEach((element) => element.placeholder = "#".repeat(element.maxLength));`. – Sebastian Simon Jan 17 '22 at 22:12

12 Answers12

373

The best way to do this (that I've seen) is

var str = new Array(len + 1).join( character );

That creates an array with the given length, and then joins it with the given string to repeat. The .join() function honors the array length regardless of whether the elements have values assigned, and undefined values are rendered as empty strings.

You have to add 1 to the desired length because the separator string goes between the array elements.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • Nailed it. :) I just needed to remember to use `parseInt()` on the inputs maxlength value before using it to size the array. Just what I was looking for . . . thanks! – talemyn Jan 15 '13 at 18:19
  • 13
    Note, this solution is VERY slow. It looks sexy but is probably exactly the wrong way to do this. – Hogan Sep 30 '15 at 19:10
  • 43
    new `repeat` function built into String.prototype handles this now (ES6+) – AlexMA Oct 18 '16 at 18:05
  • Just for information. repeat() is 90% times faster. padEnd() is also 90% faster (with repeat the fastest). – Patrice Thimothee May 20 '22 at 08:30
294

Give this a try :P

s = '#'.repeat(10)

document.body.innerHTML = s
  • 6
    Definitely the easiest implementation, but also the least supported, since it's part of ECMAScript 6. Looks like, right now, it's only supported in Firefox, Chrome, and desktop version of Safari. – talemyn Jan 15 '16 at 17:41
  • 6
    If you don't mind using [lodash](https://lodash.com/docs#repeat), you could use the following : `_.repeat('*',10);` – Geraud Gratacap Feb 05 '16 at 09:55
  • 8
    It's 2018 now and ES6 is well established - this should be the accepted answer. And in case of needing to support legacy browsers for those not using Babel or something similar, you can use lodash as mentioned above, or a polyfill for backup. – seanTcoyote Jan 13 '18 at 21:11
  • @seanTcoyote As much as I'd like to, there are still people who have to contend with IE 11 and Adroid's Webviews, neither of which support the `repeat()` method. Looking forward to the day when I don't have to care about them anymore, myself . . . – talemyn Sep 24 '18 at 19:41
  • Unfortunately, not compatible with IE – Lorenzo Lerate Apr 23 '19 at 10:26
  • definitely the right answer nowadays – stackers Dec 08 '22 at 18:36
56

ES2015 the easiest way is to do something like

'X'.repeat(data.length)

X being any string, data.length being the desired length.

see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat

Weeks
  • 675
  • 5
  • 7
31

Unfortunately although the Array.join approach mentioned here is terse, it is about 10X slower than a string-concatenation-based implementation. It performs especially badly on large strings. See below for full performance details.

On Firefox, Chrome, Node.js MacOS, Node.js Ubuntu, and Safari, the fastest implementation I tested was:

function repeatChar(count, ch) {
    if (count == 0) {
        return "";
    }
    var count2 = count / 2;
    var result = ch;

    // double the input until it is long enough.
    while (result.length <= count2) {
        result += result;
    }
    // use substring to hit the precise length target without
    // using extra memory
    return result + result.substring(0, count - result.length);
};

This is verbose, so if you want a terse implementation you could go with the naive approach; it still performs betweeb 2X to 10X better than the Array.join approach, and is also faster than the doubling implementation for small inputs. Code:

// naive approach: simply add the letters one by one
function repeatChar(count, ch) {
    var txt = "";
    for (var i = 0; i < count; i++) {
        txt += ch;
    }
    return txt;
}

Further information:

Zero Trick Pony
  • 496
  • 5
  • 5
24

I would create a constant string and then call substring on it.

Something like

var hashStore = '########################################';

var Fiveup = hashStore.substring(0,5);

var Tenup = hashStore.substring(0,10);

A bit faster too.

http://jsperf.com/const-vs-join

Hogan
  • 69,564
  • 10
  • 76
  • 117
  • 1
    This is very clever! And it even works with string patterns containing multiple characters. – Markus Nov 09 '15 at 12:24
  • 1
    Just revisiting this page because it seems to keep getting a lot of attention and wanted to comment on your solution. While I definitely like the simplicity and speed of your approach, my biggest concern with it was around maintenance/reuse. While in my specific scenario, I wasn't needing more than 4 characters, as a generic function, using a fixed length string means that you have to come back and update the string, if your use case ever needs more characters. The other two solutions allow for more flexibility in that regard. +1 for the speed, though. – talemyn Nov 10 '15 at 15:46
  • 1
    Oh, and, on the flip side (and I realize this is really more of a stylistic concern, than a programmatic one), maintaining a 30+ character string that will only ever be used for 3-4 characters didn't sit well with me either . . . again, a minor issue around flexibility of the solution for various use cases. – talemyn Nov 10 '15 at 15:47
  • @talemyn In IT everything is a trade off. Have a 4 character string and make it bigger or have a 30 character string and not worry. All in all, a constant string of any size -- even 1k, is tiny resource usage on modern machines. – Hogan Nov 10 '15 at 18:25
  • @Hogan - totally agree . . . and, that's actually part of the reason that I didn't put a huge emphasis on the speed either. In my specific use case, I was looking for three strings that were 3, 3, and 4 characters in length. While Pointy's solution was slowest, since the strings were short and there were only a few of them, I went with his because I liked the fact that it was streamlined, easy-to-read, and easy-to-maintain. In the end, they are all good solutions for different variations of the question . . . it's all going to depend on what the priorities are for your specific situation. – talemyn Nov 12 '15 at 15:31
  • I'm using a combination of this and the accepted (slow) answer. As I only need to determine the total width of a padded string once, I use the slow answer to build the padding string (timestamp length + 6+1 spaces), and append a portion of it thereafter, like so: `timestamp = new Date().toLocaleTimeString();` `timestamp += timestamp_padding.substring(timestamp.length);` it works wonderfully. – Terra Ashley Jul 27 '16 at 23:46
  • @Hogan -1 You do not create a filled string dynamically, so it does not answer the question directly. Instead, you create a hardcoded string yourself and then just extract a substring from it. But the question was about creating a string with a specified length and char to fill. Something like: `function fill(chr, len) { ... }`. Your solution will not work then. – StanE Aug 19 '16 at 02:02
  • If you're going to hardcode a string, just hardcode it. – mbomb007 Jul 11 '18 at 16:40
  • @mbomb007 sometimes you want strings of different sizes. – Hogan Jul 18 '18 at 16:41
15

A great ES6 option would be to padStart an empty string. Like this:

var str = ''.padStart(10, "#");

Note: this won't work in IE (without a polyfill).

Mendy
  • 7,612
  • 5
  • 28
  • 42
  • 6
    Any reason in particular that you would recommend this ES6 approach over the other one ( `s = '#'.repeat(10)` ) from @user4227915 's answer above? – talemyn Jun 12 '18 at 19:51
  • FYI, padStart and repeat function give different result. – Michael Harley Sep 25 '20 at 15:47
  • Amazing, I didn't know we have this as a native method! – Technotronic Apr 28 '21 at 11:43
  • @MichaelHarley How? '#'.repeat(10)===''.padStart(10, "#"); – EricLaw Jun 15 '21 at 01:10
  • @EricLaw `repeat` and `padStart` has a different use case. For empty string? it is 'kinda' the same. With dynamic string that needs an exact length of a string? You definitely need `padStart` or `padEnd` instead of `repeat`, tho you can reproduce the same result with additional logic. – Michael Harley Jun 18 '21 at 10:00
4

Version that works in all browsers

This function does what you want, and performs a lot faster than the option suggested in the accepted answer :

var repeat = function(str, count) {
    var array = [];
    for(var i = 0; i <= count;)
        array[i++] = str;
    return array.join('');
}

You use it like this :

var repeatedCharacter = repeat("a", 10);

To compare the performance of this function with that of the option proposed in the accepted answer, see this Fiddle and this Fiddle for benchmarks.

Version for moderns browsers only

In modern browsers, you can now also do this :

var repeatedCharacter = "a".repeat(10) };

This option is even faster. However, unfortunately it doesn't work in any version of Internet explorer.

The numbers in the table specify the first browser version that fully supports the method :

enter image description here

Community
  • 1
  • 1
John Slegers
  • 45,213
  • 22
  • 199
  • 169
  • In my browser (Vivaldi 2.6.1566.49 (Stable channel) (64-bit)) your solution takes about double the time than the accepted answer. – markus s Mar 22 '21 at 08:03
  • @markuss : It's roughly the same for me when I run the benchmarks today in the latest Chrome. Note that my answer is more than 4 years old, so the performance difference is probably the result of updates that happened during those 4 years... – John Slegers Mar 23 '21 at 12:08
3
For Evergreen browsers, this will build a staircase based on an incoming character and the number of stairs to build.
function StairCase(character, input) {
    let i = 0;
    while (i < input) {
        const spaces = " ".repeat(input - (i+1));
        const hashes = character.repeat(i + 1);
        console.log(spaces + hashes);
        i++;
    }
}

//Implement
//Refresh the console
console.clear();
StairCase("#",6);   

You can also add a polyfill for Repeat for older browsers

    if (!String.prototype.repeat) {
      String.prototype.repeat = function(count) {
        'use strict';
        if (this == null) {
          throw new TypeError('can\'t convert ' + this + ' to object');
        }
        var str = '' + this;
        count = +count;
        if (count != count) {
          count = 0;
        }
        if (count < 0) {
          throw new RangeError('repeat count must be non-negative');
        }
        if (count == Infinity) {
          throw new RangeError('repeat count must be less than infinity');
        }
        count = Math.floor(count);
        if (str.length == 0 || count == 0) {
          return '';
        }
        // Ensuring count is a 31-bit integer allows us to heavily optimize the
        // main part. But anyway, most current (August 2014) browsers can't handle
        // strings 1 << 28 chars or longer, so:
        if (str.length * count >= 1 << 28) {
          throw new RangeError('repeat count must not overflow maximum string size');
        }
        var rpt = '';
        for (;;) {
          if ((count & 1) == 1) {
            rpt += str;
          }
          count >>>= 1;
          if (count == 0) {
            break;
          }
          str += str;
        }
        // Could we try:
        // return Array(count + 1).join(this);
        return rpt;
      }
    } 
Terry Slack
  • 571
  • 5
  • 6
3

Based on answers from Hogan and Zero Trick Pony. I think this should be both fast and flexible enough to handle well most use cases:

var hash = '####################################################################'

function build_string(length) {  
    if (length == 0) {  
        return ''  
    } else if (hash.length <= length) {  
        return hash.substring(0, length)  
    } else {  
        var result = hash  
        const half_length = length / 2  
        while (result.length <= half_length) {  
            result += result  
        }  
        return result + result.substring(0, length - result.length)  
    }  
}  
Leandro
  • 31
  • 1
1

You can use the first line of the function as a one-liner if you like:

function repeat(str, len) {
    while (str.length < len) str += str.substr(0, len-str.length);
    return str;
}
Sarsaparilla
  • 6,300
  • 1
  • 32
  • 21
1

I would do

Buffer.alloc(length, character).toString()
rexsheng
  • 161
  • 2
  • 3
0

If it's performance you need (prior to ES6), then a combination of substr and a template string is probably best. This function is what I've used for creating space padding strings, but you can change the template to whatever you need:

function strRepeat(intLen, strTemplate) {
  strTemplate = strTemplate || "          ";
  var strTxt = '';
  while(intLen > strTemplate.length) {
    strTxt += strTemplate;
    intLen -= strTemplate.length;
  }
  return ((intLen > 0) ? strTxt + strTemplate.substr(0, intLen) : strTxt);
}
kevstev01
  • 330
  • 5
  • 13