Create a string of variable length, filled with a repeated character - javascript

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

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.

Give this a try :P
s = '#'.repeat(10)
document.body.innerHTML = s

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

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:
Run speed test in your own browser
Full source code of speed test
Speed test results

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

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

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 :

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

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

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

I would do
Buffer.alloc(length, character).toString()

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

Related

Undefined in Split String

i have a function to split string into 2 part, front and back. Then reverse it to back and front. Here is my code
function reverseString(string) {
let splitString = ""
let firstString = ""
for(i = 0; i <= string.length/2 - 1; i++) {
firstString += string[i]
}
for(i = string.length/2; i <= string.length; i++) {
splitString += string[i]
}
return splitString + firstString
}
Sorry for bad explanation, this is test case and expected result (first one is expected result, the second one is my result)
console.log(reverseString("aaabccc")); // "cccbaaa" "undefinedundefinedundefinedundefinedaaa"
console.log(reverseString("aab")); // "baa" "undefinedundefineda"
console.log(reverseString("aaaacccc")); // "ccccaaaa" "ccccundefinedaaa"
console.log(reverseString("abcdefghabcdef")); // "habcdefabcdefg" "habcdefundefinedabcdefg"
could you help me, whats wrong with it. Thank you
You could try another approach and use the slice function
function reverseString(string)
{
if (string.length < 2) { return string; }
let stringHalfLength = string.length / 2;
let isLengthOdd = stringHalfLength % 1 !== 0;
if (isLengthOdd) {
return string.slice(Math.ceil(stringHalfLength), string.length + 1) + string[Math.floor(stringHalfLength)] + string.slice(0, Math.floor(stringHalfLength));
}
return string.slice(stringHalfLength, string.length + 1) + string.slice(0, stringHalfLength);
}
console.log(reverseString("aaabccc") === "cccbaaa");
console.log(reverseString("aab") === "baa");
console.log(reverseString("aaaacccc") === "ccccaaaa");
console.log(reverseString("abcdefghabcdef") === "habcdefabcdefg");
A more efficient way to reverse the string would be to split the string, then use the built-in reverse javascript function (which reverses the elements of the split string), and then re-join the elements using the join function.. No need to re-invent the wheel?
You can concatenate the functions in shorthand (.split.reverse.join etc...) so your function would look something like this:
function reverseString(string) {
return string.split("").reverse().join("");
}
Try it out!
function reverseString(string) {
return string.split("").reverse().join("");
}
console.log(reverseString("hello"));
console.log(reverseString("aaabbbccc"));
If there's a particular reason you're opting not to use the in-built functions (i.e. if I've missed something?) , feel free to comment.
The short version of what you need:
function reverseString(string) {
const splitPosition = Math.ceil(string.length / 2);
return string.substring(splitPosition) + string.substring(0, splitPosition);
}
The key to your question is the middle element. To accomplish that, you probably want to use Math.floor that round under.
console.log(reverseString("aaabccc")); // "cccbaaa"
console.log(reverseString("abcdefghabcdef")); // "habcdefabcdefg"
function reverseString (str) {
if (str.length<2) {
return str
}
var half = Math.floor(str.length / 2);
return (str.slice(-half) + (str.length%2?str[half]:"") + str.slice(0,half));
}
reverseString('')
> ""
reverseString('1')
> "1"
reverseString('12')
> "21"
reverseString('123')
> "321"
reverseString('1234')
> "3412"
reverseString('12345')
> "45312"
reverseString("aaabccc")
> "cccbaaa"
reverseString("abcdefghabcdef")
> "habcdefabcdefg"
So basically your problem is not to grab 2 parts of the string and rearrange, it is to grab 3 parts.
1 part: str.slice(0,half)
2 part: str.length%2 ? str[half] : ""
3 part: str.slice(-half)
The second part is empty if the string length is even and the middle character if is odd.
So the code version in long self explanatory code:
function reverseString (str) {
if (str.length<2) {
return str
}
var half = Math.floor(str.length / 2);
var firstPart = str.slice(0,half);
var midlePart = str.length % 2 ? str[half] : ""; // we could expand also this
var endPart = str.slice(-half);
return endPart + midlePart + firstPart;
}
And also, notice the precondition, so I don't have to deal with the easy cases.
Also, in your code, you got undefined because you access in the last loop to:
string[string.length] you need to change <= by <

How can i make a loop that will show '-' mark x time iteration was? [duplicate]

In Perl I can repeat a character multiple times using the syntax:
$a = "a" x 10; // results in "aaaaaaaaaa"
Is there a simple way to accomplish this in Javascript? I can obviously use a function, but I was wondering if there was any built in approach, or some other clever technique.
These days, the repeat string method is implemented almost everywhere. (It is not in Internet Explorer.) So unless you need to support older browsers, you can simply write:
"a".repeat(10)
Before repeat, we used this hack:
Array(11).join("a") // create string with 10 a's: "aaaaaaaaaa"
(Note that an array of length 11 gets you only 10 "a"s, since Array.join puts the argument between the array elements.)
Simon also points out that according to this benchmark, it appears that it's faster in Safari and Chrome (but not Firefox) to repeat a character multiple times by simply appending using a for loop (although a bit less concise).
In a new ES6 harmony, you will have native way for doing this with repeat. Also ES6 right now only experimental, this feature is already available in Edge, FF, Chrome and Safari
"abc".repeat(3) // "abcabcabc"
And surely if repeat function is not available you can use old-good Array(n + 1).join("abc")
Convenient if you repeat yourself a lot:
String.prototype.repeat = String.prototype.repeat || function(n){
n= n || 1;
return Array(n+1).join(this);
}
alert( 'Are we there yet?\nNo.\n'.repeat(10) )
Array(10).fill('a').join('')
Although the most voted answer is a bit more compact, with this approach you don't have to add an extra array item.
An alternative is:
for(var word = ''; word.length < 10; word += 'a'){}
If you need to repeat multiple chars, multiply your conditional:
for(var word = ''; word.length < 10 * 3; word += 'foo'){}
NOTE: You do not have to overshoot by 1 as with word = Array(11).join('a')
The most performance-wice way is https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
Short version is below.
String.prototype.repeat = function(count) {
if (count < 1) return '';
var result = '', pattern = this.valueOf();
while (count > 1) {
if (count & 1) result += pattern;
count >>>= 1, pattern += pattern;
}
return result + pattern;
};
var a = "a";
console.debug(a.repeat(10));
Polyfill from Mozilla:
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;
}
}
If you're not opposed to including a library in your project, lodash has a repeat function.
_.repeat('*', 3);
// → '***
https://lodash.com/docs#repeat
For all browsers
The following function will perform 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'd use it like this :
var repeatedString = 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.
For moderns browsers only
In modern browsers, you can now do this using String.prototype.repeat method:
var repeatedString = "a".repeat(10);
Read more about this method on MDN.
This option is even faster. 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:
In ES2015/ES6 you can use "*".repeat(n)
So just add this to your projects, and your are good to go.
String.prototype.repeat = String.prototype.repeat ||
function(n) {
if (n < 0) throw new RangeError("invalid count value");
if (n == 0) return "";
return new Array(n + 1).join(this.toString())
};
String.repeat() is supported by 96.39% of browsers as of now.
function pad(text, maxLength){
return text + "0".repeat(maxLength - text.length);
}
console.log(pad('text', 7)); //text000
/**
* Repeat a string `n`-times (recursive)
* #param {String} s - The string you want to repeat.
* #param {Number} n - The times to repeat the string.
* #param {String} d - A delimiter between each string.
*/
var repeat = function (s, n, d) {
return --n ? s + (d || "") + repeat(s, n, d) : "" + s;
};
var foo = "foo";
console.log(
"%s\n%s\n%s\n%s",
repeat(foo), // "foo"
repeat(foo, 2), // "foofoo"
repeat(foo, "2"), // "foofoo"
repeat(foo, 2, "-") // "foo-foo"
);
Just for the fun of it, here is another way by using the toFixed(), used to format floating point numbers.
By doing
(0).toFixed(2)
(0).toFixed(3)
(0).toFixed(4)
we get
0.00
0.000
0.0000
If the first two characters 0. are deleted, we can use this repeating pattern to generate any repetition.
function repeat(str, nTimes) {
return (0).toFixed(nTimes).substr(2).replaceAll('0', str);
}
console.info(repeat('3', 5));
console.info(repeat('hello ', 4));
Another interesting way to quickly repeat n character is to use idea from quick exponentiation algorithm:
var repeatString = function(string, n) {
var result = '', i;
for (i = 1; i <= n; i *= 2) {
if ((n & i) === i) {
result += string;
}
string = string + string;
}
return result;
};
For repeat a value in my projects i use repeat
For example:
var n = 6;
for (i = 0; i < n; i++) {
console.log("#".repeat(i+1))
}
but be careful because this method has been added to the ECMAScript 6 specification.
function repeatString(n, string) {
var repeat = [];
repeat.length = n + 1;
return repeat.join(string);
}
repeatString(3,'x'); // => xxx
repeatString(10,'🌹'); // => "🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹"
This is how you can call a function and get the result by the helps of Array() and join()
using Typescript and arrow fun
const repeatString = (str: string, num: number) => num > 0 ?
Array(num+1).join(str) : "";
console.log(repeatString("🌷",10))
//outputs: 🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷
function repeatString(str, num) {
// Array(num+1) is the string you want to repeat and the times to repeat the string
return num > 0 ? Array(num+1).join(str) : "";
}
console.log(repeatString("a",10))
// outputs: aaaaaaaaaa
console.log(repeatString("🌷",10))
//outputs: 🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷
Here is what I use:
function repeat(str, num) {
var holder = [];
for(var i=0; i<num; i++) {
holder.push(str);
}
return holder.join('');
}
I realize that it's not a popular task, what if you need to repeat your string not an integer number of times?
It's possible with repeat() and slice(), here's how:
String.prototype.fracRepeat = function(n){
if(n < 0) n = 0;
var n_int = ~~n; // amount of whole times to repeat
var n_frac = n - n_int; // amount of fraction times (e.g., 0.5)
var frac_length = ~~(n_frac * this.length); // length in characters of fraction part, floored
return this.repeat(n) + this.slice(0, frac_length);
}
And below a shortened version:
String.prototype.fracRepeat = function(n){
if(n < 0) n = 0;
return this.repeat(n) + this.slice(0, ~~((n - ~~n) * this.length));
}
var s = "abcd";
console.log(s.fracRepeat(2.5))
I'm going to expand on #bonbon's answer. His method is an easy way to "append N chars to an existing string", just in case anyone needs to do that. For example since "a google" is a 1 followed by 100 zeros.
for(var google = '1'; google.length < 1 + 100; google += '0'){}
document.getElementById('el').innerText = google;
<div>This is "a google":</div>
<div id="el"></div>
NOTE: You do have to add the length of the original string to the conditional.
Lodash offers a similar functionality as the Javascript repeat() function which is not available in all browers. It is called _.repeat and available since version 3.0.0:
_.repeat('a', 10);
var stringRepeat = function(string, val) {
var newString = [];
for(var i = 0; i < val; i++) {
newString.push(string);
}
return newString.join('');
}
var repeatedString = stringRepeat("a", 1);
Can be used as a one-liner too:
function repeat(str, len) {
while (str.length < len) str += str.substr(0, len-str.length);
return str;
}
In CoffeeScript:
( 'a' for dot in [0..10]).join('')
String.prototype.repeat = function (n) { n = Math.abs(n) || 1; return Array(n + 1).join(this || ''); };
// console.log("0".repeat(3) , "0".repeat(-3))
// return: "000" "000"

