Modify formatting-pattern to replace placeholders in string - javascript

I write currently a simple formatting function to replace some placeholders in a string.
var format = function (a, c) {
return a.replace(/{ *([^} ]+) *}/g, function (b, a) {
b = c;
a.replace(/[^.|\[\]]+/g, function (a) {
b = b[a];
});
return b;
});
};
The syntax uses currently curly-bracket notation {key}, I try now to modify the RegExp-pattern to work with one percent instead %key.
var pattern = /{ *([^} ]+) *}/g;
I tried to just replace the parentheses {} with a percent %, but this still doesn't work properly.
var pattern = /% *([^% ]+) */g;
The original pattern works with the following conditions as expected:
var data = {
obj: {
foo: 'Foo',
bar: 'Bar'
},
arr: ['Foo', 'Bar']
};
var objTest = '{obj.foo}, is not equal to {obj.bar}.'
format(objTest, data) // => 'Foo, is not equal to Bar.'
var arrTest = '{arr[0]}, is not equal to {arr[1]}.'
format(arrTest, data) // => 'Foo, is not equal to Bar.'
If we use my modified pattern it seems that the last character after each placeholder-replacement will be removed:
'%obj.foo, is not equal to %obj.bar.' // => 'undefined is not equal to Bar'
'%arr[0], is not equal to %arr[1]' // => 'undefined is not equal to Bar'
Any ideas how to modify the pattern to make it possible to use it with percentage % instead of curly-brackets {}?

You can use this pattern:
var regex = /%([^\b]+)/g;
which means a % sign followed by a complete word. This excludes whitespace characters, underscores, etc.
If you instead want to be able to use those characters as well, you can write:
var regex = /%(\S+)/g;
which is the equivalent of:
var regex = /%([^\s]+)/g;

The reason for this is that your modified regex pattern does not know when to stop matching. In the previous one it terminated at the following } symbol.
This is not an easy thing to fix as there is a myriad of possibilities that could be seen to end your format, here you have a piece of punctuation at the end of each replacement string, i.e. a full stop %arr[1]. or comma %obj.foo,.
So to make this work in your case toy could replace } in the original pattern with [\.,] i.e.
/% *([^% ]+) *[\.,]/g
This will work, but now your replacement pattern needs to always be terminated with either a full stop or comma which i suspect is not exactly what you want. Better to terminate with a know character such as % which would make your matching pattern /% *([^% ]+) *%/g and your format %obj.foo% and you can output a % by doubling up i.e. %obj.bar%%%

Related

Matching an exact word from a string

I need a way to match a word against a string and not get false positives. Let me give an example of what I mean:
"/thing" should match the string "/a/thing"
"/thing" should match the string "/a/thing/that/is/here"
"/thing" should NOT match the string "/a/thing_foo"
Basically, it should match if the exact characters are there in the first string and the second, but not if there are run-ons in the second (such as an underscore like in thing_foo).
Right now, I'm doing this, which is not working.
let found = b.includes(a); // true
Hopefully my question is clear enough. Thanks for the help!
Boy did this turn in to a classic XY Problem.
If I had to guess, you want to know if a path contains a particular segment.
In that case, split the string on a positive lookahead for '/' and use Array.prototype.includes()
const paths = ["/a/thing", "/a/thing/that/is/here", "/a/thing_foo"]
const search = '/thing'
paths.forEach(path => {
const segments = path.split(/(?=\/)/)
console.log('segments', segments)
console.info(path, ':', segments.includes(search))
})
Using the positive lookahead expression /(?=\/)/ allows us to split the string on / whilst maintaining the / prefix in each segment.
Alternatively, if you're still super keen in using a straight regex solution, you'll want something like this
const paths = ["/a/thing", "/a/thing/that/is/here", "/a/thing_foo", "/a/thing-that/is/here"]
const search = '/thing'
const rx = new RegExp(search + '\\b') // note the escaped backslash
paths.forEach(path => {
console.info(path, ':', rx.test(path))
})
Note that this will return false positives if the search string is followed by a hyphen or tilde as those are considered to be word boundaries. You would need a more complex pattern and I think the first solution handles these cases better.
I'd recommend using regular expressions...
e.g. The following regular expression /\/thing$/ - matches anything that ends with /thing.
console.log(/\/thing$/.test('/a/thing')) // true
console.log(/\/thing$/.test('/a/thing_foo')) // false
Update: To use a variable...
var search = '/thing'
console.log(new RegExp(search + '$').test('/a/thing')) // true
console.log(new RegExp(search + '$').test('/a/thing_foo')) // false
Simply with following regex you can do it
var a = "/a/thing";
var b = "/a/thing/that/is/here";
var c = "/a/thing_foo";
var pattern = new RegExp(/(:?(thing)(([^_])|$))/);
pattern.test(a) // true
pattern.test(b) // true
pattern.test(c) // false

