var str = "This is a test. Here is word1 and here is word2, which may or may not exist.";
var matches = str.match( /word1|word2/g );
//-> ["word1", "word2"]
String.prototype.match will run a regex against the string and find all matching hits. In this case we use alternation to allow the regex to match either word1 or word2.
You need to apply the global flag to the regex so that match() will find all results.
If you care about matching only on word boundaries, use /\b(?:word1|word2)\b/g.
regex javascript - match multiple search terms ignoring their order - Stack Overflow
Match multiple strings
javascript - Regular Expression to MATCH ALL words in a query, in any order - Stack Overflow
Multiple words in any order using regex - Stack Overflow
Returns true when all parts (divided by , or ' ') of a searchString occur in text. Otherwise false is returned.
filter(text, searchString) {
const regexStr = '(?=.*' + searchString.split(/\,|\s/).join(')(?=.*') + ')';
const searchRegEx = new RegExp(regexStr, 'gi');
return text.match(searchRegEx) !== null;
}
I'm pretty sure you could come up with a regex to do what you want, but it may not be the most efficient approach.
For example, the regex pattern (?=.*bc)(?=.*e)(?=.*a) will match any string that contains bc, e, and a.
var isMatch = 'abcde'.match(/(?=.*bc)(?=.*e)(?=.*a)/) != null; // equals true
var isMatch = 'bcde'.match(/(?=.*bc)(?=.*e)(?=.*a)/) != null; // equals false
You could write a function to dynamically create an expression based on your search terms, but whether it's the best way to accomplish what you are doing is another question.
Hi, Iโm looking for a regular expression to match multiple strings (including a literal asterisk) So far this is what I have got: https://regex101.com/r/bmRaaT/1
Unfortunately itโs not accepting the โ*โ
You can achieve this will lookahead assertions
^(?=.*\bmeat\b)(?=.*\bpasta\b)(?=.*\bdinner\b).+
See it here on Regexr
(?=.*\bmeat\b) is a positive lookahead assertion, that ensures that \bmeat\b is somewhere in the string. Same for the other keywords and the .+ is then actually matching the whole string, but only if the assertions are true.
But it will match also on "dinner meat Foobar pasta"
stema's answer is technically correct, but it doesn't take performance into account at all. Look aheads are extremely slow (in the context of regular expressions, which are lightning fast). Even with the current logic, the regular expression is not optimal.
So here are some measurements, calculated on larger strings which contain all three words, running the search 1000 times and using four different approaches:
stema's regular expression
/^(?=.*\bmeat\b)(?=.*\bpasta\b)(?=.*\bdinner\b).+/
result: 605ms
optimized regular expression
/^(?=.*?\bmeat\b)(?=.*?\bpasta\b)(?=.*?\bdinner\b)/
uses lazy matching and doesn't need the end all selector
result: 291ms
permutation regular expression
/(\bmeat\b.*?(\bpasta\b.*?\bdinner\b|\bdinner\b.*?\bpasta\b)|\bpasta\b.*?(\bmeat\b.*?\bdinner\b|\bdinner\b.*?\bmeat\b)|\bdinner\b.*?(\bpasta\b.*?\bmeat\b|\bmeat\b.*?\bpasta\b))/
result: 56ms
this is fast because the first pattern is matching, if the last pattern matched, it would be even slower than the look ahead one (300 ms)
array of regular expressions
var regs=[/\bmeat\b/,/\bpasta\b/,/\bdinner\b/];
var result = regs.every(reg=>reg.test(text));
result: 26ms
Note that if the strings are crafted to not match, then the results are:
- 521ms
- 220ms
- 161ms - much slower because it has to go through all the branches
- 14ms
As you can see, in all cases just using a loop is an order of magnitude faster, not to mention easier to read.
The original question was asking for a regular expression, so my answer to that is the permutation regular expression, but I would not use it, as its size would grow exponentially with the number of search words.
Also, in most cases this performance issue is academic, but it is necessary to be highlighted.
You can use
(?=.*test)(?=.*long)
Source: MySQL SELECT LIKE or REGEXP to match multiple words in one record
Use a capturing group if you want to extract the matches: (test)|(long)
Then depending on the language in use you can refer to the matched group using $1 and $2, for example.
You forgot to allow for a space between your "words":
\b\D*?\b(?:\s+\b\D*?\b)+
^^^
There are a number of other problems I can see:
I'm also rather suspicious of your definition of "word". Any non-numeric character also includes punctuation and whitespace. That's probably not what you really mean. You might want to try defining word like this instead: [^\d\s]+. This still allows words to contain punctuation, but it disallows both numerals and whitespace.
There is a problem with your usage of word boundaries - if a word can consist of punctuation then words beginning or ending on punctuation won't have a word boundary so your regular expression will miss them.
Are you searching for a string that contains at least two "words", and possibly also some numbers? Or must the string consist only of "words" and no numbers at all anywhere in the string? Currently your regular expression is looking for two consecutive "words" but in general they might not be consecutive.
You can globally search for a "word" and check the length of the .match() if a match is found:.
If two or more words are found, you're good:
var matches = string.match(/\b[^\d\s]+\b/g);
if ( matches && matches.length >= 2 )
{ /* Two or more words ... */ };
You can define a word as \b[^d\s]+\b, which is a word boundary \b, one or more non digits and non whitespaces [^d\s]+, and another word boundary \b. You have to make sure to use the global option g for the regex to find all the possible matches.
You can tweak the definition of a word in your regex. The trick is to make use of the length property of the .match(), but you should not check this property if there are no matches, since it'll break the script, so you must do if (matches && matches.length ...).
Additionally it's quite simple to modify the above code for X words where X is either a number or a variable.
jsFiddle example with your 4 examples
For the situation you're describing, you don't even need regular expressions. If you split the search string on spaces; you can check every one of the words to match is contained within the array of search words.
function matchesAllWords(searchWords, inputString) {
var wordsToMatch = inputString.toLowerCase().split(' ');
return wordsToMatch.every(
word => searchWords.indexOf(word) >= 0);
}
In the snippet below, typing in the input causes a recalculation of the searchWords. The matching li elements are then given the .match class to highlight them.
function updateClasses(e) {
var searchWords = e.target.value.toLowerCase().split(' ');
listItems.forEach(listItem => listItem.classList.remove('match'));
listItems.filter(
listItem =>
matchesAllWords(searchWords, listItem.innerText))
.forEach(
matchingListItem =>
matchingListItem.classList.add('match'));
}
function matchesAllWords(searchWords, inputString) {
var wordsToMatch = inputString.toLowerCase().split(' ');
return wordsToMatch.every(
word => searchWords.indexOf(word) >= 0);
}
function searchProperties(e) {
var searchWords = e.target.value.toLowerCase().split(' ');
for (var property in propertiesToSearch) {
if (matchesAllWords(searchWords, property)) {
console.log(property, propertiesToSearch[property]);
}
}
}
var propertiesToSearch = {
"red apples": 1,
"juicy fruit": 2
};
listItems = [].slice.call(
document.getElementById('matches').querySelectorAll('li')
);
document.getElementById('search').addEventListener('keyup', updateClasses);
document.getElementById('search').addEventListener('keyup', searchProperties);
.match {
color: green;
}
<label for="search">
Search:
</label>
<input type="text" name="search" id="search" />
<ul id="matches">
<li>red apples</li>
<li>juicy fruits</li>
</ul>
Update To use this kind of implementation to search for a property, use a for .. in loop like below. Again, see the snippet for this working in context.
function searchProperties(e) {
var searchWords = e.target.value.toLowerCase().split(' ');
for (var property in propertiesToSearch) {
if (matchesAllWords(searchWords, property)) {
console.log(property, propertiesToSearch[property]);
}
}
}
var propertiesToSearch = {
"red apples": 1,
"juicy fruit": 2
};
I think you might benefit from transforming your data structure from an object literal to an array like so:
const wordGroupings = [
'red apples',
'juicy fruits'
];
I wrote a processString function that accepts a string and some word-groupings as inputs and returns a filtered list of the word-groupings from where each word in the word-grouping occurs in the input string.
In other words let's imagine the test string is:
const testString = 'I like big, frozen whales.';
And let's further imagine that your wordGroupings array looked something like this:
const wordGroupings = [
'whales frozen',
'big frozen whales toucan',
'big frozen',
'sweaty dance club',
'frozen whales big'
]
The output of calling processString(wordGroupings, testString) would be:
[
'whales frozen',
'big frozen',
'frozen whales big'
]
If this is what you're looking for, here is the implementation of processString(..):
function processString(wordGroupings, testString) {
var regexes = wordGroupings.map(words => ({
origString: words,
regex: new RegExp(words.replace(/\s+/g, '|'), 'g'),
expCount: words.split(/\s+/g).length
})
);
filtered = regexes.filter(({regex, expCount}) =>
(testString.match(regex) || []).length === expCount
);
return filtered.map(dataObj => dataObj.origString);
}
Hope this helps!
You can use this regex with optional space matching:
var reg = /\babc ?def ?ghi\b/i;
\b is used for word boundary.
Or you can strip all whitespace beforehand from input and match the string as:
var m = input.replace(/\s+/g, "").match(/\babcdefghi\b/i);
You can use .replace(/\s/g, '').match(/abcdefghi/) If you space is allowed to be anywhere.
If you just want to ignore space in some location, abc *def *ghi is short enough.