0

This is hard to explain so I'll just show an example. I have an array of specific keywords, like so:

const keywordArr = ['jim', 'john'];

then I have a string:

const message = 'Hello john, how are you doing today?'

I would like to check if message contains a value from keywordArr. I know I could loop through each value in keywordArr and check like this:

keywordArr.forEach(function(word) {
    if (message.toLowerCase().includes(word)) {
         rest of code here..
    }
}

However, I get around 5 messages each second, so that method would be very performance consuming. Is there any efficient way to do this?

Behrooz
  • 2,181
  • 1
  • 16
  • 24
  • You're either going to loop the `keywordArr` or `message` multiple times. Perhaps run some performance tests for your use-case? –  Apr 03 '18 at 17:45
  • You can try regular expressions. – Harshal Patil Apr 03 '18 at 17:52
  • you should check this first: [Multiple Pattern String Matching Algorithm](https://www.researchgate.net/publication/314191170_Single_and_Multiple_Pattern_String_Matching_Algorithm), if the min length of keywords are long, I prefer to using WM. – Sphinx Apr 03 '18 at 17:54
  • If performance is all you care about and it is like to top most priority then I would suggest looking at some algorithms like these [Knuth-Morris-Pratt](https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm). and [Boyer–Moore](https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm) compare them (possibly with regular expression as well) and choose accordingly. – Matus Dubrava Apr 03 '18 at 17:54
  • I don't think that 5 messages per second warrants hyper-performant code. Though that certainly depends on how long your `message` is and how many keywords you have. – pushkin Apr 03 '18 at 17:56
  • @pushkin 5 messages per second probably not, but this can get pretty nasty pretty quickly. – Matus Dubrava Apr 03 '18 at 17:57
  • @MatusDubrava, BM and KMP is single pattern algorithm, multiple patterns should be WM or AC etc. – Sphinx Apr 03 '18 at 17:58
  • I found something interesting, [PatternMatchingAlgorithms in Github](https://github.com/foo123/PatternMatchingAlgorithms) – Sphinx Apr 03 '18 at 18:02

3 Answers3

2

You can use forEach() for array and match() to check string contain or not.

DEMO

const keywordArr = ['jim', 'john'],
  message = 'Hello john, how are you doing today?';

keywordArr.forEach(v => {
  if (message.match(v)) console.log(`message contain ${v}`);
});
Narendra Jadhav
  • 10,052
  • 15
  • 33
  • 44
  • As I said before, I wouldn't want to forEach through the array everytime as I get 5 messages a second-- and everytime the message is received, the code runs again. However, what I like about this is that it shows which word is in the array (in this case is `john`). Is there any other way to do this more efficiently? – SpecialVirusTasker Apr 03 '18 at 19:43
  • For other way you can aslo use [`RegExp.prototype.test()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test). Example like this `RegExp(['jim', 'john'].join("|")).test('Hello john, how are you doing today?')` – Narendra Jadhav Apr 04 '18 at 06:14
1

Check if the array contains any of the words using RegExp.test() (regex101 example).

const keywordArr = ['jim', 'john'];

const message = 'Hello john, how are you doing today?'

const pattern = new RegExp(`\\b${keywordArr.join('|')}\\b`, 'gi');
const contains = pattern.test(message);

console.log(contains);
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • This works great iif none of the keywords contain any regex special characters. (And if the list if keywords isn't very long.) – Jordan Running Apr 03 '18 at 18:02
  • And you can always [escape the keywords](https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex). – Ori Drori Apr 03 '18 at 18:06
1

Return true/false

We can do this using reduce and includes, if your not into the whole RegExp thing:

const keywordArr = ['jim', 'john'];

const message = 'Hello john, how are you doing today?'

let has = keywordArr.reduce((r,v) => message.toLowerCase().includes(v.toLowerCase()) || r, false)

console.log(has)

This will match with case insensitivity. Remove both of the toLowerCaser() methods if you want a case sensitive match.

Return a name

We can modify the reduce function by adding && v which will return the value instead of a true/false value. We also will modify the starting value from false to an empty string.

const keywordArr = ['jim', 'john'];

const message = 'Hello john, how are you doing today?'

let name = keywordArr.reduce((r,v) => message.toLowerCase().includes(v.toLowerCase()) && v || r, '')

console.log(name)

Return an array of names

If we want to return a list of all names within the string, we can replace && v with && r.concat(v) then replace the starting value from an empty string to an empty array.

const keywordArr = ['jim', 'john', 'paul'];

const message = 'Hello john, how are you doing today? -- jim'

let names = keywordArr.reduce((r,v) => message.toLowerCase().includes(v.toLowerCase()) && r.concat(v) || r, [])

console.log(names)
Get Off My Lawn
  • 34,175
  • 38
  • 176
  • 338
  • Calling `toLowerCase()` on the same `message` in every iteration isn't doing you any favors in the performance department. – Jordan Running Apr 03 '18 at 18:02
  • It could be put on the main message, but if it needs to be used somewhere else as-is, you then lose your casing. Creating a temp var would solve that though. – Get Off My Lawn Apr 03 '18 at 18:04
  • This method works great, is there a way to get **which** word is in the sentence? Which in this case would be `john`, how can I let the code know that? – SpecialVirusTasker Apr 03 '18 at 19:44
  • I have updated the answer with a second example that returns the string instead of a `true/false` value – Get Off My Lawn Apr 03 '18 at 19:49