2

I want to detect job and jobs in a piece of text. So The jobs report came out and it was a great job! should match job twice and jobs once.

If I do:

const res = str.matchAll(/job|jobs/gi)

Then it matches jobs once and job once. How can I modify it to do what I want?

UPDATE Ultimately, I have a lot more like /job|jobs|get\sa\sjob/

5
  • What is the exact output you want here? Do you want an array with [job, jobs, job]? Something else? Commented Oct 8, 2020 at 16:09
  • Ideally, the array would be perfect Commented Oct 8, 2020 at 16:11
  • Seems like the simplest thing to do is add the jobs count to the job count after the matching is finished. Commented Oct 8, 2020 at 16:21
  • str.match(/jobs?/gi).flatMap(m => m.endsWith('s') ? [m, m.slice(0, -1)] : m) ? Commented Oct 8, 2020 at 16:23
  • ... how about ... [...str.match(/jobs/gi), ...str.match(/job/gi)]? Commented Oct 8, 2020 at 17:52

3 Answers 3

2

The most naive approach for achieving the OP's result was to just concat each match(ing) array of two separate regular expressions (here, the singular and the plural variant of 'job') ...

const text = 'The jobs report came out and it was a great JOB!';

console.log(
  text.match(/jobs/gi).concat(text.match(/job/gi))
);
console.log(
  [...text.match(/jobs/gi), ...text.match(/job/gi)]
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

If one also wants to take into account word boundaries, as done by other answers, one has to change e.g. the second example of the above first approach to ...

const text = 'The jobs report came out and it was a great NUTJOB of a JOB!';

// does not take word boundaries into account ...
console.log(
  [...text.match(/jobs/gi), ...text.match(/job/gi)]
);

// ... changed in order to support word boundaries.
console.log([
  ...text.match(/\bjobs\b/gi),
  ...[...text.matchAll(/\b(job)s?\b/gi)].map(([match, $1]) => $1)
]);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Sign up to request clarification or add additional context in comments.

Comments

1

I am not a JavaScript person, and I couldn't get matchAll to work in a snippet here, nor in any other demo site. So, I used an old-fashioned regex iterator.

For one solution, we can try matching on the following regex pattern:

\bjob(?=s|\b)|(?<=\bjob)s\b

This will match job, when it appears either as a standalone word, or when it appears as a substring of jobs. The above alternation also matches the letter s when preceded by job. Then, as we iterate, we replace the s match with jobs, to get the final result.

var input = "The jobs report came out and it was a great job!";
var matches = [];
var counter = 0;
var re = /\bjob(?=s|\b)|(?<=\bjob)s\b/g;
var m;

do {
    m = re.exec(input);
    if (m) {
        matches[counter++] = m[0] === "job" ? "job" : "jobs";
    }
} while (m);

console.log(matches);

Comments

1

You could also match either job or jobs by making the s optional.

Then afterwards loop the array, and if the string equals jobs, then add another job.

let str = "The jobs report came out and it was a great job!";
const res = str.match(/\bjobs?\b/gi);
res.forEach(s => {
  if (s.toLowerCase() === "jobs") {
    res.push("job");
  }
});
console.log(res);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.