How to append strings in linear time

I want to change a string with dashes placed randomly in between groups of characters to a string with dashes in between groups of n characters. I would like to keep this at a worst case of O(n) time complexity. The following soln works but I believe that the string concatenation is slow and would prefer a constant time operation in order to maintain O(n).
//Desired string is 15-678435-555339
let s = "1-5678-43-5555-339"
let newString = ""
let counter = 0
let n = 6
if(s.length === 1 || s.length < k) return s
for(let i = s.length-1; i >= 0; i--){
if(counter === 6) {
counter = 0;
newString = "-" + newString
}
if(s.charAt(i) !== "-") {
counter += 1
newString = s.charAt(i) + newString
}
}
As this is javascript you usually get the most performant solution with the least code, which would be:
function* chunk(iterable, size) {
for(let i = iterable.length; i >= 0; i -= size)
yield iterable.slice(Math.max(0, i - size), i);
}
let result = [...chunk(s.replace(/-/g, ""), 6)].reverse().join("-");
(But thats just speculation, that heavily depends on the engine)
Hmm, I thought that the string concat is expensive, making it well above o(n).
Usually yes, but some very agressive inlining might optimize it away.
If this is a real life problem and not homework with arbitrary constraints, you should just concatenate the strings. On a modern javascript, and especially for short strings like you've got there, this will not present a performance problem under normal conditions.
If you really wanted to minimize the number of strings created, you could construct an integer array of character codes using .charCodeAt(i), and then s = String.fromCharCode.apply(null, arrayOfIntegers). But this isn't something you'd normally have to do.
Not sure what language you are operating in, but most have some concept of a StringBuilder, which is just implemented as an ArrayList of strings underneath and concatenated when you ask for the resulting string - usually via a toString() method.
Here is a Java example: https://ideone.com/vHHdHH
public static void main(String[] args) {
String s = "1-5678-43-5555-339";
StringBuilder sb = new StringBuilder();
int dashPosition = 2;
int count = 0;
char[] ch = s.toCharArray();
for (int i = 0; i < ch.length; i++) {
if (Character.getNumericValue(ch[i]) != -1) {
sb.append(ch[i]);
count++;
if (count % dashPosition == 0) {
sb.append('-');
count = 0;
dashPosition = 6;
}
}
}
if (sb.charAt(sb.length() - 1) == '-') {
sb.deleteCharAt(sb.length() - 1);
}
//Desired string is 15-678435-555339
System.out.println(sb.toString());
}

