Related
I want the replaceFunction to run only one time. For now works correctly only on first time, E-1 return Ε-1 (APPLE) but when user try to edit text field again system detect
Ε-1 and return Ε-1 (APPLE) (APPLE)..
td.onchange = function(e) {
this.value = this.value.replace(/(\E-(\d+))/g, replaceFunction);
function replaceFunction(match) {
// add additional rules here for more cases
if (match === "E-1") return "Ε-1 (APPLE)";
if (match === "E-2") return "Ε-2 (SUMSUNG)";
.
.
.
if(match === "E-99") return "Ε-99 (LG)";
return match;
}
}
How I stop this?
You can use something like this one more condition:
if (match === "E-1" && match !== "Ε-1 (APPLE)") return "Ε-1 (APPLE)";
this can be optimized, if you put the mapping into object:
var map = {
"E-1": "Ε-1 (APPLE)",
...
}
if (map[match] && !map[match] !== match) { return map[match]; }
and for this to work you will need regex that also match the word after in bracket:
var names = ['APPLE', 'SAMSUNG'];
var re = new RegExp('(E-(\\d+))(?! \\((?:' + names.join('|') + ')\\))', 'g');
Yet another solution is to use only array (this will only work if you E-NUM match index in array)
var names = ['APPLE', 'SAMSUNG'];
var re = new RegExp('(E-(\\d+))(?! \\((?:' + names.join('|') + ')\\))', 'g');
// regex explanation, same as yours but \\d is because it's a string
// we create negative look ahead so we check if next text
// after E-1 is not " (" and any of the names.
// we use (?: to group what's inside it's the same as with ()
// but the value will not be captured so there will be
// no param in function for this group
// so this regex will be the same as yours but will not match "E-1 (APPLE)"
// only "E-1"
this.value = this.value.replace(re, replaceFunction);
function replaceFunction(match, eg, num) {
// convert string to number E starts
var i = parseInt(num, 10) - 1;
if (i <= names.length) {
return match + ' (' + names[i] + ')';
}
}
the regex and function can be created outside of the change function, so it don't create new function on each change.
When replacing, also optionally lookahead for a space and parentheses that come after. This way, in the replacer function, you can check to see if what follows is already the value you want (eg, (APPLE)). If it is, then do nothing - otherwise, replace with the new string:
const replacementsE = [
, // nothing for E-0
'APPLE',
'SUMSUNG',
];
td.onchange = function(e) {
td.value = td.value.replace(/E-(\d+)(?= \(([^)]+)\)|)/g, replaceFunction);
function replaceFunction(match, digits, followingString) {
const replacement = replacementsE[digits];
if (!replacement || replacement === followingString) {
return match;
}
return `E-${digits} (${replacement})`;
}
}
<input id="td">
What /E-(\d+)(?= \(([^)]+)\)|)/ does is:
E- - Match E-
(\d+) - Capture digits in a group
(?= \(([^)]+)\)|) Lookahead for either:
\(([^)]+)\) A literal (, followed by non-) characters, followed by ). If this is matched, the non-) characters will be the second capture group
| - OR match the empty string (so that the lookahead works)
The digits will be the first capture group; the digits variable in the callback. The non-) characters will be the second capture group; the followingString variable in the callback.
If you also want to permit the final ) to be deleted, then make the final ) optional, and also make sure the character set does not match spaces (so that the space following APPLE, with no end ), doesn't get matched):
const replacementsE = [
, // nothing for E-0
'APPLE',
'SUMSUNG',
];
td.onchange = function(e) {
td.value = td.value.replace(/E-(\d+)(?= \(([^) ]+)\)?|)/g, replaceFunction);
function replaceFunction(match, digits, followingString) {
const replacement = replacementsE[digits];
if (!replacement || replacement === followingString) {
return match;
}
console.log(followingString)
return `E-${digits} (${replacement})`;
}
}
<input id="td">
If you want to permit any number of characters before the final ) to be deleted, then check if the replacement startsWith the following string:
const replacementsE = [
, // nothing for E-0
'APPLE',
'SUMSUNG',
];
td.onchange = function(e) {
td.value = td.value.replace(/E-(\d+)(?= \(([^) ]+)\)?|)/g, replaceFunction);
function replaceFunction(match, digits, followingString, possibleTrailingParentheses) {
const replacement = replacementsE[digits];
if (!replacement || replacement === followingString || replacement.startsWith(followingString)) {
return match;
}
return `E-${digits} (${replacement})`;
}
}
<input id="td">
I have a string that is composed of different elements and I need to separate them at another tag in the string.
I used .split() and .pop() which worked great if there is only one element.
function getText(fullText, type) {
var partial = fullText.split('*' + type + '*').pop().split('*/' + type + '*')[0] || 'unknown';
return partial;
}
var str = "*a*Substring A*/a**b*Substring B*/b**c*Substring C*/c*"
getText(str, a) // returns 'Substring A'
However, I have now encountered multiple elements and in this case it only returns the last element.
var str = "*a*Substring A*/a**b*Substring B1*/b**b*Substring B2*/b*"
getText(str, b) // returns 'Substring B2'
How do I get an array with all the substrings between those tags?
function getText(fullText, type) {
let re = `\\*${type}\\*(.*?)\\*/${type}\\*`;
return fullText
.match(new RegExp(re, 'g'))
.map(str => str.match(new RegExp(re))[1]);
}
var str = "*a*Substring A*/a* woah *a*Substring A 2*/a*"
x = getText(str, 'a')
console.log(x)
You can use shift() to remove first element and then use map()
function getText(fullText, type) {
var partial = fullText.split('*' + type + '*')
partial.shift();
for(let i = 0;i<partial.length;i++){
partial[i] = partial[i].split('*/' + type + '*')[0]
}
return partial
}
var str = "*a*Substring A*/a**b*Substring B1*/b**b*Substring B2*/b*"
console.log(getText(str, 'b'))
The reason why you're getting the last element is because you're using pop() which just returns the last element in array:
The pop() method removes the last element from an array and returns that element. This method changes the length of the array.
You can read more about it on here website
Using pop() returns the last element in an array(the array that is created when you do your first split)
try:
function getText(fullText, type) {
var partial = fullText.split('*' + type + '*');
var result = [];
$.each(partial, function(i, item) {
var split = item.split('*/' + type + '*');
if (split.length > 1) {
result.push(split[0]);
}
});
return result;
}
var str = "*a*Substring A*/a**b*Substring B1*/b**b*Substring B2*/b*";
console.log(getText(str, 'b'));
Here's one way to do it.
function getText(fullText, type) {
var typeStr = "\*" + type + "\*";
return fullText
.split('/')
.filter(item=>item.match(typeStr.replace(/\*/g, '\\*')))
.map(item=>item
.substr(item.indexOf(typeStr) + typeStr.length)
.replace(/\*$/, ''));
}
var str = "*a*Substring A*/a**b*Substring B*/b**c*Substring C*/c*"
console.log(getText(str, 'a')); // returns 'Substring A'
var str = "*a*Substring A*/a**b*Substring B1*/b**b*Substring B2*/b*"
console.log(getText(str, 'b')); // returns 'Substring B2'
Explaining:
// this creates a str with the type to be searched
var typeStr = "\*" + type + "\*";
return fullText
// this splits all elements in your string
.split('/')
// this leaves only elements that match type...
.filter(item=>item.match(
// using a regex created with the typeStr created above
// replace escape the * char to make regex work
typeStr.replace(/\*/g, '\\*')))
// now with the remaining parts, alter them as follows...
.map(item=>item
// remove everything before type and type itself
.substr(item.indexOf(typeStr) + typeStr.length)
// remove last * char
.replace(/\*$/, ''));
EDIT
Reading #junvar's answer I noticed a pattern that I haven't noticed when I answered. That if you substitute * by < or > you have a XML pattern. So it seems to me that the OP has replaced < and > by * in order to mask this is XML parsing. If it is a XML parse, please read the most famous answer in SO:
https://stackoverflow.com/a/1732454/2752520
JUST DON'T PARSE XML WITH REGEX.
You can backreference like this in JavaScript:
var str = "123 $test 123";
str = str.replace(/(\$)([a-z]+)/gi, "$2");
This would (quite silly) replace "$test" with "test". But imagine I'd like to pass the resulting string of $2 into a function, which returns another value. I tried doing this, but instead of getting the string "test", I get "$2". Is there a way to achieve this?
// Instead of getting "$2" passed into somefunc, I want "test"
// (i.e. the result of the regex)
str = str.replace(/(\$)([a-z]+)/gi, somefunc("$2"));
Like this:
str.replace(regex, function(match, $1, $2, offset, original) { return someFunc($2); })
Pass a function as the second argument to replace:
str = str.replace(/(\$)([a-z]+)/gi, myReplace);
function myReplace(str, group1, group2) {
return "+" + group2 + "+";
}
This capability has been around since Javascript 1.3, according to mozilla.org.
Using ESNext, quite a dummy links replacer but just to show-case how it works :
let text = 'Visit http://lovecats.com/new-posts/ and https://lovedogs.com/best-dogs NOW !';
text = text.replace(/(https?:\/\/[^ ]+)/g, (match, link) => {
// remove ending slash if there is one
link = link.replace(/\/?$/, '');
return `${link.substr(link.lastIndexOf('/') +1)}`;
});
document.body.innerHTML = text;
Note: Previous answer was missing some code. It's now fixed + example.
I needed something a bit more flexible for a regex replace to decode the unicode in my incoming JSON data:
var text = "some string with an encoded 's' in it";
text.replace(/&#(\d+);/g, function() {
return String.fromCharCode(arguments[1]);
});
// "some string with an encoded 's' in it"
If you would have a variable amount of backreferences then the argument count (and places) are also variable. The MDN Web Docs describe the follwing syntax for sepcifing a function as replacement argument:
function replacer(match[, p1[, p2[, p...]]], offset, string)
For instance, take these regular expressions:
var searches = [
'test([1-3]){1,3}', // 1 backreference
'([Ss]ome) ([A-z]+) chars', // 2 backreferences
'([Mm][a#]ny) ([Mm][0o]r[3e]) ([Ww][0o]rd[5s])' // 3 backreferences
];
for (var i in searches) {
"Some string chars and many m0re w0rds in this test123".replace(
new RegExp(
searches[i]
function(...args) {
var match = args[0];
var backrefs = args.slice(1, args.length - 2);
// will be: ['Some', 'string'], ['many', 'm0re', 'w0rds'], ['123']
var offset = args[args.length - 2];
var string = args[args.length - 1];
}
)
);
}
You can't use 'arguments' variable here because it's of type Arguments and no of type Array so it doesn't have a slice() method.
When I run the snippet below in JSbin it says that capitalize won't work because the property is undefined. I looked at the documentation and it mentioned the use of the word 'this' but I am not quite sure if that applies here since the string value passed in is correct (console logged it to confirm). Why is the capitalize method not apple to work off the map value?
class bookTitle {
constructor(title) {
this.title = title; // this creates the object ONCE, see top of jasmine file for explanation
}
get title() {
return this._title; // retrieves the title string
}
set title(title) {
this._title = this.titleCreator(title); // calls the method and sets title, see top of jasmine file for explanation
}
titleCreator(string) {
if (string == null){
return string; // catches first error
}
// Note that this isn't meant to be a fully fledged title creator, just designed to pass these specific tests
var littleWords = ['and', 'do', 'the', 'a', 'an', 'in', 'of']; // These are the words that we don't want to capitalize
var modifiedString = string
.split(' ') // Splits string into array of words, basically breaks up the sentence
.map(function(word,index) {
if (index == 0) {
return this.capitalize(word); // capitalize the first word of the string
} else if (littleWords.indexOf(word) == -1) {
return this.capitalize(word); // capitalize any words that are not little, the -1 is returned by indexOf if it can't find the word in the array
} else if (littleWords.indexOf(word) >= 0) {
return word; // do not capitalize as this word is in the list of littleWords
}
})
.join(' '); // Joins every element of an array into a string with a space inbetween each value. Basically you created a sentence from an array of words
return modifiedString;
}
capitalize(word) {
return word.charAt(0).toUpperCase() + word.slice(1);
// This function just capitalizes the word given to it
}
}
let bookTitles = new bookTitle();
bookTitles.title = 'inferno';
console.log(bookTitles.title); // The goal is to output Inferno
The problem is that this within your map refers to the function you're passing to map. Use an arrow function (word, index) => { ... } instead and this should fall through to the parent class.
var modifiedString = string
.split(' ') // Splits string into array of words, basically breaks up the sentence
.map((word,index) => { // <-- ARROW FUNCTION
if (index == 0) {
return this.capitalize(word); // capitalize the first word of the string
} else if (littleWords.indexOf(word) == -1) {
return this.capitalize(word); // capitalize any words that are not little, the -1 is returned by indexOf if it can't find the word in the array
} else if (littleWords.indexOf(word) >= 0) {
return word; // do not capitalize as this word is in the list of littleWords
}
})
.join(' ');
With regex (i assume) or some other method, how can i convert things like:
marker-image or my-example-setting to markerImage or myExampleSetting.
I was thinking about just splitting by - then convert the index of that hypen +1 to uppercase. But it seems pretty dirty and was hoping for some help with regex that could make the code cleaner.
No jQuery...
Try this:
var camelCased = myString.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
The regular expression will match the -i in marker-image and capture only the i. This is then uppercased in the callback function and replaced.
This is one of the great utilities that Lodash offers if you are enlightened and have it included in your project.
var str = 'my-hyphen-string';
str = _.camelCase(str);
// results in 'myHyphenString'
You can get the hypen and the next character and replace it with the uppercased version of the character:
var str="marker-image-test";
str.replace(/-([a-z])/g, function (m, w) {
return w.toUpperCase();
});
Here's my version of camelCase function:
var camelCase = (function () {
var DEFAULT_REGEX = /[-_]+(.)?/g;
function toUpper(match, group1) {
return group1 ? group1.toUpperCase() : '';
}
return function (str, delimiters) {
return str.replace(delimiters ? new RegExp('[' + delimiters + ']+(.)?', 'g') : DEFAULT_REGEX, toUpper);
};
})();
It handles all of the following edge cases:
takes care of both underscores and hyphens by default (configurable with second parameter)
string with unicode characters
string that ends with hyphens or underscore
string that has consecutive hyphens or underscores
Here's a link to live tests: http://jsfiddle.net/avKzf/2/
Here are results from tests:
input: "ab-cd-ef", result: "abCdEf"
input: "ab-cd-ef-", result: "abCdEf"
input: "ab-cd-ef--", result: "abCdEf"
input: "ab-cd--ef--", result: "abCdEf"
input: "--ab-cd--ef--", result: "AbCdEf"
input: "--ab-cd-__-ef--", result: "AbCdEf"
Notice that strings that start with delimiters will result in a uppercase letter at the beginning.
If that is not what you would expect, you can always use lcfirst.
Here's my lcfirst if you need it:
function lcfirst(str) {
return str && str.charAt(0).toLowerCase() + str.substring(1);
}
Use String's replace() method with a regular expression literal and a replacement function.
For example:
'uno-due-tre'.replace(/-./g, (m) => m[1].toUpperCase()) // --> 'unoDueTre'
Explanation:
'uno-due-tre' is the (input) string that you want to convert to camel case.
/-./g (the first argument passed to replace()) is a regular expression literal.
The '-.' (between the slashes) is a pattern. It matches a single '-' character followed by any single character. So for the string 'uno-due-tre', the pattern '-.' matches '-d' and '-t' .
The 'g' (after the closing slash) is a flag. It stands for "global" and tells replace() to perform a global search and replace, ie, to replace all matches, not just the first one.
(m) => m[1].toUpperCase() (the second argument passed to replace()) is the replacement function. It's called once for each match. Each matched substring is replaced by the string this function returns. m (the first argument of this function) represents the matched substring. This function returns the second character of m uppercased. So when m is '-d', this function returns 'D'.
'unoDueTre' is the new (output) string returned by replace(). The input string is left unchanged.
This doesn't scream out for a RegExp to me. Personally I try to avoid regular expressions when simple string and array methods will suffice:
let upFirst = word =>
word[0].toUpperCase() + word.toLowerCase().slice(1)
let camelize = text => {
let words = text.split(/[-_]/g) // ok one simple regexp.
return words[0].toLowerCase() + words.slice(1).map(upFirst)
}
camelize('marker-image') // markerImage
Here is my implementation (just to make hands dirty)
/**
* kebab-case to UpperCamelCase
* #param {String} string
* #return {String}
*/
function toUpperCamelCase(string) {
return string
.toLowerCase()
.split('-')
.map(it => it.charAt(0).toUpperCase() + it.substring(1))
.join('');
}
// Turn the dash separated variable name into camelCase.
str = str.replace(/\b-([a-z])/g, (_, char) => char.toUpperCase());
Here is another option that combines a couple answers here and makes it method on a string:
if (typeof String.prototype.toCamel !== 'function') {
String.prototype.toCamel = function(){
return this.replace(/[-_]([a-z])/g, function (g) { return g[1].toUpperCase(); })
};
}
Used like this:
'quick_brown'.toCamel(); // quickBrown
'quick-brown'.toCamel(); // quickBrown
You can use camelcase from NPM.
npm install --save camelcase
const camelCase = require('camelcase');
camelCase('marker-image'); // => 'markerImage';
camelCase('my-example-setting'); // => 'myExampleSetting';
Another take.
Used when...
var string = "hyphen-delimited-to-camel-case"
or
var string = "snake_case_to_camel_case"
function toCamelCase( string ){
return string.toLowerCase().replace(/(_|-)([a-z])/g, toUpperCase );
}
function toUpperCase( string ){
return string[1].toUpperCase();
}
Output: hyphenDelimitedToCamelCase
is also possible use indexOf with recursion for that task.
input some-foo_sd_dsd-weqe
output someFooSdDsdWeqe
comparison ::: measure execution time for two different scripts:
$ node camelCased.js
someFooSdDsdWeqe
test1: 2.986ms
someFooSdDsdWeqe
test2: 0.231ms
code:
console.time('test1');
function camelCased (str) {
function check(symb){
let idxOf = str.indexOf(symb);
if (idxOf === -1) {
return str;
}
let letter = str[idxOf+1].toUpperCase();
str = str.replace(str.substring(idxOf+1,idxOf+2), '');
str = str.split(symb).join(idxOf !== -1 ? letter : '');
return camelCased(str);
}
return check('_') && check('-');
}
console.log(camelCased ('some-foo_sd_dsd-weqe'));
console.timeEnd('test1');
console.time('test2');
function camelCased (myString){
return myString.replace(/(-|\_)([a-z])/g, function (g) { return g[1].toUpperCase(); });
}
console.log(camelCased ('some-foo_sd_dsd-weqe'));
console.timeEnd('test2');
Just a version with flag, for loop and without Regex:
function camelCase(dash) {
var camel = false;
var str = dash;
var camelString = '';
for(var i = 0; i < str.length; i++){
if(str.charAt(i) === '-'){
camel = true;
} else if(camel) {
camelString += str.charAt(i).toUpperCase();
camel = false;
} else {
camelString += str.charAt(i);
}
}
return camelString;
}
Use this if you allow numbers in your string.
Obviously the parts that begin with a number will not be capitalized, but this might be useful in some situations.
function fromHyphenToCamelCase(str) {
return str.replace(/-([a-z0-9])/g, (g) => g[1].toUpperCase())
}
function fromHyphenToCamelCase(str) {
return str.replace(/-([a-z0-9])/g, (g) => g[1].toUpperCase())
}
const str1 = "category-123";
const str2 = "111-222";
const str3 = "a1a-b2b";
const str4 = "aaa-2bb";
console.log(`${str1} => ${fromHyphenToCamelCase(str1)}`);
console.log(`${str2} => ${fromHyphenToCamelCase(str2)}`);
console.log(`${str3} => ${fromHyphenToCamelCase(str3)}`);
console.log(`${str4} => ${fromHyphenToCamelCase(str4)}`);
You can also use string and array methods; I used trim to avoid any spaces.
const properCamel = (str) =>{
const lowerTrim = str.trim().toLowerCase();
const array = lowerTrim.split('-');
const firstWord = array.shift();
const caps = array.map(word=>{
return word[0].toUpperCase() + word.slice(1);
})
caps.unshift(firstWord)
return caps.join('');
}
This simple solution takes into account these edge cases.
Single word
Single letter
No hyphen
More than 1 hyphen
const toCamelCase = (text) => text.replace(/(.)([^-|$]*)[-]*/g, (_,letter,word) => `${letter.toUpperCase()}${word.toLowerCase()}`)