5

I have a string with a known beginning and end, but I want to match only the unknown center.

For example, say you knew you were going to have strings which said "I had ________ for lunch today" and you only wanted to match the blank.

Here's what I have tried:

^I had (.*) for lunch today$

Which matches the entire string, and also the group, which is the blank.

So when given "I had pizza for lunch today" it produces two matches:
"I had pizza for lunch today" and "pizza"

Is there any way to only match the blank? Is there any way to just get "pizza"? Or at least get "pizza" as the first match?

MMAdams
  • 1,508
  • 15
  • 29
  • 1
    "^I had (.*) for lunch today$" matches both the whole string and captures everything within ( and ). Why do you need it as a first match? Wouldn't it be sufficient to always access matches[1] ? – Paul Mar 14 '18 at 14:39
  • Are you doing a highlighting on page or it's a simple replace process? – revo Mar 14 '18 at 14:44
  • @revo This is just an example. I need to access the word(s) for something, say, making an array of lunch items ["pizza","ham sandwich","ceasar salad wrap",...] – MMAdams Mar 14 '18 at 14:48
  • 1
    So you only need to access to capturing group 1 which you constructed already. – revo Mar 14 '18 at 14:49

3 Answers3

4

You constructed a capturing group; use it.

When you create a regular expression with something wrapped in parentheses, in your case (.*?), you have created a capturing group. Whatever is matched by capturing groups will be returned alongside the full match.

String.prototype.match() returns an array. Its first element is the full match. All subsequent elements are captured results. Since you only have one capturing group, the result is matches[1].

var sentences = [
    "I had pizza for lunch today",
    "I had hamburger for lunch today",
    "I had coconut for lunch today",
    "I had nothing for lunch today"
];

sentences.map(function(sentence) {
   console.log(sentence + " -> " + sentence.match(/^I had (.*?) for lunch today$/)[1])
})
MultiplyByZer0
  • 6,302
  • 3
  • 32
  • 48
revo
  • 47,783
  • 14
  • 74
  • 117
  • 1
    I did end up doing something similar to this, just keeping the regex I had, so I am accepting this answer. Also, nice code snippet! – MMAdams Mar 16 '18 at 18:09
2
(?<=^I had )(.*?)(?= for lunch today$)

(?<=) and (?=) are described here: what is the difference between ?:, ?! and ?= in regex?

=>

(?<=^I had ): It starts with "I had " but this is not captured.
(?= for lunch today$): It ends with " for lunch today" but this is not captured

=>

/(?<=^I had )(.*?)(?= for lunch today$)/_/ 

should work

Or like that, if positive look behind is not supported:

/(^I had )(.*?)(?= for lunch today$)/$1_/

=> I had _ for lunch today

D. Braun
  • 508
  • 1
  • 4
  • 11
  • 3
    Lookbehinds are not supported by all major browsers yet. – revo Mar 14 '18 at 14:49
  • While this answers my question about how to not include the beginning and end of the string, because I have to support several different browsers I ended up keeping my original code. – MMAdams Mar 16 '18 at 18:10
0

It doesn't use RegEx and isn't the prettiest, but this works perfectly assuming that you know the words that wrap around the string you are trying to find.

const testString = 'I had pizza for lunch today';

const getUnknown = (str, startWord, endWord) => str.split(startWord)[1]
  .split(endWord)[0]
  .trim();

console.log(getUnknown(testString, 'had', 'for'));
th3n3wguy
  • 3,649
  • 2
  • 23
  • 30