views:

143

answers:

5
text = '#container a.filter(.top).filter(.bottom).filter(.middle)';

regex = /(.*?)\.filter\((.*?)\)/;

matches = text.match(regex);

log(matches);
// matches[1] is '#container a'
//matchss[2] is '.top'

I expect to capture

matches[1] is '#container a'
matches[2] is '.top'
matches[3] is '.bottom'
matches[4] is '.middle'

One solution would be to split the string into #container a and rest. Then take rest and execute recursive exec to get item inside ().

Update: I am posting a solution that does work. However I am looking for a better solution. Don't really like the idea of splitting the string and then processing Here is a solution that works.

matches = [];

var text = '#container a.filter(.top).filter(.bottom).filter(.middle)';
var regex = /(.*?)\.filter\((.*?)\)/;
var match = regex.exec(text);
firstPart = text.substring(match.index,match[1].length);
rest = text.substring(matchLength, text.length);

matches.push(firstPart);

regex = /\.filter\((.*?)\)/g;
while ((match = regex.exec(rest)) != null) {
  matches.push(match[1]);
}
log(matches);

Looking for a better solution.

A: 

You need to do several matches repeatedly, starting where the last match ends (see while example at https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/exec):

If your regular expression uses the "g" flag, you can use the exec method multiple times to find successive matches in the same string. When you do so, the search starts at the substring of str specified by the regular expression's lastIndex property. For example, assume you have this script:

var myRe = /ab*/g;
var str = "abbcdefabh";
var myArray;
while ((myArray = myRe.exec(str)) != null)
{
  var msg = "Found " + myArray[0] + ".  ";
  msg += "Next match starts at " + myRe.lastIndex;
  print(msg);
}

This script displays the following text:

Found abb. Next match starts at 3
Found ab. Next match starts at 9

However, this case would be better solved using a custom-built parser. Regular expressions are not an effective solution to this problem, if you ask me.

Blixt
I posted a solution that works. However I am looking for a better solution.
Neeraj Singh
+3  A: 

You have to iterate, I think.

var head, filters = [];
text.replace(/^([^.]*)(\..*)$/, function(_, h, rem) {
  head = h;
  rem.replace(/\.filter\(([^)]*)\)/g, function(_, f) {
    filters.push(f);
  });
});
console.log("head: " + head + " filters: " + filters);

The ability to use functions as the second argument to String.replace is one of my favorite things about Javascript :-)

Pointy
very nicely done. Love it. Never saw usage of replace in regex context. Thank you.It is essentially same thing as I did. However this code shows it is code of a pro.
Neeraj Singh
thank you sir :-)
Pointy
+5  A: 

This will match the single example you posted:

<html>
  <body>
    <script type="text/javascript">
      text = '#container a.filter(.top).filter(.bottom).filter(.middle)';
      matches = text.match(/^[^.]*|\.[^.)]*(?=\))/g);
      document.write(matches);
    </script>
  </body>
</html>

which produces:

#container a,.top,.bottom,.middle 

EDIT

Here's a short explanation:

^         # match the beginning of the input
[^.]*     # match any character other than '.' and repeat it zero or more times
          #
|         # OR
          #
\.        # match the character '.'
[^.)]*    # match any character other than '.' and ')' and repeat it zero or more times
(?=       # start positive look ahead
  \)      #   match the character ')'
)         # end positive look ahead

EDIT part II

The regex looks for two types of character sequences:

  1. one ore more characters starting from the start of the string up to the first ., the regex: ^[^.]*
  2. or it matches a character sequence starting with a . followed by zero or more characters other than . and ), \.[^.)]*, but must have a ) ahead of it: (?=\)). This last requirement causes .filter not to match.
Bart Kiers
very nicely done. I will have to read a regex book to understand look-ahead
Neeraj Singh
I don't get this code. Can someone explain?
Nadal
@Raj, @dorelal, see my edit. And thank you Raj.
Bart Kiers
thank you Bark. I still do not see where filter word is getting rejected and where you are sepcifying to capture everything within (). I guess I have to dig deeper into positive look ahead. Thank you this is the best answer and ,I guess, I would be a better regexeer when I understand your code. Again thank you.
Nadal
@dorelal, you're welcome, and see my edit part II.
Bart Kiers
ust have a ) ahead of it . That did it. Thanks
Nadal
A: 
var text = '#container a.filter(.top).filter(.bottom).filter(.middle)';
var result = text.split('.filter');

console.log(result[0]);
console.log(result[1]);
console.log(result[2]);
console.log(result[3]);
Darin Dimitrov
instead of .top you will get (.top)
Neeraj Singh
`result[0].replace(/\(|\)/g, '')`
Darin Dimitrov
A: 

text.split() with regex does the trick.

var text = '#container a.filter(.top).filter(.bottom).filter(.middle)';
var parts = text.split(/(\.[^.()]+)/);
var matches = [parts[0]];

for (var i = 3; i < parts.length; i += 4) {
    matches.push(parts[i]);
}

console.log(matches);
Imran