str.match(reg) equivalent for str.split(".") in javascript - javascript

Is there a regular expression reg, so that for any string str the results of str.split(".") and str.match(reg) are equivalent? If multiline should somehow matter, a solution for a single line would be sufficient.
As an example: Considering the RegExp /[^\.]+/g: for the string "nice.sentance", "nice.sentance".split(".") gives the same result as "nice.sentance".match(/[^\.]+/g) - ["nice", "sentance"]. However, this is not the case for any string. E.g. for the empty string "" they would give different results, "".split(".") returning [""] and "".match(/[^\.]+/g) returning null, meaning /[^\.]+/g is not a solution, as it would need to work for any possible string.
The question comes from a misinterpretation of another question here and left me wondering. I do not have a practical application for it at the moment and am interested because i could not find an answer - it looks like an interesting RegExp problem. It may however be impossible.
Things i have considered:
Imho it is fairly clear that reg needs the global flag, removing capture groups as a possibility
/[^\.]+/g does not match empty parts, e.g. for "", ".a" or "a..a"
/[^\.]*/g produces additional empty strings after non-empty matches, because when iteration starts for the next match, it can fit in an empty match. E.g. for "a"
With features not available on javascript currently (but on other languages), one could repair the previous flaw: /(?<=^|\.)[^\.]*/g
My conclusion here would be that real empty matches need to be considered but cannot be differentiated from empty matches between a non-empty match and the following dot or EOL, without "looking behind". This seems a bit vague to count as a proper argument for it being impossible, but maybe is already enough. There might however be a RegExp feature i don't know about, e.g. to advance the index after a match without including the symbol, or something similar to be used as a trick.
Allowing some correction step on the array resulting from match makes the problem trivial.
I found some related questions, which as expected utilize look-behind or capture groups though:
Regular Expression to find a string included between two characters while EXCLUDING the delimiters
characters between two delimiters
lots more similar to the above

I do not see the point but assume you have to apply this in an environment where .split is not available.
Crafting a matching regex that does the same as .split(".") or /\./ requires to account for several cases:
no input => empty split
single . => two empty splits
. at the beginning => empty split at position 0
. at the end => empty split at the end
. in the middle
multiple consecutive .s => one empty split per ..
Following this, I came up with the following solution:
^(?=\.)[^.]*|[^.]+(?=\.)|(?<=\.)[^.]*$|^$|[^.]+|(?=\.)(?<=\.)
Code Sample*:
const regex = /^(?=\.)[^.]*|[^.]+(?=\.)|(?<=\.)[^.]*$|^$|[^.]+|(?=\.)(?<=\.)/gm;
const test = `
.
.a
a.
a.a
a..a
.a.
..a..
.a.z
..`;
var a = test.split("\n");
a.forEach(str => {
console.log(`"${str}"`);
console.log(str.split("."));
let m; let matches = [];
while ((m = regex.exec(str)) !== null) {
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
matches.push(m[0]);
}
console.log(matches);
});
The output should be read in triple blocks: input/split/regex-match.
The output on each 2nd and 3rd line should be the same.
Have fun!
*Caveat: This requires RegExp Lookbehind Assertions: JavaScript Lookbehind assertions are approved by TC39 and are now part of the ES2018 standard.
RegExp Lookbehind Assertions have been implemented in V8 and shipped without flags with Google Chrome v62 and in Node.js v6 behind a flag and v9 without a flag. The Firefox team is working on it, and for Microsoft Edge, it's an implementation suggestion.

Related

What Regex would capture both the beginning and end from of a string?

I am trying to edit a DateTime string in typescript file.
The string in question is 02T13:18:43.000Z.
I want to trim the first three characters including the letter T from the beginning of a string AND also all 5 characters from the end of the string, that is Z000., including the dot character. Essentialy I want the result to look like this: 13:18:43.
From what I found the following pattern (^(.*?)T) can accomplish only the first part of the trim I require, that leaves the initial result like this: 13:18:43.000Z.
What kind of Regex pattern must I use to include the second part of the trim I have mentioned? I have tried to include the following block in the same pattern (Z000.)$ but of course it failed.
Thanks.
Any help would be appreciated.
There is no need to use regular expression in order to achieve that. You can simply use:
let value = '02T13:18:43.000Z';
let newValue = value.slice(3, -5);
console.log(newValue);
it will return 13:18:43, assumming that your string will always have the same pattern. According to the documentation slice method will substring from beginIndex to endIndex. endIndex is optional.
as I see you only need regex solution so does this pattern work?
(\d{2}:)+\d{2} or simply \d{2}:\d{2}:\d{2}
it searches much times for digit-digit-doubleDot combos and digit-digit-doubleDot at the end
the only disadvange is that it doesn't check whether say there are no minutes>59 and etc.
The main reason why I didn't include checking just because I kept in mind that you get your dates from sources where data that are stored are already valid, ex. database.
Solution
This should suffice to remove both the prefix from beginning to T and postfix from . to end:
/^.*T|\..*$/g
console.log(new Date().toISOString().replace(/^.*T|\..*$/g, ''))
See the visualization on debuggex
Explanation
The section ^.*T removes all characters up to and including the last encountered T in the string.
The section \..*$ removes all characters from the first encountered . to the end of the string.
The | in between coupled with the global g flag allows the regular expression to match both sections in the string, allowing .replace(..., '') to trim both simultaneously.

