3

The emphasis here is on the word exactly. This needs to work for any number of permutations, so hopefully my example is clear enough.

Given a string of random letters, is it possible (using RegEx) to match an exact number of letters within the given string?

So if I have a string (str1) containing letters ABZBABJDCDAZ and I wanted to match the letters JDBBAA (str2), my function should return true because str1 contains all the right letters enough times. If however str1 were to be changed to ABAJDCDA, then the function would return false as str2 requires that str1 have at least 2 instances of the letter B.

This is what I have so far using a range:

const findLetters = (str1, str2) => {
  const regex = new RegExp(`[${str2}]`, 'g')
  const result = (str1.match(regex))
  console.log(result)
}

findLetters('ABZBABJDCDAZ', 'JDBBAA')

As you can see it matches the right letters, but it matches all instances of them. Is there any way to do what I'm trying to do using RegEx? The reason I'm focusing on RegEx here is because I need this code to be highly optimised, and so far my other functions using Array.every() and indexOf() are just too slow.

Note: My function only requires to return a true/false value.

Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
Joss Classey
  • 1,054
  • 1
  • 7
  • 22
  • You'd probably need to put a lot of [lookaheads](https://www.regular-expressions.info/lookaround.html): [`^(?=[^J]*J)(?=[^D]*D)`...](https://regex101.com/r/WovUjc/1) – bobble bubble Oct 24 '19 at 10:54
  • @bobblebubble Thank you for your comment. I did think of this, but considering `str2` could be 1000 characters long or more, a very large RegEx pattern would have to be generated, which probably would slow the whole thing down a lot. Can you think of a very fast way to achieve what I'm trying to achieve without RegEx? I'm really scratching my head on this one! – Joss Classey Oct 24 '19 at 10:58
  • 1
    I like the idea of Kamil's answer. – bobble bubble Oct 24 '19 at 11:06

2 Answers2

5

Try (here we sort letters of both strings and then create regexp like A.*A.*B.*B.*D.*J)

const findLetters = (str1, str2) => {
  const regex = new RegExp([...str2].sort().join`.*`)
  return regex.test([...str1].sort().join``)
}

console.log( findLetters('ABZBABJDCDAZ', 'JDBBAA') );
console.log( findLetters('ABAJDCDA', 'JDBBAA') );
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
2

I dont know if regex is the right way for this as this can also get very expensive. Regex is fast, but not always the fastest.

const findLetters2 = (strSearchIn, strSearchFor) => {
  var strSearchInSorted = strSearchIn.split('').sort(function(a, b) {
    return a.localeCompare(b);
  });
  var strSearchForSorted = strSearchFor.split('').sort(function(a, b) {
    return a.localeCompare(b);
  });

  return hasAllChars(strSearchInSorted, strSearchForSorted);
}

const hasAllChars = (searchInCharList, searchCharList) => {
  var counter = 0;
  for (i = 0; i < searchCharList.length; i++) {
    var found = false;
    for (counter; counter < searchInCharList.length;) {
      counter++;
      if (searchCharList[i] == searchInCharList[counter - 1]) {
        found = true;
        break;
      }
    }
    if (found == false) return false;
  }
  return true;
}

// No-Regex solution
console.log('true: ' + findLetters2('abcABC', 'abcABC'));
console.log('true: ' + findLetters2('abcABC', 'acbACB'));
console.log('true: ' + findLetters2('abcABCx', 'acbACB'));
console.log('false: ' + findLetters2('abcABC', 'acbACBx'));
console.log('true: ' + findLetters2('ahfffmbbbertwcAtzrBCasdf', 'acbACB'));
console.log('false: ' + findLetters2('abcABC', 'acbAACB'));

Feel free to test it's speed and to optimize it as I'm no js expert. This solution should iterate each string once after sorting. Sorting is thanks to https://stackoverflow.com/a/51169/9338645.

Chrᴉz remembers Monica
  • 1,829
  • 1
  • 10
  • 24