How to find a character only if it is always followed by the same letter (random and unknown)?
For example:
kelmqaetrstaeiii
E.g with the letter a, it is twice followed by the same letter e, so i would like it to be found, but with the following:
kelmqaetrstafiii
We have ae and af, 2 differents letters after a, in this case nothing should be taken.
Is it possible?
You could iterate over the string and check if one of the pairs already appeared in the string:
function hasPair(str) {
const dupe = {};
for (let index = 0; index < str.length - 1; index++) {
const pair = str.substr(index, 2);
if (dupe[pair])
return pair;
dupe[pair] = true;
}
return false;
}
console.log(
hasPair("kelmqaetrstaeii"),
hasPair("kelmqaetrstaii")
);
Match the first letter, then use lookahead, match the second letter, and use backreferences:
const re = /(\w)(?=(\w).+\1\2)/;
console.log('kelmqaetrstaeiii'.match(re)[0]);
console.log('kelmqaetrstafiii'.match(re));
Related
I got a string
For example:
This is for trails and I want to learn Js and Coding and Development
The above mentioned line as a string
function trail(sen){
var cat = "and"
var fin = sen.indexOf(cat);
if(fin > 0){
var last = sen.substring(0, fin)
}
else{
var last = sen;
}
return last;
}
console.log(
trail("This is for trails and I want to learn Js and Coding and Development ")
);
I am trying to find the index of the second "and" in a string rather than the first one.
and get the string part from index 0 to that second "and"
Could you please provide the better approach ?
You can use split together with join to achieve this, like so:
const myStr = 'This is for trails and I want to learn Js and Coding and Development'
const subStr = 'and'
const splitted = getSplitted(myStr, subStr, 2) // Splits before the "N th" ocurrence of subStr
console.log(splitted)
function getSplitted(str, subStr, idx) {
return str.split(subStr, idx).join(subStr);
}
You can first find the second occurrence and then remove it via simple slice.
This method also supports regular expressions as pattern.
/**
* Find the n-th occurrence of given pattern in a string.
* #param { string } str The string to be examined.
* #param { string | RegExp } pattern The pattern to be matched.
* #param { number } n Starting index.
* #return { [number, string | RegExpExecArray] } The index & the match result. `[-1, null]` if pattern occurs less than n times.
*/
function findNth(str, pattern, n = 1) {
// The total processed index & and the last match
let index = 0, result;
for(; n--; ) {
// Index of the next match relative to the end of the last one
let offset = -1;
if(pattern instanceof RegExp) {
const match = pattern.exec(str);
if(match !== null) {
offset = match.index;
result = match[0];
}
}
else { // string case
offset = str.indexOf(pattern);
result = pattern;
}
// If none is matched
if(offset === -1)
return [-1, null];
// Seek over the match result
offset += result.length;
str = str.slice(offset);
index += offset;
}
// Gotta go back to the start of the last match
index -= result.length;
return [index, result];
}
/** Remove the n-th occurrence of given pattern out of a string. */
function removeNth(str, pattern, n = 1) {
const result = findNth(str, pattern, n);
if(result[0] === -1)
return str;
return str.slice(0, result[0]) + str.slice(result[0] + result[1].length);
}
{
const str = 'This is for trails and I want to learn Js and Coding and Development';
console.log(removeNth(str, 'and', 2));
console.log(removeNth(str, /\s*and/, 2));
}
Use split
sen.split(cat, 2) // This line will divide the syntax into an array of two elements till second "and" occurrence
// ['This is for trails ', ' I want to learn Js ']
Then you need to join them to add the first and
sen.split(cat, 2).join(cat)
And to get the length
sen.split(cat, 2).join(cat).length
let str = "This is for trails and I want to learn Js and Coding and Development".split("and", 2).join("");
console.log(str);
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(''))
Everything I can find by searching is people wanting to convert to sentence/title case from lower/upper/random case. That's the opposite of my problem.
What I have is already correct, I want to convert it to uppercase except for the "c" or "ac" etc. So McDonald becomes McDONALD, MacDonald becomes MacDONALD, etc.
Probably the best way is separating out the lower-case letters that occur between two upper-case letters, either before or after running toUpperCase(), but my brain is fried at the moment so I'm not sure how to go about it.
It's for an After Effects expression, controlling the display so I can have sentence case in one composition and upper case in another, from the same source layer. So I know input will be perfect.
You can try something like this:
const input = "MacDonald";
const matches = input.match(/([A-Z][a-z]*)/g);
const output = matches.length > 1 ?
matches.reduce((acc, match, index) => {
if (index === 0) {
return match;
}
return acc + match.toUpperCase();
}) :
input.toUpperCase();
First we take the input apart by matching it against a simple regular expression. The match method in the example will return ["Mac","Donald"].
Then, if there is only one match, we return it in uppercase.
In case of multiple matches, we construct the result by concatenating uppercase parts except for the first part.
Here's a version for a whole sentence:
const input = "Old MacDonald is a fine man.";
const output = input
.split(/\s/)
.map(word => {
const matches = word.match(/([A-Z][a-z]*)/g);
if (!matches || matches.length === 1) {
return word.toUpperCase();
}
return matches.reduce((acc, match, index) => {
return index === 0 ? match : acc + match.toUpperCase();
});
})
.join(' ');
// output == "OLD MacDONALD IS A FINE MAN."
Sami Hult's answer covers most of the bases, but unfortunately refuses to work in After Effects due to syntax issues and map() and reduce() not being supported, and I wanted to make one small tweak, all-capsing only the last portion rather than all but the first (to account for a possible double prefix).
So based on that code, I came up with this:
function str_uppercase(str) {
str = str.split(/\s/);
var output = [];
for (i = 0; i < str.length; i++) {
var word = str[i];
var matches = word.match(/([A-Z][a-z]*)/g);
if (!matches || matches.length === 1) {
word = word.toUpperCase();
} else {
var x = matches.length - 1;
matches[x] = matches[x].toUpperCase();
word = matches.join('');
}
output.push(word);
}
return output.join(' ');
}
console.log(str_uppercase('Old MacMcDonald Had a farm'));
// => OLD MacMcDONALD HAD A FARM
The code below assumes a string prefix to be one capital letter character followed by one or more small letter characters followed by one capital letter character and always at the beginning of the whole word.
The prefix will be retained as it is and the rest will be capitalized.
const input = [
"McDonald",
"MacDonald",
"Mcdonald",
"mcDonald",
"mcdonald"
];
// Function for converting to special uppercase
const specialUpperCase = function(item) {
// Find prefix (one or more lower case characters between upper case character - at the beginning)
const match = item.match(/^[A-Z][a-z]+[A-Z]/);
if (match) {
// If prefix, capitalize only the remaining
return match[0] + item.substr(match[0].length).toLocaleUpperCase();
}
// If no prefix, capitalize the whole string
return item.toLocaleUpperCase();
};
const output = input.map(specialUpperCase);
console.log(output);
The easiest solution would probably be to keep a list of prefixes and test if the word starts with one of these:
//Prefixes to look for
var prefixToKeep = [
"Mac",
"Mc"
];
//Selective uppercase function
function selectiveUpperCase(text) {
//Find words by wordBoundaries
return text.replace(/\b\w+\b/gim, function (word) {
//Test for prefixes
for (var prefixToKeepIndex = 0; prefixToKeepIndex < prefixToKeep.length; prefixToKeepIndex++) {
var prefix = prefixToKeep[prefixToKeepIndex];
if (word.indexOf(prefix) === 0) {
//prefix matches. Return prefix as is + rest of the word in uppercase
return word.slice(0, prefix.length) + word.slice(prefix.length).toUpperCase();
}
}
//No prefix found, return word as uppercase
return word.toUpperCase();
});
}
//TEST
var text = "Old MacDonald had a farm\nE-I-E-I-O\nAnd on this farm he had a cow\nE-I-E-I-O\nWith a moo-moo here\nAnd a moo-moo there\nHere a moo, there a moo\nEverywhere a moo-moo\nOld MacDonald had a farm\nE-I-E-I-O ";
console.log(selectiveUpperCase(text));
EDIT 1 - Upper-Lower-Upper Test
In response to the comments, this newer version tests for Upper-Lower-Upper cases and uses its findings to determine which parts to uppercase.
//Selective uppercase function
function selectiveUpperCase(text) {
//Find words by wordBoundaries
return text.replace(/\b\w+\b/gim, function (word) {
var reg = /[A-Z]+[a-z]+[A-Z]\w+/gm;
//Test for Upper-Lower-Upper combo
if (reg.test(word) || reg.test(word)) {
//start at index 1
var l = 0;
while (l++ < word.length) {
//move up the word and test for an uppercase letter
if (word[l] === word[l].toUpperCase()) {
break;
}
l++;
//return the first slice (the prefix) as is and uppercase the rest
return word.slice(0, l) + word.slice(l).toUpperCase();
}
}
//No prefix found, return word as uppercase
return word.toUpperCase();
});
}
//TEST
var text = "Old MacDonald had a farm\nE-I-E-I-O\nAnd on this farm he had a cow\nE-I-E-I-O\nWith a moo-moo here\nAnd a moo-moo there\nHere a moo, there a moo\nEverywhere a moo-moo\nOld McDonald had a farm\nE-I-E-I-O ";
console.log(selectiveUpperCase(text));
ES6 version with RegEx, you can try below function replaceStr()
const replaceStr = str => str.replace(/(^[A-Z])([a-z]{1,2})(.+)/,
(_, p1, p2, p3) => p1.toUpperCase() + p2 + p3.toUpperCase());
I wrote the following code to check for a palindrome in JS. I can't get the following strings to work:
palindrome("My age is 0, 0 si ega ym.")
palindrome("A man, a plan, a canal. Panama")
I don't understand why. I understand there are more simple methods but I want to understand why this in particular isn't working. Here is the code:
function palindrome(str) {
//gets rid of all special characters, turns string to lowercase
str = str.replace(/[^a-z0-9]/g, '');
str = str.toLowerCase();
//turns string into an array of characters, including whitespaces
array = str.split("");
//removes white space in the array
array = array.filter(function(val) {
return /\S/.test(val);
});
//defines new variable for the length of the array
var length = array.length;
//while loop to cycle through the array. Originally used zero, but I realized that wouldnt work for an odd-
// numbered array. takes off the first and last value and compares them. If they are not equal, returns
// false.
while (length > 1) {
var a = array.shift();
var b = array.pop();
length = array.length; //sets new length
if (a != b) {
return false;
}
}
return true;
}
console.log(palindrome("My age is 0, 0 si ega ym."))
console.log(palindrome("A man, a plan, a canal. Panama"))
You're eliminating UPPER CASE characters before you lower case the string.
For example: "A man, a plan, a canal. Panama"
str = str.replace(/[^a-z0-9]/g, '');
We now have: "manaplanacanalanama" - because you've said "kill of anything that isn't lower case or a number. Oops.
Change the order to this and it will work!
str = str.toLowerCase();
str = str.replace(/[^a-z0-9]/g, '');
Your first regex is case sensitive and only matches lowercase letters, so you are stripping out all the capital letters.
Fixed by adding one character:
(alternatively, you could move the .toLowerCase() line up before using the regex)
function palindrome(str) {
//gets rid of all special characters, turns string to lowercase
str = str.replace(/[^a-z0-9]/ig, '');
str = str.toLowerCase();
//turns string into an array of characters, including whitespaces
array = str.split("");
//removes white space in the array
array = array.filter(function(val) {
return /\S/.test(val);
});
//defines new variable for the length of the array
var length = array.length;
//while loop to cycle through the array. Originally used zero, but I realized that wouldnt work for an odd-
// numbered array. takes off the first and last value and compares them. If they are not equal, returns
// false.
while (length > 1) {
var a = array.shift();
var b = array.pop();
length = array.length; //sets new length
if (a != b) {
return false;
}
}
return true;
}
console.log(palindrome("My age is 0, 0 si ega ym."));
console.log(palindrome("A man, a plan, a canal. Panama"));
console.log(palindrome('Ada'));
console.log(palindrome('Dada'));
You first remove non-lower-alphanumeric characters before you lower-case the string. Swap those two lines so you first convert the string to all lower-case characters, then remove non-alphanumerics.
Since you already removed characters that aren't a-z or 0-9, you don't have to account for whitespace at all! This means you can remove your filter(), which does nothing in this case.
function palindrome(str) {
//gets rid of all special characters, turns string to lowercase
str = str.toLowerCase();
str = str.replace(/[^a-z0-9]/g, '');
array = str.split("");
//defines new variable for the length of the array
var length = array.length;
//while loop to cycle through the array. Originally used zero, but I realized that wouldnt work for an odd-
// numbered array. takes off the first and last value and compares them. If they are not equal, returns
// false.
while (length > 1) {
var a = array.shift();
var b = array.pop();
length = array.length; //sets new length
if (a != b) {
return false;
}
}
return true;
}
console.log(palindrome("My age is 0, 0 si ega ym."))
console.log(palindrome("A man, a plan, a canal. Panama"))
Move the string to lowercase above the replace and try again.
function palindrome(str) {
//gets rid of all special characters, turns string to lowercase
str = str.toLowerCase();
str = str.replace(/[^a-z0-9]/g, '');
//turns string into an array of characters, including whitespaces
array = str.split("");
//removes white space in the array
array = array.filter(function(val) {
return /\S/.test(val);
});
//defines new variable for the length of the array
var length = array.length;
//while loop to cycle through the array. Originally used zero, but I realized that wouldnt work for an odd-
// numbered array. takes off the first and last value and compares them. If they are not equal, returns
// false.
while (length > 1) {
var a = array.shift();
var b = array.pop();
length = array.length; //sets new length
if (a != b) {
return false;
}
}
return true;
}
console.log(palindrome("My age is 0, 0 si ega ym."))
console.log(palindrome("A man, a plan, a canal. Panama"))
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.