234

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

4
  • 1
    Are you doing this to mask the text of an input field? Commented Jan 15, 2013 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. Commented Jan 15, 2013 at 18:00
  • possible duplicate of Repeat String - Javascript Commented Jun 2, 2013 at 17:58
  • Today, placeholder is probably recommended. Applying this to all <input>s that have a maxlength looks like document.querySelectorAll("[maxlength]").forEach((element) => element.placeholder = "#".repeat(element.maxLength));. Commented Jan 17, 2022 at 22:12

12 Answers 12

386

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.

Sign up to request clarification or add additional context in comments.

4 Comments

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!
Note, this solution is VERY slow. It looks sexy but is probably exactly the wrong way to do this.
new repeat function built into String.prototype handles this now (ES6+)
Just for information. repeat() is 90% times faster. padEnd() is also 90% faster (with repeat the fastest).
329

Give this a try :P

s = '#'.repeat(10)

document.body.innerHTML = s

6 Comments

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.
If you don't mind using lodash, you could use the following : _.repeat('*',10);
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 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 . . .
definitely the right answer nowadays
|
59

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

1 Comment

Best answer for anyone reading this today.
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:

3 Comments

This is still MUCH slower (by 2 to 3 orders of magnitude) than my solution.
@Hogan Your solution does not create a filled string from nowhere. You create it yourself and then just extract a substring.
This approach is 10x slower than Array.join in Node.js for extremely large strings.
27

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

9 Comments

This is very clever! And it even works with string patterns containing multiple characters.
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.
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 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 - 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.
|
17

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).

5 Comments

Any reason in particular that you would recommend this ES6 approach over the other one ( s = '#'.repeat(10) ) from @user4227915 's answer above?
FYI, padStart and repeat function give different result.
Amazing, I didn't know we have this as a native method!
@MichaelHarley How? '#'.repeat(10)===''.padStart(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.
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

2 Comments

In my browser (Vivaldi 2.6.1566.49 (Stable channel) (64-bit)) your solution takes about double the time than the accepted answer.
@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...
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;
      }
    } 

Comments

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)  
    }  
}  

3 Comments

Please add some more explanations to your answer as a code snippet itself doesn't provide an answer.
@Leandro Clever and minimizes processing! +1
I think you meant hash.length >= length
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;
}

Comments

1

I would do

Buffer.alloc(length, character).toString()

Comments

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);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.