interpolate tags in strings using only text offsets

I've been struggling with javascript string methods and regexes, and I may be overlooking something obvious. I hope I violate no protocol by restating tofutim's question in some more detail. Responses to his question focus upon s.replace(), but for that to work, you have to know which occurrence of a substring to replace, replace all of them, or be able to identify somehow uniquely the string to replace by means of a regex. Like him, I only have an array of text offsets like this:
[[5,9], [23,27]]
and a string like this:
"eggs eggs spam and ham spam"
Given those constraints, is there a straightforward way (javaScript or some shortcut with jQuery) to arrive at a string like this?
"eggs <span>eggs</span> spam and ham <span>spam</span>"
I don't know in advance what the replacement strings are, or how many occurrences of them there might be in the base text. I only know their offsets, and it is only the occurrences identified by their offsets that I want to wrap with tags.
any thoughts?
I found a way to do it with regexp. Not sure about performance, but it's short and sweet:
/**
* replaceOffset
* #param str A string
* #param offs Array of offsets in ascending order [[2,4],[6,8]]
* #param tag HTML tag
*/
function replaceOffset(str, offs, tag) {
tag = tag || 'span';
offs.reverse().forEach(function(v) {
str = str.replace(
new RegExp('(.{'+v[0]+'})(.{'+(v[1]-v[0])+'})'),
'$1<'+tag+'>$2</'+tag+'>'
);
});
return str;
}
Demo: http://jsbin.com/aqowum/3/edit
iquick solution (not tested)
function indexWrap(indexArr,str){
// explode into array of each character
var chars = str.split('');
// loop through the MD array of indexes
for(var i=0; i<indexArr.length;i++){
var indexes = indexArr[i];
// if the two indexes exist in the character array
if(chars[indexes[0]] && chars[indexes[1]]){
// add the tag into each index
chars.splice(indexes[0],0,"<span>");
chars.splice(indexes[1],0,"</span>");
}
}
// return the joined string
return chars.join('');
}
Personally, I like a string replace solution, but if you dont want one, this might work
You can try slice method.
var arr = [[5,9], [23,27]];
arr = arr.reverse()
$.each(arr, function(i, v){
var f = v[0], last = v[1];
$('p').html(function(i, v){
var o = v.slice(0, f);
var a = '<span>' + v.slice(f, last) + '</span>';
var c = v.slice(last, -1);
return o+a+c
})
})
http://jsfiddle.net/rjQt7/
First, you'd want to iterate backwards, in order to make sure you won't eventually overwrite the replacements previously made, however, in my example it is not important because the string is reassembled all at once in the very end.
// > interpolateOnIndices([[5,9], [23,27]], "eggs eggs spam and ham spam");
// < 'eggs <span>eggs</span> spam and ham <span>spam</span>'
function interpolateOnIndices(indices, string) {
"use strict";
var i, pair, position = string.length,
len = indices.length - 1, buffer = [];
for (i = len; i >= 0; i -= 1) {
pair = indices[i];
buffer.unshift("<span>",
string.substring(pair[0], pair[1]),
"</span>",
string.substring(pair[1], position));
position = pair[0];
}
buffer.unshift(string.substr(0, position));
return buffer.join("");
}
This is a little bit better then the example with spliceing, because it doesn't create additional arrays (splice in itself will create additional arrays). Using mapping and creating functions repeatedly inside other functions is a certain memory hog, but it doesn't run very fast either... Although, it is a little bit shorter.
On large strings joining should, theoretically, give you an advantage over multiple concatenations because memory allocation will be made once, instead of subsequently throwing away a half-baked string. Of course, all these need not concern you, unless you are processing large amounts of data.
EDIT:
Because I had too much time on my hands, I decided to make a test, to see how variations will compare on a larger (but fairly realistic) set of data, below is my testing code with some results...
function interpolateOnIndices(indices, string) {
"use strict";
var i, pair, position = string.length,
len = indices.length - 1, buffer = [];
for (i = len; i >= 0; i -= 1) {
pair = indices[i];
buffer.unshift("<span>",
string.substring(pair[0], pair[1]),
"</span>",
string.substring(pair[1], position));
position = pair[0];
}
buffer.unshift(string.substr(0, position));
return buffer.join("");
}
function indexWrap(indexArr, str) {
var chars = str.split("");
for(var i = 0; i < indexArr.length; i++) {
var indexes = indexArr[i];
if(chars[indexes[0]] && chars[indexes[1]]){
chars.splice(indexes[0], 0, "<span>");
chars.splice(indexes[1], 0, "</span>");
}
}
return chars.join("");
}
function replaceOffset(str, offs, tag) {
tag = tag || "span";
offs.reverse().forEach(
function(v) {
str = str.replace(
new RegExp("(.{" + v[0] + "})(.{" + (v[1] - v[0]) + "})"),
"$1<" + tag + ">$2</" + tag + ">"
);
});
return str;
}
function generateLongString(pattern, times) {
"use strict";
var buffer = new Array(times);
while (times >= 0) {
buffer[times] = pattern;
times -= 1;
}
return buffer.join("");
}
function generateIndices(pattern, times, step) {
"use strict";
var buffer = pattern.concat(), block = pattern.concat();
while (times >= 0) {
block = block.concat();
block[0] += step;
block[1] += step;
buffer = buffer.concat(block);
times -= 1;
}
return buffer;
}
var longString = generateLongString("eggs eggs spam and ham spam", 100);
var indices = generateIndices([[5,9], [23,27]], 100,
"eggs eggs spam and ham spam".length);
function speedTest(thunk, times) {
"use strict";
var start = new Date();
while (times >= 0) {
thunk();
times -= 1;
}
return new Date() - start;
}
speedTest(
function() {
replaceOffset(longString, indices, "span"); },
100); // 1926
speedTest(
function() {
indexWrap(indices, longString); },
100); // 559
speedTest(
function() {
interpolateOnIndices(indices, longString); },
100); // 16
Tested against V8 (Node.js) on amd64 Linux (FC-17).
I didn't test the undefined's answer because I didn't want to load that library, especially so it doesn't do anything useful for this test. I would imagine it will lend somewhere between andbeyond's and elclanrs's variants, more towards elclanrs's answer though.
you may use the substring method
String.substring (startIndex, endIndex);
description: return the string between start & end index
usage:
var source="hello world";
var result=source.substring (3,7); //returns 'lo wo'
you already have an array with initial & final index, so you are almost done :)

