My Goal
What I want to do is something similar to this:
let json_obj = {
hello: {
to: 'world'
},
last_name: {
john: 'smith'
},
example: 'a ${type}', // ${type} -> json_obj.type
type: 'test'
}
// ${hello.to} -> json_obj.hello.to -> "word"
let sample_text = 'Hello ${hello.to}!\n' +
// ${last_name.john} -> json_obj.last_name.john -> "smith"
'My name is John ${last_name.john}.\n' +
// ${example} -> json_obj.example -> "a test"
'This is just ${example}!';
function replacer(text) {
return text.replace(/\${([^}]+)}/g, (m, gr) => {
gr = gr.split('.');
let obj = json_obj;
while(gr.length > 0)
obj = obj[gr.shift()];
/* I know there is no validation but it
is just to show what I'm trying to do. */
return replacer(obj);
});
}
console.log(replacer(sample_text));
Until now this is pretty easy to do.
But if $ is preceded by a backslash(\) I don't want to replace the thing between brackets. For example: \${hello.to}would not be replaced.
The problem grows up when I want to be able to escape the backslashes. What I mean by escaping the backslashes is for example:
\${hello.to} would become: ${hello.to}
\\${hello.to} would become: \world
\\\${hello.to} would become: \${hello.to}
\\\\${hello.to} would become: \\${hello.to}
etc.
What I've tried?
I didn't try many thing so far cause I've absolutely no idea how to achieve that since from what I know there is no lookbehind pattern in javascript regular expressions.
I hope the way I explained it is clear enoughto be understood andI hope someone has a solution.
I recommend you to solve this problem in separate steps :)
1) First step:
Simplify backslashes of your text replacing all occurrences of "\\" for "". This will eliminate all redundancies and make the token replacement part easier.
text = text.replace(/\\\\/g, '');
2) Second step:
To replace the tokens of the text, use this regex: /[^\\](\${([^}]+)})/. This one will not permit tokens that have with \ before them. Ex: \${hello.to}.
Here is you code with the new expression:
function replacer(text) {
return text.replace(/[^\\](\${([^}]+)})/, (m, gr) => {
gr = gr.split('.');
let obj = json_obj;
while(gr.length > 0)
obj = obj[gr.shift()];
/* I know there is no validation but it
is just to show what I'm trying to do. */
return replacer(obj);
});
}
If you still have any problems, let me know :)
Related
I have a JavaScript String which looks like this:
From Windows to Linux
With JavaScript, how can I highlight the words From and Linux, via a substring which looks like this:
From Linux
so the string looks like this in the end:
<mark>From</mark> Windows to <mark>Linux</mark>
This is my current implementation of the function to do that job:
function highlightSearchTerm(string, substring) {
const regex = new RegExp(`(${substring})`, 'ig');
return string.replace(regex, '<mark>$1</mark>');
}
I call it like this:
highlightSearchTerm("From Windows to Linux", "from linux")
It works well, the only thing that is missing is to make it work when the substring has words which are not directly next to each other.
These substrings for instance work:
from windows
From
to Linux
While these don't (Words are not directly next to each other in the main string):
Windows Linux
from To
Linux from
Short Answer
Call highlightSearchTerm() with a pipe(|) between the terms to achieve the desired output.
Longer Answer
The answer has to deal with how you are building your Regex.
The function
function highlightSearchTerm(string, substring) {
const regex = new RegExp(`(${substring})`, 'ig');
return string.replace(regex, '<mark>$1</mark>');
}
It's important to understand what the corresponding RegExp object that is created reads like, and how it equates to a form that we would maybe write out directly.
First, if we call
// assume substring = 'hello';
new RegExp(`(${substring})`, 'ig');
// Equivalent: /(hello)/ig;
Notice that the grouped item is looking for the word hello.
Now, if we supply something that has multiple things we want in it, such as hi and you then if we supply them as a single string separated by space, e.g.
const substring = 'hey you';
new RegExp(`(${substring})`,'ig');
// Equivalent: /(hey you)/ig
This will not give us what we want because instead of looking for hey or you, the parser is now looking hey you as a phrase.
However, if we separate those things by a pipe (|) we get
// assume substring = 'hey|you';
new RegExp(`(${substring})`,'ig');
// Equivalent: /(hey|you)/ig
This now looks for either hey or you in the string. This is because the pipe character in RegEx is the OR.
If you'd like to expand the search for multiple phrases, you separate each specific one by a pipe, e.g.
new RegExp('(hey|you|that guy)', 'ig');
Will search for the words hey and you and the phrase (space included) that guy.
You can use the Pipe | just like #Jhecht explained above, alternatively you can split your substring and doing it this way:
function highlightSearchTerm(string, substring) {
let arr = substring.split(' ');
arr.forEach(el => {
const regex = new RegExp(el, 'ig'),
temp = el;
el = el.replace(regex, `<mark>${el}</mark>`);
string = string.replace(temp, el);
})
return string;
}
let text = document.querySelector('div').innerHTML;
document.querySelector('div').innerHTML = highlightSearchTerm(text, 'From Linux');
<div>From Windows to Linux</div>
this is how you return true or false if your text includes the substring
let text = document.querySelector('div').innerHTML;
function isIncludesSubstring(text, substring){
let arr = substring.split(' '),
arrResult = [];
arr.forEach(el => {
const regex = new RegExp(el, 'ig');
arrResult.push(regex.test(text));
});
/* arrResult includes true or false based on whether substring single word
is included in the text or not, the function will return true if all words are included
else it will return false */
return arrResult.includes(false) ? false : true;
}
console.log(isIncludesSubstring(text, 'From Windows Linux'))
console.log(isIncludesSubstring(text, 'To Windows from'))
console.log(isIncludesSubstring(text, 'From Test Linux'))
<div>From Windows to Linux</div>
I'm trying to get an array of JSON objects. To do that, I'm trying to make the input I have parsable, then parse it and push it to that array using a for loop. The inputs I have to work with look like this:
firstname: Chris, lastname: Cheshire, email: chris#cmdcheshire.com, viewerlink: audiencematic.com/viewer?v\u003dTESTSHOW\u0026push\u003d8A043B5A, tempid: 8A043B5A, permaid: F8tGYNx, showid: TESTSHOW
I've gotten it to the point where each loop produces something like this:
{ "firstname": First Name, "lastname": Last Name, "email": sample#gmail.com, "viewerlink": audiencematic.com/viewer?v=TESTSHOW&push=715B3074, "tempid": 715B3074, "permaid": F8tGYNx, "showid": TESTSHOW }
But got stuck on the last bit, making the values strings. I want it to look like this, so I can use JSON.parse():
{ "firstname": "First Name", "lastname": "Last Name", "email": "sample#gmail.com", "viewerlink": "audiencematic.com/viewer?v=TESTSHOW&push=715B3074", "tempid": "715B3074", "permaid": "F8tGYNx", "showed": "TESTSHOW" }
I tried a couple of different methods I found on here, but one of the values is a URL and the period is screwing with the replace expressions. I tried using the replace function like this:
var jsonStr2 = jsonStr.replace(/(: +\w)|(:+\w)/g, function(matchedStr) {
return ':"' + matchedStr.substring(2, matchedStr.length) + '"';
});
But it just becomes this:
{ "firstname":""irst Name, "lastname":""ast Name, "email":""ample#gmail.com, "viewerlink":""udiencematic.com/viewer?v=TESTSHOW&push=715B3074, "tempid":""15B3074, "permaid":""8tGYNx, "showid":""ESTSHOW }
How should I change my replace function?
(I tried that code because I'm using
var jsonStr = string.replace(/(\w+:)|(\w+ :)/g, function(matchedStr) {
return '"' + matchedStr.substring(0, matchedStr.length - 1) + '":';
});
to put parenthesis around the key sides and that seems to work.)
FIGURED IT OUT!! SEE MY ANSWER BELOW.
One option might be to try using a deserialized version of the string, alter the values associated with the properties of the object, and then convert back to a string.
var person = "{fname:\"John\", lname:\"Doe\", age:25}";
var obj = JSON.parse(person);
for (x in obj) {
obj[x] = "";
}
var result = JSON.stringify(obj);
It's a little longer than doing a string replacement, but I find it a little easier to follow.
I figured it out! I just had to mess around in regexr to figure out what conditions I needed. Here's the working for loop code:
for (i = 0; i < audiencelistdirty.feed.openSearch$totalResults.$t; i++) {
var string = '{ ' + audiencelistdirty.feed.entry[i].content.$t + ' }';
var jsonStr = string.replace(/(\w+:)|(\w+ :)/g, function(matchedStr) {
return '"' + matchedStr.substring(0, matchedStr.length - 1) + '":';
});
var jsonStr1 = jsonStr.replace(/(:(.*?),)|(:\s(.*?)\s)/g, function(matchedStr) {
return ':"' + matchedStr.substring(2, matchedStr.length - 1) + '",';
});
var jsonStr2 = jsonStr1.replace(/(",})/g, function(matchedStr) {
return '" }';
});
var newObj = JSON.parse(jsonStr2);
audiencelist.push(newObj);
};
It's pretty ugly but it works.
EDIT: Sorry, I completely misread the question. To replace the values with quoted strings use this regex replace function:
const str =
'firstname: Chris, lastname: Cheshire, email: chris#cmdcheshire.com, viewerlink: audiencematic.com/viewer?v\u003dTESTSHOW\u0026push\u003d8A043B5A, tempid: 8A043B5A, permaid: F8tGYNx, showid: TESTSHOW'
const json = (() => {
const result = str
.replace(/\w+:\s(.*?)(?:,|$)/g, function (match, subStr) {
return match.replace(subStr, `"${subStr}"`)
})
.replace(/(\w+):/g, function (match, subStr) {
return match.replace(subStr, `"${subStr}"`)
})
return '{' + result + '}'
})()
Wrap the input string into commas then use a regex to identify the keys (between , and :) and their associated values (between : and ,) and construct the object directly as in the example below:
const input = ' firstname : Chris , lastname: Cheshire, email: chris#cmdcheshire.com, viewerlink: audiencematic.com/viewer?v\u003dTESTSHOW\u0026push\u003d8A043B5A, tempid: 8A043B5A, permaid: F8tGYNx, showid: TESTSHOW ';
const wrapped = `,${input},`;
const re = /,\s*([^:\s]*)\s*:\s*(.*?)\s*(?=,)/g;
const obj = {}
Array.from(wrapped.matchAll(re)).forEach((match) => obj[match[1]] = match[2]);
console.log(obj)
String.matchAll() is a newer function, not all JavaScript engines have implemented it yet. If you are one of the unlucky ones (or if you write code to be executed in a browser) then you can use the old-school way:
const input = ' firstname : Chris , lastname: Cheshire, email: chris#cmdcheshire.com, viewerlink: audiencematic.com/viewer?v\u003dTESTSHOW\u0026push\u003d8A043B5A, tempid: 8A043B5A, permaid: F8tGYNx, showid: TESTSHOW ';
const wrapped = `,${input},`;
const re = /,\s*([^:\s]*)\s*:\s*(.*?)\s*(?=,)/g;
const obj = {}
let match = re.exec(wrapped);
while (match) {
obj[match[1]] = match[2];
match = re.exec(wrapped);
}
console.log(obj);
The anatomy of the regex used above
The regular expression piece by piece:
/ # regex delimiter; not part of the regex but JavaScript syntax
, # match a comma
\s # match a white space character (space, tab, new line)
* # the previous symbol zero or more times
( # start the first capturing group; does not match anything
[ # start a character class...
^ # ... that matches any character not listed inside the class
: # ... i.e. any character but semicolon...
\s # ... and white space character
] # end of the character class; the entire class matches only one character
* # the previous symbol zero or more times
) # end of the first capturing group; does not match anything
\s*:\s* # zero or more spaces before and after the semicolon
( # start of the second capturing group
.* # any character, any number of times; this is greedy by default
? # make it not greedy
) # end of the second capturing group
\s* # zero or more spaces
(?= # lookahead positive assertion; matches but does not consume the matched substring
, # matches a comma
) # end of the assertion
/ # regex delimiter; not part of the regex but JavaScript
g # regex flag; 'g' for 'global' is needed to find all matches
Read about the syntax of regular expressions in JavaScript. For a more comprehensive description of the regex patterns I recommend reading the PHP documentation of PCRE (Perl-Compatible Regular Expressions).
You can see the regex in action and play with it on regex101.com.
I didn't find a solution inside regex's documentation for my current problem. I'm using javascript, html.
My code is like this:
var text = 'This [close](animal) is a dog';
I want to get this using regex:
'This {animal} is a dog';
what I mean, i want to have 'close' replaced with { and }.
I know, there's a solution like:
var res = text.replace('[close](','{').replace(')','}');
but in my case, I have many rules and I don't want to duplicate that line to do so. Sometimes I'm using other replacement like '[ xxxxx ]'.
Any idea? Thank you!
You may use
var text = 'This [close](animal) is a dog';
console.log(text.replace(/\[[^\][]*]\(([^()]*)\)/g, '{$1}'));
See the regex demo.
Details
\[ - a [ char
[^\][]* - 0 or more chars other than [ and ]
]\( - a ]( substring
([^()]*) - Capturing group 1: any 0 or more chars other than ( and )
\) - a ) char.
The {$1} replacement is the contents of the capturing group enclosed with braces.
If you can only have two values - close and open - inside [...], and replace close with {...} and open with }...{, you may use
var text = '[open](animal)This [close](animal) is a dog';
console.log(text.replace(/\[(open|close)]\(([^()]*)\)/g, function($0, $1, $2) {
return $1==='close' ? '{'+$2+'}' : '}'+$2+'{';})
);
Don't forget, that you can pass custom regex in Array.prototype.replace. In your case it would be text.replace(/[close](/g,'{'). Full solution of your question will look like:
var res = res.replace(/\[\w+\]\((.*)\)/, (a, b) => {
console.log(a, b);
return `{${b}}`;
});
The brackets around .* used to 'capture' animal inside variable b
Thank you Wiktor, I'v found a solution by what you said
var res0 = text.replace(/\[close]\(([^()]*)\)/g, '{$1}');
var res1 = text.replace(/\[open]\(([^()]*)\)/g, '}$1{');
Sorry if i did miskates, i'm not used to english expression so :-)
I've got a function that will display urls visited by users e.g. https://stackoverflow.com/questions/78/of/103/uk/ask
I want to use a regex expression with javascript to capture a specific parameter of the url and replace it with placeholder text. Taking the example above;
When https://stackoverflow.com/questions/7845w98439/uk/ask is matched, replace the '/78/of/103/uk/' part with '/task/of/total/country/'. The code below I have doesn't seem to work. Can someone show me where I'm going wrong?
JS:
const url_change = [
{expression: '/^/78/of/103/uk//i', value: '/task/of/total/country/'}
];
const urlParam = url_change.reduce(function(result, item) {
return element.classList.contains(item.expression) ? item.value : result;
}, '');
return urlParam(
toChange ? toChange.value : getElementText(element)
);
Do as follow:
var str = "https://stackoverflow.com/questions/78/of/103/uk/ask";
const url_change = [
{expression: /\/78\/of\/103\/uk\//i, value: '/task/of/total/country/'}
];
// console.log(url_change[0].expression.test(str));
var result = str.replace(url_change[0].expression,url_change[0].value);
console.log(result);
Sign / is special character (announces the end of Regex expression), it needs a mark \ lead to become a / in url, for Regex expression!
I'm trying to build a simple parser with PEG.js. I want the user to be able to input a series of keywords, with an optional "AND" between them, but I can't seem to get the optional and working. It always expects it, even though I've marked it with a ? (zero or one).
Paste this grammar into http://pegjs.majda.cz/online:
parse = pair+
pair = p:word and? { return p }
word = w:char+ { return w.join(""); }
char = c:[^ \r\n\t] { return c; }
and = ws* 'and'i ws*
ws = [ \t]
My goal is to have either of these inputs parse into an array of ["foo", "bar"]:
foo bar
foo and bar
Ok, nevermind. I figured it out. It was because I made the optional whitespace preceding the 'and' as part of the and rule, so it expected the rest of the rule. I just needed to move it out, into the pair rule, like so:
parse = pair+
pair = p:word ws* and? { return p }
word = w:char+ { return w.join(""); }
char = c:[^ \r\n\t] { return c; }
and = 'and'i ws*
ws = [ \t]
I know this is a very old question, but seeing how it wasn't answered and someone might stumble upon it I'd like to submit my answer:
Program
= w1:$Word _ wds:(_ "and"? _ w:$Word { return w; })* _ {
wds.unshift(w1);
return wds;
}
Word
= [a-zA-Z]+
_
= [ ,\t]*
The "and" is optional and you can add as many words as you want. The parser will skip the "and"'s and return a list of the words. I took the liberty of removing commas.
You can try it out https://pegjs.org/online with a string like:
carlos, peter, vincent, thomas, and shirly
Hope it helps someone.
Here is my answer:
start
= words
words
= head:word tail:(and (word))* {
var words = [head];
tail.forEach(function (word) {
word = word[1];
words.push(word);
})
return words;
}
and
= ' and '
/ $' '+
word
= $chars
chars 'chars'
= [a-zA-Z0-9]+