Simplify parent-child expression with regex

I have a JavaScript object such as this:
{
a : {
b : c,
d : e
}
}
In my code, if b my was my starting point, I could input the string "this.$parent.d" which would evaluate to e.
However I could also input the string "this.$parent.b.$parent.d" to get the same result.
My question is if I am given a string such as "this.$parent.b.$parent.d" how can I simplify it to the first way?
I think what I need to do is use regex to replace all occurrences of "$parent.<anything>.$parent" with just "$parent" and that should work but not sure how to do this exactly.
In fact, you'll want to simplify an even shorter expression:
.prop.$parent
Such a substring should be removed, since it is a no-operation.
Here is how you could do that with a regular expression:
function simplify(str) {
const expression = /\.(?!\$parent\.)[\w$]+\.\$parent(?=\.|$)/g;
while (str.length > (str = str.replace(expression, "")).length) {}
return str;
}
var strings = [
"this.$parent.b.$parent.d", // should be this.$parent.d
"this.$parent.b.$parent.d.$parent.b.$parent.b", // should be this.$parent.b
"this.$parent.b.$parent.$parent.b.$parent", // should be this.$parent.$parent
"this.$parent.b.$parent.d.$parent.$parent.b", // should be this.$parent.$parent.b
"this.$parent.b.c.$parent.$parent.b", // should be this.$parent.b
"this.$parentX.b.$parentX", // should be this.$parentX.b.$parentX
];
const results = strings.map(simplify);
console.log(results);
I think the regex you're looking for is this:
/\.\$parent\.[^$]?[^p]?[^a]?[^r]?[^e]?[^n]?[^t]?\.\$parent/g
Someone who is better at regex than me can hopefully simplify this, since it seems very verbose.
The solution below uses a recursive function, because if there are two instances that get replaced in the same string, you have now created another instance of .$parent.*.$parent, which needs to then get replaced with just .$parent.
For example:
this.$parent.b.$parent.d.$parent.$parent.b
After 1 .replace() --> this.$parent.d.$parent.$parent.b
After 2 .replace() --> this.$parent.$parent.b
Here's the code in action:
var expression = /\.\$parent\.[^$]?[^p]?[^a]?[^r]?[^e]?[^n]?[^t]?\.\$parent/g;
var strings = [
"this.$parent.b.$parent.d", // should be this.$parent.d
"this.$parent.b.$parent.d.$parent.b.$parent.b", // should be this.$parent.b
"this.$parent.b.$parent.$parent.b.$parent", // should be this.$parent.$parent
"this.$parent.b.$parent.d.$parent.$parent.b" // should be this.$parent.$parent.b
];
function simplify (str) {
var str = str.replace(expression, ".$parent");
if (expression.test(str)) {
return simplify(str);
}
return str;
}
strings.forEach(function (str) {
console.log(simplify(str));
});

Regex - match the better part of a word in a search string

I am using Javascript and currently looking for a way to match as many of my pattern's letters as possible, maintaining the original order..
For example a search pattern queued should return the march Queue/queue against the any of the following search strings:
queueTable
scheduledQueueTable
qScheduledQueueTable
As of now I've reached as far as this:
var myregex = new RegExp("([queued])", "i");
var result = myregex.exec('queueTable');
but it doesn't seem to work correctly as it highlights the single characters q,u,e,u,e and e at the end of the word Table.
Any ideas?
Generate the regex with optional non-capturing group part where regex pattern can be generate using Array#reduceRight method.
var myregex = new RegExp("queued"
.split('')
.reduceRight(function(str, s) {
return '(?:' + s + str + ')?';
}, ''), "i");
var result = myregex.exec('queueTable');
console.log(result)
The method generates regex : /(?:q(?:u(?:e(?:u(?:e(?:d?)?)?)?)?)?)?/
UPDATE : If you want to get the first longest match then use g modifier in regex and find out the largest using Array#reduce method.
var myregex = new RegExp(
"queued".split('')
.reduceRight(function(str, s) {
return '(?:' + s + str + ')?';
}, ''), "ig");
var result = 'qscheduledQueueTable'
.match(myregex)
.reduce(function(a, b) {
return a.length > b.length ? a : b;
});
console.log(result);
I think the logic would have to be something like:
Match as many of these letters as possible, in this order.
The only real answer that comes to mind is to get the match to continue if possible, but allow it to bail out. In this case...
myregex = /q(?:u(?:e(?:u(?:e(?:d|)|)|)|)|)/;
You can generate this, of course:
function matchAsMuchAsPossible(word) { // name me something sensible please!
return new RegExp(
word.split("").join("(?:")
+ (new Array(word.length).join("|)"))
);
}
You are using square brackets - which mean that it will match a single instance of any character listed inside.
There are a few ways of interpreting your intentions:
You want to match the word queue with an optional 'd' at the end:
var myregex = new RegExp("queued?", "i");
var result = myregex.exec('queueTable');
Note this can be shorter try this:
'queueTable'.match(/queued?/i);
I also removed the brackets as these were not adding anything here.
This link provides some good examples that may help you further: https://www.w3schools.com/js/js_regexp.asp
When you use [] in a regular expression, it means you want to match any of the characters inside the brackets.
Example: if I use [abc] it means "match a single character, and this character can be 'a', 'b' or 'c'"
So in your code [queued] means "match a single character, and this character can be 'q', 'u', 'e' or 'd'" - note that 'u' and 'e' appear twice so they are redundant in this case. That's why this expression matches just one single character.
If you want to match the whole string "queued", just remove the brackets. But in this case it won't match, because queueTable doesn't have 'd'. If you want 'd' to be optional, you can use queued? as already explained in previous answers.
Try something like the following :
var myregex = /queued?\B/g;
var result = myregex.exec('queueTable');
console.log(result);

