I have this string :
var a='abc123#xyz123';
I want to build 2 regexes replace functions which :
1) Replace all characters that do have a future '#' - with '*' (not including '#')
so the result should look like :
'******#xyz123'
2) Replace all characters that do not have a future '#' - with '*' (not including '#')
so the result should look like :
'abc123#******'
What have I tried :
For the positive lookahead :
var a='abc123#xyz123';
alert(a.replace(/(.+(?=#))+/ig,'*')); //*#xyz123 --wrong result since it is greedy...
Question :
How can I make my regexes work as expected ?
First part using lookahead:
repl = a.replace(/.(?=[^#]*#)/g, "*");
//=> "******#xyz123"
Explanation:
This regex finds any character that is followed by # using lookahead and replaced that with *.
Second part using replace callback:
repla = a.replace(/#(.*)$/, function(m, t) { return m[0] + t.replace(/./g, '*'); } );
//=> abc123#******
Explanation:
This code finds text after #. Inside the callback function is replaces every character with asterisk.
You can use indexOf and substr for this instead:
function maskBeforeAfter(before, str, character, maskCharacter) {
character = character || '#';
maskCharacter = maskCharacter || '*';
var characterPosition = str.indexOf(character);
if (characterPosition > -1) {
var mask = '';
if (before) {
for (var i = 0; i < characterPosition; i++) {
mask += maskCharacter;
}
return mask + str.substr(characterPosition);
} else {
for (var i = 0; i < str.length - characterPosition - 1; i++) {
mask += maskCharacter;
}
return str.substr(0, characterPosition + 1) + mask;
}
}
return str;
}
function maskBefore(str, character, maskCharacter) {
return maskBeforeAfter(true, str, character, maskCharacter);
}
function maskAfter(str, character, maskCharacter) {
return maskBeforeAfter(false, str, character, maskCharacter);
}
> var a = 'abc12345#xyz123';
> maskBefore(a);
"********#xyz123"
> maskAfter(a);
"abc12345#******"
If you insist on a simple regex:
The first one is already answered. The second can be written similarly:
a.replace(/[^#](?![^#]*#)/g, '*')
(?![^#]*#) is a negative lookahead that checks that there isn't a pound after the current character.
[^#] also checks that the current character isn't a pound. (we could have also used /(?![^#]*#)./g, but it is less pretty.
A positive option is:
a.replace(/[^#](?=[^#]*$)/g, '*');
this is very similar to the first one: (?=[^#]*$) checks that we have only non-pounds ahead, until the end of the string.
In both of this options, all characters in strings with no pounds will be replaces: "abcd" -> "****"
Related
I have a kebabize function which converts camelCase to kebab-case. I am sharing my code. Can it be more optimized? I know this problem can be solved using regex. But, I want to do it without using regex.
const kebabize = str => {
let subs = []
let char = ''
let j = 0
for( let i = 0; i < str.length; i++ ) {
char = str[i]
if(str[i] === char.toUpperCase()) {
subs.push(str.slice(j, i))
j = i
}
if(i == str.length - 1) {
subs.push(str.slice(j, str.length))
}
}
return subs.map(el => (el.charAt(0).toLowerCase() + el.substr(1, el.length))).join('-')
}
kebabize('myNameIsStack')
const kebabize = str => {
return str.split('').map((letter, idx) => {
return letter.toUpperCase() === letter
? `${idx !== 0 ? '-' : ''}${letter.toLowerCase()}`
: letter;
}).join('');
}
console.log(kebabize('myNameIsStack'));
console.log(kebabize('MyNameIsStack'));
You can just check every letter is if upperCase or not and replace it.
I have a one-liner similar to Marc's but with a simpler Regular Expression and ~20% faster according my benchmark (Chrome 89).
const kebabize = (str) => str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, ($, ofs) => (ofs ? "-" : "") + $.toLowerCase())
const words = ['StackOverflow', 'camelCase', 'alllowercase', 'ALLCAPITALLETTERS', 'CustomXMLParser', 'APIFinder', 'JSONResponseData', 'Person20Address', 'UserAPI20Endpoint'];
console.log(words.map(kebabize));
[A-Z]+(?![a-z]) matches any consecutive capital letters, excluding any capitals followed by a lowercase (signifying the next word). Adding |[A-Z] then includes any single capital letters. It must be after the consecutive capital expression, otherwise the expression will match all capital letters individually and never match consecutives.
String.prototype.replace can take a replacer function. Here, it returns the lowercased matched capital(s) for each word, after prefixing a hyphen when the match offset is truthy (not zero - not the first character of the string).
I suspect Marc's solution is less performant than mine because by using replace to insert hyphens and lowercasing the whole string afterwards, it must iterate over the string more than once, and its expression also has more complex look aheads/behind constructs.
Benchmark
RegEx is faster!
Unlike what you might think, the RegEx way of doing this is actually significantly faster! See benchmark.
The function below supports converting both camelCase and PascalCase into kebab-case:
function toKebabCase(str) {
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
}
Here is my solution:
Works with camelCase and PascalCase:
let words = ['StackOverflow', 'camelCase', 'alllowercase', 'ALLCAPITALLETTERS', 'CustomXMLParser', 'APIFinder', 'JSONResponseData', 'Person20Address', 'UserAPI20Endpoint'];
let result = words.map(w => w.replace(/((?<=[a-z\d])[A-Z]|(?<=[A-Z\d])[A-Z](?=[a-z]))/g, '-$1').toLowerCase());
console.log(result);
/*
Returns:
[
"stack-overflow",
"camel-case",
"alllowercase",
"allcapitalletters",
"custom-xml-parser",
"api-finder",
"json-response-data",
"person20-address",
"user-api20-endpoint"
]
*/
Explanation:
Match any of the following regular expressions:
Find any capital letter, that is immediately preceeded by a small letter or a number, or
Find any capital letter, that is immediately preceeded by a capital letter or a number, that is immediately followed by a small letter
Replace the captured position with a dash ('-') followed by the captured capital letter
Finally, convert the whole string to lowercase.
I would use something like this.
function kebabize(string) {
// uppercase after a non-uppercase or uppercase before non-uppercase
const upper = /(?<!\p{Uppercase_Letter})\p{Uppercase_Letter}|\p{Uppercase_Letter}(?!\p{Uppercase_Letter})/gu;
return string.replace(upper, "-$&").replace(/^-/, "").toLowerCase();
}
const strings = ["myNameIsStack", "HTTPRequestData", "DataX", "Foo6HelloWorld9Bar", "Áb"];
const result = strings.map(kebabize);
console.log(result);
This snippet replaces all uppercase characters before or after a non-uppercase character with - followed by the uppercase. It then removes the - at the start of the string (if there is any) and downcases the whole string.
Simple solution for older browsers:
var str = 'someExampleString'
var i
function camelToKebab() {
var __str = arguments[0]
var __result = ''
for (i = 0; i < __str.length; i++) {
var x = __str[i]
if(x === x.toUpperCase()) {
__result += '-' + x.toLowerCase()
} else {
__result += x
}
}
return __result
}
console.log(str, '->', camelToKebab(str))
Here is the solution I came up with:
let resultDiv = document.querySelector(".result");
let camelCase = "thisIsCamelCase";
let kebabCase;
kebabCase = camelCase.split('').map(el=> {
const charCode = el.charCodeAt(0);
if(charCode>=65 && charCode<=90){
return "-" + el.toLowerCase()
}else{
return el;
}
})
return(kebabCase.join(''))
Im trying to replace a character at a specific indexOf to uppercase.
My string is a surname plus the first letter in the last name,
looking like this: "lovisa t".
I check the position with this and it gives me the right place in the string. So the second gives me 8(in this case).
first = texten.indexOf(" ");
second = texten.indexOf(" ", first + 1);
And with this I replace the first letter to uppercase.
var name = texten.substring(0, second);
name=name.replace(/^./, name[0].toUpperCase());
But how do I replace the character at "second" to uppercase?
I tested with
name=name.replace(/.$/, name[second].toUpperCase());
But it did´t work, so any input really appreciated, thanks.
Your error is the second letter isn't in position 8, but 7.
Also this second = texten.indexOf(" ", first + 1); gives -1, not 8, because you do not have a two spaces in your string.
If you know that the string is always in the format surname space oneLetter and you want to capitalize the first letter and the last letter you can simply do this:
var name = 'something s';
name = name[0].toUpperCase() + name.substring(1, name.length - 1) + name[name.length -1].toUpperCase();
console.log(name)
Here's a version that does exactly what your question title asks for: It uppercases a specific index in a string.
function upperCaseAt(str, i) {
return str.substr(0, i) + str.charAt(i).toUpperCase() + str.substr(i + 1);
}
var str = 'lovisa t';
var i = str.indexOf(' ');
console.log(upperCaseAt(str, i + 1));
However, if you want to look for specific patterns in the string, you don't need to deal with indices.
var str = 'lovisa t';
console.log(str.replace(/.$/, function (m0) { return m0.toUpperCase(); }));
This version uses a regex to find the last character in a string and a replacement function to uppercase the match.
var str = 'lovisa t';
console.log(str.replace(/ [a-z]/, function (m0) { return m0.toUpperCase(); }));
This version is similar but instead of looking for the last character, it looks for a space followed by a lowercase letter.
var str = 'lovisa t';
console.log(str.replace(/(?:^|\s)\S/g, function (m0) { return m0.toUpperCase(); }));
Finally, here we're looking for (and uppercasing) all non-space characters that are preceded by the beginning of the string or a space character; i.e. we're uppercasing the start of each (space-separated) word.
All can be done by regex replace.
"lovisa t".replace(/(^|\s)\w/g, s=>s.toUpperCase());
Try this one (if it will be helpfull, better move constants to other place, due performance issues(yes, regexp creation is not fast)):
function normalize(str){
var LOW_DASH = /\_/g;
var NORMAL_TEXT_REGEXP = /([a-z])([A-Z])/g;
if(!str)str = '';
if(str.indexOf('_') > -1) {
str = str.replace(LOW_DASH, ' ');
}
if(str.match(NORMAL_TEXT_REGEXP)) {
str = str.replace(NORMAL_TEXT_REGEXP, '$1 $2');
}
if(str.indexOf(' ') > -1) {
var p = str.split(' ');
var out = '';
for (var i = 0; i < p.length; i++) {
if (!p[i])continue;
out += p[i].charAt(0).toUpperCase() + p[i].substring(1) + (i !== p.length - 1 ? ' ' : '');
}
return out;
} else {
return str.charAt(0).toUpperCase() + str.substring(1);
}
}
console.log(normalize('firstLast'));//First Last
console.log(normalize('first last'));//First Last
console.log(normalize('first_last'));//First Last
I'm trying to match a specific URL(http://www.example.me/area/?si=) that allows me to get value from si. si value will be dynamic
http://www.example.me/area/?si=4077765
Get any query string value
function qstr(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
}
}
return false;
}
Check Query String Exists
function isset_qstr(field) {
var url = vp_furl();
if (url.indexOf('?' + field + '=') != -1) {
return true;
} else if (url.indexOf('&' + field + '=') != -1) {
return true;
}
return false;
}
I think you need the first function. Vote up if you think its helpful.
Thank you. Good Luck
Assuming that the value of the si key in the query string will always be digits (ie: 0 - 9), try this...
var url = 'http://www.example.me/area/?test=4894&si=4077765&f=fjjjf',
si = url.match(/.*[\?&]si=(\d+)/i)[1];
or a little more generic...
function getQueryStringValue(url, key) {
var regex = new RegExp('.*[\\?&]' + key + '=(\\d+)', 'i');
return url.match(regex)[1];
}
if not digits try this...
/.*[\?&]si=(\w+)/i
Explanation:
.* matches any character (except newline) between 0 and unlimited times
[\?&] match a single character, either ? or &
si= matches the characters si= literally (case insensitive)
(\d+) first capturing group. matches any digit [0-9] between 1 and unlimitted times
i modifier - case insensitive
regex101
Something like that can do the trick I think:
var regex = /http\:\/\/www\.example\.me\/area\/\?si=(\w*)/;
var url = 'http://www.example.me/area/?si=4077765';
var si = url.match(regex)[1]; // si = '4077765'
The first part of the regex is simply your URL "\" is used to escape special characters.
(\d+) is a capturing group that matches all character from a-z, A-Z, 0-9, including the _ (underscore) character from 0 to n iteration.
I am trying to convert below function to a simple regex check to find illegal pattern in my search app.
The rules are
Any string without * :Not match.
* by itself :Not match.
if * is present and length of the string is less than 3 (ignoring space):Match
Here is what makes it tricky, | and (space)or(space) in my app results in running two different searches so therefore it has to evaluate the strings on each side separately. just like below function. the function works but the regexCheck doesn't.
var regexCheck = /^[A-Za-z0-9]*$/g;
var check = function (s) {
var flag = true;
var b = s.replace(/ or /g,'|').split('|');
for (var i = 0; i < b.length; i++) {
if (flag && b[i].indexOf('*') > - 1) {
var startCount = (b[i].match(/[*]/g) || []).length;
var limit = (startCount > 0) ? 2 : 3;
var c = b[i].replace(/[ *]/g, '');
if (c.length < limit && c.length > 0) {
flag = false;
break;
}
}
}
if (flag) {
console.log('good:' + s);
} else {
console.log('fail:' + s);
}
return flag;
};
var s = [
'a',
'*',
'a*',
'*a',
'a*a',
'a*a|aa',
'a*a|a',
'a*a|a*',
'a*a or a*',
];
var j = 0;
while (j < s.length) {
check(s[j]);
if(regexCheck.test(s[j])){
console.log('rg-good:' + s[j]);
} else {
console.log('rg-fail:' + s[j]);
}
j++;
}
Ok, still not sure if I got you correctly, so please test and tell me if that is what you want:
/^.*(?:^| or |\|)(?=.?\*)(\**)[^\s\*](\**)(?=$| or |\|).*$/
Here is the version that ignores whitespaces:
/^.*(?:^| or |\|)\s*(?=\s*.?\s*\*)\s*(\*\s*)*[^\s\*]\s*(\*\s*)*(?=$| or |\|).*$/
To make it easier to understand, I will explain the basic one:
Surround everything with ^.* and .*$ - this will make us match the entire string if one match is found. Aka if we have a*|aa, we will match the whole thing, not just a*. If you want to match only the subpattern - you can remove those.
Surround everything with (?:^| or |\|) and (?=$| or |\|) - will make us evaluate subpatterns one at a time. Aka separate each attempt to match with either line start/end, or |, or or.
(?=.?\*) - positive lookahead that says "in the following pattern, either the first or the second character has to be a *. By the 3rd rule, the first found star shouldn't be at third or later position, nor are we allowed not to have a star by the 1st rule.
(\**)[^\s\*](\**) - match any amount of *, followed by a character, that is not a star or a whitespace, followed by any amount of *.
I'm trying to make a Regex in JavaScript to match each not escaped specific characters.
Here I'm looking for all the ' characters. They can be at the beginning or the end of the string, and consecutive.
E.g.:
'abc''abc\'abc
I should get 3 matchs: the 1st, 5 and 6th character. But not 11th which escaped.
You'll have to account for cases like \\' which should match, and \\\' which shouldn't. but you don't have lookbehinds in JS, let alone variable-length lookbehinds, so you'll have to use something else.
Use the following regex:
\\.|(')
This will match both all escaped characters and the ' characters you're looking for, but the quotes will be in a capture group.
Look at this demo. The matches you're interested in are in green, the ones to ignore are in blue.
Then, in JS, ignore each match object m where !m[1].
Example:
var input = "'abc''abc\\'abc \\\\' abc";
var re = /\\.|(')/g;
var m;
var positions = [];
while (m = re.exec(input)) {
if (m[1])
positions.push(m.index);
}
var pos = [];
for (var i = 0; i < input.length; ++i) {
pos.push(positions.indexOf(i) >= 0 ? "^" : " ");
}
document.getElementById("output").innerText = input + "\n" + pos.join("");
<pre id="output"></pre>
You can use:
var s = "'abc''abc\\'abc";
var cnt=0;
s.replace(/\\?'/g, function($0) { if ($0[0] != '\\') cnt++; return $0;});
console.log(cnt);
//=> 3