Simplify parent-child expression with regex - javascript

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));
});

Related

Dynamically compare accuracy of regex patterns for match

Supposing I have two regexes and both match a string, but one of them matches it in a stricter way, is there a way to figure that out programmatically?
For example, I'm matching this string:
/path/on/file/system
and I have the following two regular expressions:
const opt1 = /\/path\/on/;
const opt2 = /\/path/;
I can see with my eyes that opt1 is stricter, but how can javascript know about that?
Is converting the regex to a string and checking for character length a good measure of strictness?
You can implement this function by:
Sorting your regular expressions by length.
loop through your sorted regular expressions array to check if there is a match
then return the most strict matching element.
function check(arrayOfRegEx, str) {
//sort the regex array by length
var sortedArr = arrayOfRegEx.sort(function(a, b) {
return a.toString().length - b.toString().length || a.toString().localeCompare(b);
});
let mostStrict = '';
sortedArr.forEach(function(reg) {
if(new RegExp((reg.toString()).replace(/\\/g, "").substring(1)).test(str)) {
mostStrict = reg;
}
});
return mostStrict;
}
var result = check([/\/path/, /\/test\/test/, /\/path\/on/], '/path/on/file/system');
console.log(result); // returns /\/path\/on/
And of course you can tweak this function to fit your needs

Failing test as a string

I am running through some exercises and run into this on codewars. Its a simple exercise with Instructions to create a function called shortcut to remove all the lowercase vowels in a given string.
Examples:
shortcut("codewars") // --> cdwrs
shortcut("goodbye") // --> gdby
I am newbie so I thought up this solution. but it doesn't work and I have no idea why
function shortcut(string){
// create an array of individual characters
var stage1 = string.split('');
// loop through array and remove the unneeded characters
for (i = string.length-1; i >= 0; i--) {
if (stage1[i] === "a"||
stage1[i] === "e"||
stage1[i] === "i"||
stage1[i] === "o"||
stage1[i] === "u") {
stage1.splice(i,1)
;}
};
// turn the array back into a string
string = stage1.join('');
return shortcut;
}
My gut is telling me that it will probably something to like split and join not creating "true" array's and strings.
I did it at first with a regex to make it a little more reusable but that was a nightmare. I would be happy to take suggestions on other methods of acheiving the same thing.
You are returning the function itself, instead of returning string
Using regex:
var str = 'codewars';
var regex = /[aeiou]/g;
var result = str.replace(regex, '');
document.write(result);
if interested in Regular Expression ;)
function shortcut(str) {
return str.replace(/[aeiou]/g, "");
}

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.

Modify formatting-pattern to replace placeholders in string

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%%%

Validating alphabetic only string in javascript

How can I quickly validate if a string is alphabetic only, e.g
var str = "!";
alert(isLetter(str)); // false
var str = "a";
alert(isLetter(str)); // true
Edit : I would like to add parenthesis i.e () to an exception, so
var str = "(";
or
var str = ")";
should also return true.
Regular expression to require at least one letter, or paren, and only allow letters and paren:
function isAlphaOrParen(str) {
return /^[a-zA-Z()]+$/.test(str);
}
Modify the regexp as needed:
/^[a-zA-Z()]*$/ - also returns true for an empty string
/^[a-zA-Z()]$/ - only returns true for single characters.
/^[a-zA-Z() ]+$/ - also allows spaces
Here you go:
function isLetter(s)
{
return s.match("^[a-zA-Z\(\)]+$");
}
If memory serves this should work in javascript:
function containsOnlyLettersOrParenthesis(str)
(
return str.match(/^([a-z\(\)]+)$/i);
)
You could use Regular Expressions...
functions isLetter(str) {
return str.match("^[a-zA-Z()]+$");
}
Oops... my bad... this is wrong... it should be
functions isLetter(str) {
return "^[a-zA-Z()]+$".test(str);
}
As the other answer says... sorry

Categories