I have a regular expression, say /url.com\/([A-Za-z]+)\.html/, and I would like to replace it with new string $1: f($1), that is, with a constant string with two interpolations, the captured string and a function of the captured string.
What's the best way to do this in JavaScript? 'Best' here means some combination of (1) least error-prone, (2) most efficient in terms of space and speed, and (3) most idiomatically appropriate for JavaScript, with a particular emphasis on #3.
The replace method can take a function as the replacement parameter.
For example:
str.replace(/regex/, function(match, group1, group2, index, original) {
return "new string " + group1 + ": " + f(group1);
});
When using String.replace, you can supply a callback function as the replacement parameter instead of a string and create your own, very custom return value.
'foo'.replace(/bar/, function (str, p1, p2) {
return /* some custom string */;
});
.replace() takes a function for the replace, like this:
var newStr = string.replace(/url.com\/([A-Za-z]+)\.html/, function(all, match) {
return match + " something";
});
You can transform the result however you want, just return whatever you want the match to be in that callback. You can test it out here.
Related
Let's say I have a string:
"__3_"
...which I would like to turn into:
"__###_"
basically replacing an integer with repeated occurrences of # equivalent to the integer value. How can I achieve this?
I understand that backreferences can be used with str.replace()
var str = '__3_'
str.replace(/[0-9]/g, 'x$1x'))
> '__x3x_'
And that we can use str.repeat(n) to repeat string sequences n times.
But how can I use the backreference from .replace() as the argument of .repeat()? For example, this does not work:
str.replace(/([0-9])/g,"#".repeat("$1"))
"__3_".replace(/\d/, function(match){ return "#".repeat(+match);})
if you use babel or other es6 tool it will be
"__3_".replace(/\d/, match => "#".repeat(+match))
if you need replace __11+ with "#".repeat(11) - change regexp into /\d+/
is it what you want?
According https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
str.replace(regexp|substr, newSubStr|function)
and if you use function as second param
function (replacement)
A function to be invoked to create the new substring (to put in place of the >substring received from parameter #1). The arguments supplied to this function >are described in the "Specifying a function as a parameter" section below.
Try this:
var str = "__3_";
str = str.replace(/[0-9]+/, function(x) {
return '#'.repeat(x);
});
alert(str);
Old fashioned approach:
"__3__".replace(/\d/, function (x) {
return Array(+x + 1).join('#');
});
Try this:
var str = "__3_";
str = str.replace(/[0-9]/g,function(a){
var characterToReplace= '#';
return characterToReplace.repeat(a)
});
As a follow up to this question (not by me), I need to replace leading numbers of an id with \\3n (where n is the number we're replacing).
Some examples:
"1foo" -> "\\31foo"
"1foo1" -> "\\31foo1"
"12foo" -> "\\31\\32foo"
"12fo3o4" -> "\\31\\32fo3o4"
"foo123" -> "foo123"
Below is a solution that replaces every instance of the number, but I don't know enough regex to make it stop once it hits a non-number.
function magic (str) {
return str.replace(/([0-9])/g, "\\3$1");
}
... Or is regex a bad way to go? I guess it would be easy enough to do it, just looping over each character of the string manually.
Here is a way to achieve what you need using a reverse string + look-ahead approach:
function revStr(str) {
return str.split('').reverse().join('');
}
var s = "12fo3o4";
document.write(revStr(revStr(s).replace(/\d(?=\d*$)/g, function (m) {
return m + "3\\\\";
}))
);
The regex is matching a number that can be followed by 0 or more numbers only until the end (which is actually start) of a reversed string (with \d(?=\d*$)). The callback allows to manipulate the match (we just add reversed \\ and 3. Then, we just reverse the result.
Just use two steps: first find the prefix, then operate on its characters:
s.replace(/^\d+/, function (m) {
return [].map.call(m, function (c) {
return '\\3' + c;
}).join('');
});
No need to emulate any features.
Here is how I would have done it:
function replace(str) {
var re = /^([\d]*)/;
var match = str.match(re)[0];
var replaced = match.replace(/([\d])/g, "\\3$1");
str = str.replace(match, replaced);
return str;
}
document.write(replace("12fo3o4"));
Don't get me wrong: the other answers are fine! My focus was more on readability.
How can I convert PascalCase string into underscore_case/snake_case string? I need to convert dots into underscores as well.
eg. convert
TypeOfData.AlphaBeta
into
type_of_data_alpha_beta
You could try the below steps.
Capture all the uppercase letters and also match the preceding optional dot character.
Then convert the captured uppercase letters to lowercase and then return back to replace function with an _ as preceding character. This will be achieved by using anonymous function in the replacement part.
This would replace the starting uppercase letter to _ + lowercase_letter.
Finally removing the starting underscore will give you the desired output.
var s = 'TypeOfData.AlphaBeta';
console.log(s.replace(/(?:^|\.?)([A-Z])/g, function (x,y){return "_" + y.toLowerCase()}).replace(/^_/, ""));
OR
var s = 'TypeOfData.AlphaBeta';
alert(s.replace(/\.?([A-Z])/g, function (x,y){return "_" + y.toLowerCase()}).replace(/^_/, ""));
any way to stop it for when a whole word is in uppercase. eg. MotorRPM into motor_rpm instead of motor_r_p_m? or BatteryAAA into battery_aaa instead of battery_a_a_a?
var s = 'MotorRMP';
alert(s.replace(/\.?([A-Z]+)/g, function (x,y){return "_" + y.toLowerCase()}).replace(/^_/, ""));
str.split(/\.?(?=[A-Z])/).join('_').toLowerCase();
u're welcome
var s1 = 'someTextHere';
var s2 = 'SomeTextHere';
var s3 = 'TypeOfData.AlphaBeta';
var o1 = s1.split(/\.?(?=[A-Z])/).join('_').toLowerCase();
var o2 = s2.split(/\.?(?=[A-Z])/).join('_').toLowerCase();
var o3 = s3.split(/\.?(?=[A-Z])/).join('_').toLowerCase();
console.log(o1);
console.log(o2);
console.log(o3);
Alternatively using lodash:
lodash.snakeCase(str);
Example:
_.snakeCase('TypeOfData.AlphaBeta');
// ➜ 'type_of_data_alpha_beta'
Lodash is a fine library to give shortcut to many everyday js tasks.There are many other similar string manipulation functions such as camelCase, kebabCase etc.
This solution solves the non-trailing acronym issue with the solutions above
I ported the code in 1175208 from Python to JavaScript.
Javascript Code
function camelToSnakeCase(text) {
return text.replace(/(.)([A-Z][a-z]+)/, '$1_$2').replace(/([a-z0-9])([A-Z])/, '$1_$2').toLowerCase()
}
Working Examples:
camelToSnakeCase('thisISDifficult') -> this_is_difficult
camelToSnakeCase('thisISNT') -> this_isnt
camelToSnakeCase('somethingEasyLikeThis') -> something_easy_like_this
"alphaBetaGama".replace(/([A-Z])/g, "_$1").toLowerCase() // alpha_beta_gamma
Problem - Need to convert a camel-case string ( such as a property name ) into underscore style to meet interface requirements or for meta-programming.
Explanation
This line uses a feature of regular expressions where it can return a matched result ( first pair of () is $1, second is $2, etc ).
Each match in the string is converted to have an underscore ahead of it with _$1 string provided. At that point the string looks like alpha_Beta_Gamma.
To correct the capitalization, the entire string is converted toLowerCase().
Since toLowerCase is a fairly expensive operation, its best not to put it in the looping handler for each match-case, and run it once on the entire string.
After toLowerCase it the resulting string is alpha_beta_gamma ( in this example )
This will get you pretty far: https://github.com/domchristie/humps
You will probably have to use regex replace to replace the "." with an underscore.
I found this but I edited it so suit your question.
const camelToSnakeCase = str => str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`).replace(/^_/,'')
Good examples for js:
Snake Case
Kebab Case
Camel Case
Pascal Case
have here
function toCamelCase(s) {
// remove all characters that should not be in a variable name
// as well underscores an numbers from the beginning of the string
s = s.replace(/([^a-zA-Z0-9_\- ])|^[_0-9]+/g, "").trim().toLowerCase();
// uppercase letters preceeded by a hyphen or a space
s = s.replace(/([ -]+)([a-zA-Z0-9])/g, function(a,b,c) {
return c.toUpperCase();
});
// uppercase letters following numbers
s = s.replace(/([0-9]+)([a-zA-Z])/g, function(a,b,c) {
return b + c.toUpperCase();
});
return s;
}
Try this function, hope it helps.
"TestString".replace(/[A-Z]/g, val => "_" + val.toLowerCase()).replace(/^_/,"")
replaces all uppercase with an underscore and lowercase, then removes the leading underscore.
A Non-Regex Answer that converts PascalCase to snake_case
Note: I understand there are tons of good answers which solve this question elegantly. I was recently working on something similar to this where I chose not to use regex. So I felt to answer a non-regex solution to this.
const toSnakeCase = (str) => {
return str.slice(0,1).toLowerCase() + str.split('').slice(1).map((char) => {
if (char == char.toUpperCase()) return '_' + char.toLowerCase();
else return char;
}).join('');
}
Eg.
inputString = "ILoveJavascript" passed onto toSnakeCase()
would become "i_love_javascript"
I have some function that uses split, but I want the separator to be a variable. separator will be used several places and it would be nice to only have to change the variable once rather than change hard code everywhere.
But, of course, it doesn't work. The pattern looks correct, but split must have some problem with variables defining the split.
Here is some testing I did in Google Chrome's console.
separator = '|'; // separator to use
var pattern = '/\\' + separator + '\\s*/'; // define pattern
undefined
pattern;
"/\|\s*/"
// pattern is correct
// now define function with constant... variable version commented:
function split( val ) {
//var pattern = '/\\' + separator + '\\s*/';
return val.split( /\|\s*/ );
//return val.split( pattern );
}
undefined
split('asdf|jkl');
["asdf", "jkl"]
// function worked as expected
// now uncomment the variable portions:
function split( val ) {
var pattern = '/\\' + separator + '\\s*/';
//return val.split( /\|\s*/ );
return val.split( pattern );
}
undefined
split('asdf|jkl');
["asdf|jkl"]
// not as expected. string is not split into array
I assume this has something to do with the split function. How do I get this to work? Note that the only reason I think I need to do it this way is because I want to easily be able to change separator. If not for that, I could just hard code everything it would work.
EDIT:
I don't care if it uses regex or not. But it needs to split the string on separator and also make sure the values are trimmed of extra spaces.
Also, I'm modeling my code after the jQuery UI page for autocomplete, here.
And here's the function as they have it. I don't know why they define their own split.
function split( val ) {
return val.split( /,\s*/ );
}
Concat the string, and use it to create an instance of a new RegExp obj.
function split( val ) {
var pattern = new RegExp('\\' + separator + '\\s*');
return val.split( pattern );
}
EDITS
Note that in this particular example, there doesn't appear to be any value over String.prototype.split().
This would be more useful if you wanted to set a limit for all splits of a certain type:
function split( val ) {
var pattern = new RegExp('\\' + separator + '\\s*');
return val.split( pattern , 3);
}
without having to write:
strVar.split(pattern, 3);
Yes, that limit can be stored as a variable and referenced, but then you have to expose that variable throughout the scope of every function you want to use it in.
What if you wanted to change it so it would take an array of strings to split, instead?
You can split on a string or on a regular expression,
but don't mix them up-
var separator='|', val='Split | on | the | pipes';
val.split(RegExp('\\' + separator + '\\s*')).join('\n');
/* returned value: (String)
Split
on
the
pipes
*/
Please use split() and don't have a hard time making your own function
http://www.w3schools.com/jsref/jsref_split.asp
A quick example
<p id="demo">Click the button to display the array values after the split.</p>
<button onclick="myFunction()">Try it</button>
<script>
function myFunction()
{
var str="How are you doing today?";
var n=str.split(" ");
document.getElementById("demo").innerHTML=n;
}
</script>
then just put the pattern on the split() parameter. make sure escape any character you will put on the parameter because the split() thinks you entered a regex. Your | separator is a regex special character and you must escape it with a backslash
Best way is to just use String.split as it can split by using strings itself.
But as you wish to explore RegEx, you need to ensure the separator is correctly escaped before attempting the split:
Here is a working function where separator can be any string:
function split(val, separator) {
var separator = separator.replace(/([^\w\s])/g, '\\$1');
return val.split(new RegExp(separator));
}
split("String|$Array|$Objects", "|$");
> Result ["String", "Array", "Objects"]
The replace function returns the new string with the replaces, but if there weren't any words to replace, then the original string is returned. Is there a way to know whether it actually replaced anything apart from comparing the result with the original string?
A simple option is to check for matches before you replace:
var regex = /i/g;
var newStr = str;
var replaced = str.search(regex) >= 0;
if(replaced){
newStr = newStr.replace(regex, '!');
}
If you don't want that either, you can abuse the replace callback to achieve that in a single pass:
var replaced = false;
var newStr = str.replace(/i/g, function(token){replaced = true; return '!';});
As a workaround you can implement your own callback function that will set a flag and do the replacement. The replacement argument of replace can accept functions.
Comparing the before and after strings is the easiest way to check if it did anything, there's no intrinsic support in String.replace().
[contrived example of how '==' might fail deleted because it was wrong]
Javascript replace is defected by design. Why? It has no compatibility with string replacement in callback.
For example:
"ab".replace(/(a)(b)/, "$1$2")
> "ab"
We want to verify that replace is done in single pass. I was imagine something like:
"ab".replace(/(a)(b)/, "$1$2", function replacing() { console.log('ok'); })
> "ab"
Real variant:
"ab".replace(/(a)(b)/, function replacing() {
console.log('ok');
return "$1$2";
})
> ok
> "$1$2"
But function replacing is designed to receive $0, $1, $2, offset, string and we have to fight with replacement "$1$2". The solution is:
"ab".replace(/(a)(b)/, function replacing() {
console.log('ok');
// arguments are $0, $1, ..., offset, string
return Array.from(arguments).slice(1, -2)
.reduce(function (pattern, match, index) {
// '$1' from strings like '$11 $12' shouldn't be replaced.
return pattern.replace(
new RegExp("\\$" + (index + 1) + "(?=[^\\d]|$)", "g"),
match
);
}, "$1$2");
});
> ok
> "ab"
This solution is not perfect. String replacement itself has its own WATs. For example:
"a".replace(/(a)/, "$01")
> "a"
"a".replace(/(a)/, "$001")
> "$001"
If you want to care about compatibility you have to read spec and implement all its craziness.
If your replace has a different length from the searched text, you can check the length of the string before and after. I know, this is a partial response, valid only on a subset of the problem.
OR
You can do a search. If the search is successfull you do a replace on the substring starting with the found index and then recompose the string. This could be slower because you are generating 3 strings instead of 2.
var test = "Hellllo";
var index = test.search(/ll/);
if (index >= 0) {
test = test.substr(0, index - 1) + test.substr(index).replace(/ll/g, "tt");
}
alert(test);
While this will require multiple operations, using .test() may suffice:
const regex = /foo/;
const yourString = 'foo bar';
if (regex.test(yourString)) {
console.log('yourString contains regex');
// Go ahead and do whatever else you'd like.
}
The test() method executes a search for a match between a regular expression and a specified string. Returns true or false.
With indexOf you can check wether a string contains another string.
Seems like you might want to use that.
have a look at string.match() or string.search()
After doing any RegExp method, read RegExp.lastMatch property:
/^$/.test(''); //Clear RegExp.lastMatch first, Its value will be ''
'abcd'.replace(/bc/,'12');
if(RegExp.lastMatch !== '')
console.log('has been replaced');
else
console.log('not replaced');