Hal_9100 Hal_9100 - 1 month ago 15
Javascript Question

Dynamically match multiple words in a string to another one using regExp

We all know that regExp can match two strings using a common word in both strings as criterion for the match. This code will match "I like apples" to "apples" because they both have "apples" in common.

var str1 = "apples";
var test1 = "I like apples";
var reg1 = new RegExp(str1, "i");

if (test1.match(reg1)) {
console.log(str1 + " is matched");
}


But what if instead of matching the strings using a single common word (or a common part of the strings), I need to match the two strings using multiple common words which may be separated by other words ? Let me show you an example :

test2
= "I like juicy red fruits" should match
str2
= "juicy fruits" because
test2
contains all of
str2
second key words (see object bellow), even though it has other words inbetween.

Tricky part is that I can't know exaclty what one the strings will be, so I have to match them dynamically. The string I don't know is the value of a user input field, and there are many possibilities. This value is to be matched to the strings stored in this object :

var str2 = {
"red apples": "fruits",
"juicy fruits": "price : $10"
};


So whatever the user types in the input field, it must be a match if and only if it contains all the words of one of the object properties. So in this example, "juicy red and ripe fruits" should match the second object property, because it contains all of its keywords.

Edit : My goal is to output the value associated to the strings I'm matching. In my example if the first key is matched, 'fruits' should be output. If 'juicy fruits' is matched, it should output 'price : $10'. Getting the strings associated to the object keys is the reason why the user has to search for them using the input.

Is it possible to do this with regExp using pure javascript ?

Here is what I (poorly) tried to do : https://jsfiddle.net/Hal_9100/fzhr0t9q/1/

Answer

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