Most efficient way to generate a really long string (tens of megabytes) in JS

I find myself needing to synthesize a ridiculously long string (like, tens of megabytes long) in JavaScript. (This is to slow down a CSS selector-matching operation to the point where it takes a measurable amount of time.)
The best way I've found to do this is
var really_long_string = (new Array(10*1024*1024)).join("x");
but I'm wondering if there's a more efficient way - one that doesn't involve creating a tens-of-megabytes array first.
For ES6:
'x'.repeat(10*1024*1024)
The previously accepted version uses String.prototype.concat() which is vastly slower than using the optimized string concatenating operator, +. MDN also recommends to keep away from using it in speed critical code.
I have made three versions of the above code to show the speed differences in a JsPerf. Converting it to using only using concat is only a third as fast as only using the string concatenating operator (Chrome - your mileage will vary). The edited version below will run twice as fast in Chrome
var x = '1234567890'
var iterations = 14
for (var i = 0; i < iterations; i++) {
x += x + x
}
This is the more efficient algorithm for generating very long strings in javascript:
function stringRepeat(str, num) {
num = Number(num);
var result = '';
while (true) {
if (num & 1) { // (1)
result += str;
}
num >>>= 1; // (2)
if (num <= 0) break;
str += str;
}
return result;
}
more info here: http://www.2ality.com/2014/01/efficient-string-repeat.html.
Alternatively, in ECMA6 you can use String.prototype.repeat() method.
Simply accumulating is vastly faster in Safari 5:
var x = "1234567890";
var iterations = 14;
for (var i = 0; i < iterations; i++) {
x += x.concat(x);
}
alert(x.length); // 47829690
Essentially, you'll get x.length * 3^iterations characters.
Not sure if this is a great implementation, but here's a general function based on #oligofren's solution:
function repeat(ch, len) {
var result = ch;
var halfLength = len / 2;
while (result.length < len) {
if (result.length <= halfLength) {
result += result;
} else {
return result + repeat(ch, len - result.length);
}
}
return result;
}
This assumes that concatenating a large string is faster than a series of small strings.

Categories

Resources