Compare strings with different spaces and potentially null characters

I'm currently making a wikipedia scraper for a project I'm doing. The problem is that my code sometimes produces bugs when trying to compare strings. If I have strings that look identical, they sometimes are still registered as different. For example:
var elementText = $("selector").text();
console.log(elementText); // "abc def"
console.log(elementText === "abc def"); // false
It seems that Wikipedia uses some weird characters that my code detects and doesn't like. I have tried:
function replaceBadSpaces(string) {
return decodeURIComponent(encodeURIComponent(string).replace("/%C2%A0/g", "%20"));
}
and using elementText.replace(/\s+/g, ''), and neither seem to work. How can I completely get rid of these characters so that strings that are intuitively equal actually do match as equal?
Note: I have also tested my code with ==, and it does seem to fix the issue; however, in the interest of avoiding future bugs I'd like to avoid using this fix.
Remove the quotation marks surrounding the first argument of replace. This is because you are using a regular expression ( /g )for the replace function, which does not need to be wrapped in quotation marks.
function replaceBadSpaces(string) {
return decodeURIComponent(encodeURIComponent(string).replace(/%C2%A0/g, "%20"));
}

javascript regex to match double word gives different result everytime

Here is the output from my browsers console. This regex checks for doubled_words, looks for occurrences of words (strings containing 1 or more letters) followed by whitespace followed by the same word.
var reg=/([A-Za-z\u00C0-\u1FFF\u2800-\uFFFD]+)\s+\1/gi;
undefined
reg.test("sdfs sdsdf")
true
reg.test("sdfs sdsdf")
false
the result is true alternate times, why is this weird behavior?
That behavior is due to use of global flag. Remove it
var reg=/([A-Za-z\u00C0-\u1FFF\u2800-\uFFFD]+)\s+\1/;
Using g causes regex state (lastIndex value) to be remembered across multipel calls to test or exec methods.
Check this official reference Read the Description section.

It seems that JavaScript RegExp isn't finding "leftmost longest"

I observe these results:
// Test 1:
var re = /a|ab/;
"ab".match(re); // returns ["a"] <--- Unexpected
// Test 2:
re = /ab|a/;
"ab".match(re); // returns ["ab"]
I would expect tests 1 and 2 to both return ["ab"], due to the principal of "leftmost longest". I don't understand why the order of the 2 alternatives in the regex should change the results.
Find the reason below:
Note that alternatives are considered left to right until a match is
found. If the left alternative matches, the right alternative is
ignored, even if it would have produced a “better” match. Thus, when
the pattern /a|ab/ is applied to the string “ab,” it matches only the
first letter.
(source: Oreilly - Javascript Pocket Reference - Chapter 9 Regular Expressions)
Thanks.
This is because JavaScript doesn't implement the POSIX engine.
POSIX NFA Engines work similarly to Traditional NFAs with one
exception: a POSIX engine always picks the longest of the leftmost
matches. For example, the alternation cat|category would match the full word "category" whenever possible, even if the first alternative ("cat") matched and appeared earlier in the alternation. (SEE MRE 153-154)
Source: Oreilly - Javascript Pocket Reference, p.4

JS/Jquery, Match not finding the PNG = match('/gif|jpg|jpeg|png/')

I have the following code which I use to match fancybox possible elements:
$('a.grouped_elements').each(function(){
var elem = $(this);
// Convert everything to lower case to match smart
if(elem.attr('href').toLowerCase().match('/gif|jpg|jpeg|png/') != null) {
elem.fancybox();
}
});
It works great with JPGs but it isn't matching PNGs for some reason. Anyone see a bug with the code?
Thanks
A couple of things.
Match accepts an object of RegExp, not a string. It may work in some browsers, but is definitely not standard.
"gif".match('/gif|png|jpg/'); // null​​​​​​​​​​​​​​​​​​​​​​​​​​​​
Without the strings
"gif".match(/gif|png|jpg/); // ["gif"]
Also, you would want to check these at the end of a filename, instead of anywhere in the string.
"isthisagif.nope".match(/(gif|png|jpg|jpeg)/); // ["gif", "gif"]
Only searching at the end of string with $ suffix
"isthisagif.nope".match(/(gif|png|jpg|jpeg)$/); // null
No need to make href lowercase, just do a case insensitive search /i.
Look for a dot before the image extension as an additional check.
And some tests. I don't know how you got any results back with using a string argument to .match. What browser are you on?
I guess the fact that it'll match anywhere in the string (it would match "http://www.giftshop.com/" for instance) could be considered a bug. I'd use
/\.(gif|jpe?g|png)$/i
You are passing a string to the match() function rather than a regular expression. In JavaScript, strings are delimited with single quotes, and regular expressions are delimited with forward slashes. If you use both, you have a string, not a regex.
This worked perfectly for me: /.+\.(gif|png|jpe?g)$/i
.+ -> any string
\. -> followed by a point.
(gif|png|jpe?g) -> and then followed by any of these extensions. jpeg may or may not have the letter e.
$ -> now the end of the string it's expected
/i -> case insensitive mode: matches both sflkj.JPG and lkjfsl.jpg

Categories