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(''))
My string have a two part and separated by /
I want left side string of slash accept any string except "HAHAHA" end of word
And right side string of slash accept any string and allow use "HAHAHA" in end of string
only by Regular Expression and match function to return result parts
For example:
Accept : fooo/baarHAHAHA
Reject : fooHAHAHA/baaar
I want if string have one part, for example baarHAHAHA, accept but result like this:
string: baarHAHAHA
Group1: empty
Group2: baarHAHAHA
Have any idea?
You can try
^(\w*?)(?<!HAHAHA)\/?(\w+)$
Explanation of the above regex:
^, $ - Represents start and end of the line respectively.
(\w*?) - Represents first capturing group capturing the word characters([a-zA-Z0-9_]) zero or more times lazily.
(?<!HAHAHA) - Represents a negative look-behind not matching if the first captured group contains HAHAHA at the end.
\/? - Matches / literally zero or one time.
(\w+) - Represents second capturing group matching word characters([0-9a-zA-Z_]) one or more times.
You can find the demo of the above regex in here.
const regex = /^(\w*?)(?<!HAHAHA)\/?(\w+)$/gm;
const str = `
fooo/baarHAHAHA
fooHAHAHA/baaar
/baar
barHAHAHA
`;
let m;
let resultString = "";
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
if(m[1] === "")resultString = resultString.concat(`GROUP 1: empty\nGROUP 2: ${m[2]}\n`);
else resultString = resultString.concat(`GROUP 1: ${m[1]}\nGROUP 2: ${m[2]}\n`);
}
console.log(resultString);
You don't need regex for this, which is good since it is quite slow. A simple string.split() should be enough to separate the parts. Then you can just check if the word contains "HAHAHA" with the string.endsWith() method.
const a = 'fooHAHAHA/bar';
const b = 'foo/bar';
const c = 'fooHAHAHA';
console.log(a.split('/')); // Array [ 'fooHAHAHA', 'bar' ]
console.log(b.split('/')); // Array [ 'foo', 'bar' ]
console.log(c.split('/')); // Array [ 'fooHAHAHA' ]
// therefore ...
function splitMyString(str) {
const strSplit = str.split('/');
if (strSplit.length > 1) {
if (strSplit[0].endsWith('HAHAHA')) {
return ''; // or whatever you want to do if it gets rejected ...
}
}
return str;
}
console.log('a: ', splitMyString(a)); // ''
console.log('b: ', splitMyString(b)); // foo/bar
console.log('c: ', splitMyString(c)); // fooHAHAHA
Alternative non-regex solution:
const a = 'fooHAHAHA/bar';
const b = 'foo/bar';
const c = 'fooHAHAHA';
function splitMyString(str) {
const separator = str.indexOf('/');
if (separator !== -1) {
const firstPart = str.substring(0, separator);
if (firstPart.endsWith('HAHAHA')) {
return ''; // or whatever you want to do if it gets rejected ...
}
}
return str;
}
console.log('a: ', splitMyString(a)); // ''
console.log('b: ', splitMyString(b)); // foo/bar
console.log('c: ', splitMyString(c)); // fooHAHAHA
var str, re;
function match(rgx, str) {
this.str = str;
this.patt = rgx
var R = [], r;
while (r = re.exec(str)) {
R.push({
"match": r[0],
"groups": r.slice(1)
})
}
return R;
}
str = `
fooo/baarHAHAHA
fooHAHAHA/baaar
/baar
barHAHAHA
barr/bhHAHAHA
`;
re = /(?<=\s|^)(.*?)\/(.*?HAHAHA)(?=\s)/g;
console.log(match(re, str))
Reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
Edit: When I make this code I think to letting user to call the str and when call it, it will return the mactheds and groups. But, if I make like this.str = str and have return too, this.str will be declined.
I want to parse this statement in javascript
["TWA"]["STEL"]
and get TWA, STEL value. I guess this is a json and use JSON.parse() method but doesn't work.
That is not a JSON, but you can easily parse it with the pattern matcher:
https://jsfiddle.net/60dshj3x/
let text = '["TWA"]["STEL"]'
let results = text.match(/\["(.*)"\]\["(.*)"]/)
// note that results[0] is always the entire string!
let first = results[1]
let second = results[2]
console.log("First: " + first + "\nSecond: " + second);
If it is a string then a simple regex will do the trick.
const regex = /\["(\w+)"\]/gm;
const str = `["TWA"]["STEL"]`;
let m;
let words = [];
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
// The result can be accessed through the `m`-variable.
m.forEach((match, groupIndex) => {
if(groupIndex===1)words.push(match)
});
}
console.log(words.join(','))
I have following Text, I want to extract Rock Smash & Dig from the below text. I tried this regex, but it doesn't seem to work..
str:
__**Cubone 100%**__Panama
Despawns # 03:40:33am __**(29m 55s)**__
Weather Boost: **Clear :sunny:**
Level: 19 - IV: 100% (15/15/15) - CP: 5120
- **Rock Smash**
- **Dig**
**Map Info**: https://maps.google.com/?q=35.1224486753,-106.6278869713
var getRegex = /(?:-\s+\*+)[\w\s\w]+/mg;
var res = getRegex.exec(str);
console.log(res)
You don't need to add 2 times \w in a character class, you could just add it once [\w\s].
Try it like this with a capturing group
(?:-\s+\*+)([\w\s]+)
Or like this:
- \*\*([^*]+)\*\*
Your matches are in group 1 ([\w\s\w]+).
Then you could loop the matches:
var regex = /(?:-\s+\*+)([\w\s\w]+)/g;
var str = `__**Cubone 100%**__Panama
Despawns # 03:40:33am __**(29m 55s)**__
Weather Boost: **Clear :sunny:**
Level: 19 - IV: 100% (15/15/15) - CP: 5120
- **Rock Smash**
- **Dig**
**Map Info**: https://maps.google.com/?q=35.1224486753,-106.6278869713`;
var m;
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
console.log(m[1]);
}
I need to remove substrings containing the word "page=" followed by number.
Ex.
s= "aaaapage=500";
Should be
s = "page=500"
I tried
s = s.replace(/&page=\d/g,"");
and
s = s.replace(/&page=[\d]+/g,"");
to no avail
You can match text before and after while capturing the page=[digits]:
s= "aaaapage=500";
document.write(s.replace(/.*(page=\d+).*/, '$1') + "<br/>");
// or with multiline input
s= "a\na\naapage=500text\nnewline";
document.write(s.replace(/[\s\S]*(page=\d+)[\s\S]*/, '$1'));
This is good when we only have 1 page=[digits].
When we have more, use exec:
var re = /page=\d+/g;
var str = 'apageaaaapage=500apageaaaapage=210';
var m;
while ((m = re.exec(str)) !== null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
document.write(m[0] + "<br/>");
}
I just realized
s = s.replace(/&page=[\d]+/g,"");
actually works
Just remove all the word chars which exists before page=
s.replace(/\w*(page=\d+)/g,"$1");