Wildcards in string equality testing

How can I match a string with a template, where the template uses asteriskses (*) to specify a wildcard?

The wildcard should match any amount of characters, but should also be escapable (\* should not be detected as a wildcard)

For example, I should get these results using the wildcard function wildCompare(string, template) (just as an example)

t = "*have some bacon"
wildCompare("Here, have some bacon", t) // true
wildCompare("Can I have some bacon please", t) // false
wildCompare("have some bacon", t) // true
t = "* \*LOL\* *"
wildCompare("*LOL*", t) // false
wildCompare("Haha *LOL* .", t) // true
t = "hello"
wildCompare("Hi, hello", t) // false
wildCompare("hello, hi", t) // false
wildCompare("hello", t) // true
t = "j*t";
wildCompare("javascript", t) // true
wildCompare("jscript", t) // true
wildCompare("Javascript", t) // false
wildCompare("just out", t) // true
wildCompare("just out here", t) // false

I'm not very confident in regex, but I could imagine that some kind of code that loops over the characters in a string to see if it is a wildcard would work, however, I am not entirely sure how this could be implemented to work in the way specified above.

After testing the two answers below, the indexOf method created by Cerbrus seems to be the fastest, however, his implementation does not support escaping of wildcards.

Answers:

Answer

Something like this works, if you ignore the need to escape the wildcard:

t = "I*have some bacon"
function wildCompare(string, search){
    var prevIndex = -1,
        array = search.split('*'), // Split the search string up in sections.
        result = true;
    for(var i = 0; i < array.length && result; i++){ // For each search section
        var index = string.indexOf(array[i]); // Find the location of the current search section
        if(index == -1 || index < prevIndex){ // If the section isn't found, or it's placed before the previous section...
            return false;
        }
    }
    return result;
}

Results:

wildCompare("Here, have some bacon", t) // False
wildCompare("I would like to have some bacon please", t) // True
wildCompare("I have some bacon", t)) // True

The easiest way to work around the "Wildcard escaping" thing would be to make the wildcard a variable character:

function wildCompare(string, search, wildcard){
    var prevIndex = -1,
        sa = search.split(wildcard),
        // Etc

This way, you can specify what the wildcard character(s) are, allowing for a little more flexibility.


Disclaimer:
I'm sure there's plenty of reasons this code isn't "good", "efficient", or something like that, but it's a way it could be done. Also, it might return incorrect values in some cases.


mrfishie fixed the function for mutliple sequential wildcards:

function wildCompareNew(string, search) {
    var startIndex = 0, array = search.split('*');
    for (var i = 0; i < array.length; i++) {
        var index = string.indexOf(array[i], startIndex);
        if (index == -1) return false;
        else startIndex = index;
    }
    return true;
}

Fiddle

Answer

As long as you include preg_quote from PHPJS (to replace characters that shouldn't get replaced by regex, such as .), then you can do this in one line using regex. It will also ignore your escaped characters.

function preg_quote(str, delimiter) {
  //  discuss at: http://phpjs.org/functions/preg_quote/
  // original by: booeyOH
  // improved by: Ates Goral (http://magnetiq.com)
  // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  // improved by: Brett Zamir (http://brett-zamir.me)
  // bugfixed by: Onno Marsman
  //   example 1: preg_quote("$40");
  //   returns 1: '\\$40'
  //   example 2: preg_quote("*RRRING* Hello?");
  //   returns 2: '\\*RRRING\\* Hello\\?'
  //   example 3: preg_quote("\\.+*?[^]$(){}=!<>|:");
  //   returns 3: '\\\\\\.\\+\\*\\?\\[\\^\\]\\$\\(\\)\\{\\}\\=\\!\\<\\>\\|\\:'

  return String(str)
    .replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
}

function wildCompare(str, search) {
    return str.match(new RegExp('^' + preg_quote(search).replace(/\\\*/g, '.*').replace(/\\\.\*/g, '\*') + '$')) !== null;
}

Please note that in order to escape a character you need to use \\* instead of \* (as the latter escapes the * and turns into * before we can do anything.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.