4

I've been wrestling with this for a while and can't figure it out. I see in Regex to match string containing two names in any order that lookarounds can be used to look for words occurring in any order. I added some additional groups to the example from that post:

const regex = /^(?=.*\b(jack)\b)(?=.*\b(james)\b).*$/;
"hi james this is jack".match(regex);
  // => ["hi james this is jack", "jack", "james"]

However, I also want to make both of the words optional. That is, something like this:

"hi james this is jack".match(someMagicRegex);
  // => ["hi james this is jack", "jack", "james"]

"hi this is jack".match(someMagicRegex);
  // => ["hi this is jack", "jack", undefined]

"hi james".match(someMagicRegex);
  // => ["hi james", undefined, "james"]

The important thing would be that the matches remain in the correct order. That is the jack match would always be the second element in the matches array.

I've tried adding ? at different places (including after new groupings), but nothing I tried produced the desired result.

Is something like this possible?


UPDATE: To be more specific, I'm going to be using this on a series of CSS media queries and am looking to match optional min-width: Xpx and max-width: Ypx expressions:

"only screen and (min-width: 500px) and (max-width: 599px)".match(someMagicRegex);
  // => ["...", "500", "599"]

"only screen and (min-width: 500px)".match(someMagicRegex);
  // => ["...", "500", undefined]

"(max-width: 599px)".match(someMagicRegex);
  // => ["...", undefined, "599"]

(Where the second match would be the numeric portion of the min-width value and the third match would be the numeric portion of the max-width value.)

Community
  • 1
  • 1
robertwbradford
  • 6,181
  • 9
  • 36
  • 61

4 Answers4

0

UPDATE here is a working example. I used 2 regural expressions. First to find class or tag in CSS file/string, and the second regular expression is looking for property that you want.

var fileCSS='.entry-meta {position: relative; top: 5px; left: 5px; font-size: 0.8em; line-height: 18px; color: #aaa; } .entry-footer { display: inline; color: white; margin: 0 2em; padding: 0; font-size: 0.8em; line-height: 18px; } div { MIN-width: 200px; display: block; max-width: 80% ; position: relative; background: rgba(255,255,255,0.05); padding-bottom: 20px; } img { width: 640px; min-width: 12vh ;height: auto; max-width: 800px; margin-bottom: 10px; }';

var properties = ['min-width', 'max-width'];

function pMatch(CSS, classOrTag, propertiesToFind) {
 var foundProperties=[]; var found=null;
 mainREGEX= new RegExp(classOrTag+"\\s*[{][()#\"'/\*a-z0-9\\s:%;*,.=-]*[}]", 'i');
 var found=CSS.match(mainREGEX)[0];
 if(found!==null) {
  for(a=0; a<propertiesToFind.length; a++) {
   propertyREGEX=new RegExp('('+propertiesToFind[a]+'\\s?:\\s?[0-9.]+[a-z%\\s]+).?;', 'i');
   var property=found.match(propertyREGEX);
   property?foundProperties.push(property[1]):foundProperties.push('undefined');
  }
 }
 return foundProperties;
}
console.log(pMatch(fileCSS, 'div', properties));
console.log(pMatch(fileCSS, '.entry-meta', properties));
console.log(pMatch(fileCSS, 'img', properties));
0

Not sure do you need full match as first result in matches array (?), but this could give you an idea. No 'magic regex' (from me, at least), just plain regex array, with capturing groups:

regex = ["min-width:\\s*(\\d+)px","max-width:\\s*(\\d+)px"];

str = "only screen and (min-width: 500px) and (max-width: 599px)";

//str = "only screen and (min-width: 500px)"; 

//str="(max-width: 599px)";


matches = [];

for( i = 0; i < regex.length; i++ ) {
    if( RegExp(regex[i]).exec(str) ) {
        matches.push(RegExp(regex[i]).exec(str)[1]);
    } else {
        matches.push(undefined)
    }
}

console.log(matches);

Test case 2:

regex = ["min-width:\\s*(\\d+)px","max-width:\\s*(\\d+)px"];



str = "only screen and (min-width: 500px)"; 

//str="(max-width: 599px)";


matches = [];

for( i = 0; i < regex.length; i++ ) {
    if( RegExp(regex[i]).exec(str) ) {
        matches.push(RegExp(regex[i]).exec(str)[1]);
    } else {
        matches.push(undefined)
    }
}

console.log(matches);

Test case 3:

regex = ["min-width:\\s*(\\d+)px","max-width:\\s*(\\d+)px"];



str="(max-width: 599px)";


matches = [];

for( i=0; i < regex.length; i++ ) {
    if( RegExp(regex[i]).exec(str) ) {
        matches.push(RegExp(regex[i]).exec(str)[1]);
    } else {
        matches.push(undefined)
    }
}

console.log(matches);
ElChiniNet
  • 2,778
  • 2
  • 19
  • 27
sinisake
  • 11,240
  • 2
  • 19
  • 27
0

Sorry my answer is in python, but it should work the same in any language with lookarounds and backreferences.

The following code matched all three of your test-cases correctly, and I expect it to correctly match in virtually any case:

import re

strings = ["only screen and (min-width: 500px) and (max-width: 599px)", "only screen and (min-width: 500px)", "(max-width: 599px)"]

regex = re.compile(r'(min|max)-width:\s*(\d+)px(.*(?!\1)(max|min)-width:\s*(\d+)px)?')
for string in strings:
    match = re.search(regex, string)
    print
    print string
    if match:
        term_1 = match.group(1)
        value_1 = match.group(2)
        term_2 = match.group(4)
        value_2 = match.group(5)
        print "Match!\n{} {}".format(term_1+"-width:", value_1)
        if term_2:
            print "{} {}".format(term_2+"-width:", value_2)
    else:
        print "Not a match"

When I ran it I got this output:

only screen and (min-width: 500px) and (max-width: 599px)
Match!
min-width: 500
max-width: 599

only screen and (min-width: 500px)
Match!
min-width: 500

(max-width: 599px)
Match!
max-width: 599

The key idea is explained in Jason Cohen's answer to this question. He explained that the lookahead (or in this case the negative lookahead) means "match expr but after that continue matching at the original match-point."

In my example here, the lookahead checks to make sure it is not looking at the same string it matched at the beginning and then checks if, at that same point, there is a max or min. Note that after the first px, even the .* is enclosed in parentheses. This keeps it from behaving too greedily. Also, the entire second term is marked with the ? so that the string will still match, even if a second occurrence is not found.

Community
  • 1
  • 1
TallChuck
  • 1,725
  • 11
  • 28
0

Here you have another solution. It uses a RegExp to match the patterns and a while to stores the values:

function getMatches (str) {
    var reg = /(min|max)-width\s*:\s*(\d*\.?\d+)/g,
        ret = [undefined, undefined],
        arr;
    while ((arr = reg.exec(str)) !== null) {
        ret[+(arr[1] === "max")] = arr[2];
    }
    return ret;
}

Jsfiddle example: https://jsfiddle.net/elchininet/bwg1onk6/

ElChiniNet
  • 2,778
  • 2
  • 19
  • 27