Replace leading numbers with Javascript regex

As a follow up to this question (not by me), I need to replace leading numbers of an id with \\3n (where n is the number we're replacing).
Some examples:
"1foo" -> "\\31foo"
"1foo1" -> "\\31foo1"
"12foo" -> "\\31\\32foo"
"12fo3o4" -> "\\31\\32fo3o4"
"foo123" -> "foo123"
Below is a solution that replaces every instance of the number, but I don't know enough regex to make it stop once it hits a non-number.
function magic (str) {
return str.replace(/([0-9])/g, "\\3$1");
}
... Or is regex a bad way to go? I guess it would be easy enough to do it, just looping over each character of the string manually.
Here is a way to achieve what you need using a reverse string + look-ahead approach:
function revStr(str) {
return str.split('').reverse().join('');
}
var s = "12fo3o4";
document.write(revStr(revStr(s).replace(/\d(?=\d*$)/g, function (m) {
return m + "3\\\\";
}))
);
The regex is matching a number that can be followed by 0 or more numbers only until the end (which is actually start) of a reversed string (with \d(?=\d*$)). The callback allows to manipulate the match (we just add reversed \\ and 3. Then, we just reverse the result.
Just use two steps: first find the prefix, then operate on its characters:
s.replace(/^\d+/, function (m) {
return [].map.call(m, function (c) {
return '\\3' + c;
}).join('');
});
No need to emulate any features.
Here is how I would have done it:
function replace(str) {
var re = /^([\d]*)/;
var match = str.match(re)[0];
var replaced = match.replace(/([\d])/g, "\\3$1");
str = str.replace(match, replaced);
return str;
}
document.write(replace("12fo3o4"));
Don't get me wrong: the other answers are fine! My focus was more on readability.

Regexp: don't replace from the beginning

I've a strings like pie and cherrypie.
How do I replace all the occurrences of pie in the strings, that aren't started with pie?
Here's what I've at the moment:
var regexp = new RegExp('pie', 'g');
var string = 'pie';
var string2 = 'cherrypie';
var cap = function(val) {
return val.charAt(0).toUpperCase()+val.slice(1);
}
console.log(string.replace(regexp, cap));
console.log(string2.replace(regexp, cap));
Online demo - http://jsbin.com/ERUpuYuq/1/
Just open a console and you will see two lines:
Pie
cherryPie
An expected result is
pie - since it starts with "pie"
cherryPie
I've tried to add * symbol at the beginning of my regexp, without any luck.
If I set .pie as my regexp, I get this:
pie
cherrYpie
The solution would be a regexp replacement, that will catch all the occurances, that are placed not at the beginnign of the string.
Anyone knows regexp?
Note: cap function shouldn't be modified.
You can add your own function that conditionally calls cap, which you say you can't modify.
var string = 'pie';
var string2 = 'cherrypie';
var string3 = 'piepie';
var cap = function(val) {
return val.charAt(0).toUpperCase()+val.slice(1);
};
// Capture two arguments: $1 is optional and only set if the string begins with
// something.
var regexp = new RegExp('(.+)?(pie)', 'g');
var capCheck = function($0, $1, $2){
// If $1 is set, return it plus the capitalised 'pie', otherwise return the
// original string (no replacement).
return $1 ? $1 + cap($2) : $0;
};
console.log(string.replace(regexp, capCheck));
// => pie
console.log(string2.replace(regexp, capCheck));
// => cherryPie
console.log(string3.replace(regexp, capCheck));
// => piePie
Since a negative lookbehind assertion isn't available in JavaScript RegExp (I has a sad), this answer gave me the solution needed.
More methods of mimicking lookbehind in JavaScript. The one about reversing the string, using lookahead, then reversing again was quite nice, but it means you can't use lookahead the normal way at the same time.
For example, you can try dot. http://jsbin.com/ERUpuYuq/3/edit?html,js,console,output

Categories