Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.javascript > #21077 > unrolled thread

Recommendation for new RegExp (Array-extra-like) methods

Started bybrettz9@gmail.com
First post2013-08-28 07:51 -0700
Last post2013-09-01 15:25 +0200
Articles 6 — 3 participants

Back to article view | Back to comp.lang.javascript


Contents

  Recommendation for new RegExp (Array-extra-like) methods brettz9@gmail.com - 2013-08-28 07:51 -0700
    Re: Recommendation for new RegExp (Array-extra-like) methods Andreas Bergmaier <andber93@web.de> - 2013-08-28 17:16 +0200
      Re: Recommendation for new RegExp (Array-extra-like) methods brettz9@gmail.com - 2013-08-28 08:21 -0700
        Recommendation for array-like methods on Iterator; Was Re: Recommendation for new RegExp (Array-extra-like) methods brettz9@gmail.com - 2013-08-28 16:30 -0700
    Re: Recommendation for new RegExp (Array-extra-like) methods Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2013-08-30 16:00 +0200
      Re: Recommendation for new RegExp (Array-extra-like) methods Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2013-09-01 15:25 +0200

#21077 — Recommendation for new RegExp (Array-extra-like) methods

Frombrettz9@gmail.com
Date2013-08-28 07:51 -0700
SubjectRecommendation for new RegExp (Array-extra-like) methods
Message-ID<ac07e89f-90ee-4b6d-a524-5adfadd0cb94@googlegroups.com>
Hi,

I'd be interested to see a way to get over the requirement for writing patterns like this:

    while ((matches = regex.exec(str)) !== null) {

I envisioned solving this through an API that could work like this:

(/^(.*?): (.*)$/m).forEach('key1: val1\nkey2: key2', function (key, val, i, n0) {
    document.writeln('key is ' + key + ', value is ' + val + ', and the iteration count is ' + i + '<br>');
});

I have this and other such Array-extra-like methods added to RegExp.prototype at https://gist.github.com/brettz9/6366721 (live at http://brett-zamir.me/tests/proposal-regexp-extensions.html ) as a kind of proof-of-concept.

Might something like this that makes global exec iteration more functional be possible as a new addition to the next ECMAScript standard?

[toc] | [next] | [standalone]


#21078

FromAndreas Bergmaier <andber93@web.de>
Date2013-08-28 17:16 +0200
Message-ID<kvl485$rse$1@news.albasani.net>
In reply to#21077
brettz9@gmail.com schrieb:

> I have Array-extra-like methods added to RegExp.prototype at
> https://gist.github.com/brettz9/6366721

I don't see much reason to do so. The method names imply that you are 
iterating over the regex object, but that's not what you're doing.

To me it would make much more sense to have a simple method like

RegExp.prototype.execAll = function execAll(str) {
     var res = [],
         regex = this.global ? this : new RegExp(this, "g"),
         matches;
     while (matches = regex.exec(str))
         res.push(matches);
     return res;
};
String.prototype.matchAll = function(reg) { return reg.execAll(this); };

that you can call on a regex and then use arbitrary (actual) Array 
methods on the result.

Greetings,
  Bergi

[toc] | [prev] | [next] | [standalone]


#21080

Frombrettz9@gmail.com
Date2013-08-28 08:21 -0700
Message-ID<4e7f201e-b614-4d58-a48e-0e0a11560aba@googlegroups.com>
In reply to#21078
On Wednesday, August 28, 2013 11:16:25 PM UTC+8, Andreas Bergmaier wrote:
> brettz9@gmail.com schrieb:
> 
> 
> 
> > I have Array-extra-like methods added to RegExp.prototype at
> 
> > https://gist.github.com/brettz9/6366721
> 
> 
> 
> I don't see much reason to do so. The method names imply that you are 
> 
> iterating over the regex object, but that's not what you're doing.
> 
> 
> 
> To me it would make much more sense to have a simple method like
> 
> 
> 
> RegExp.prototype.execAll = function execAll(str) {
> 
>      var res = [],
> 
>          regex = this.global ? this : new RegExp(this, "g"),
> 
>          matches;
> 
>      while (matches = regex.exec(str))
> 
>          res.push(matches);
> 
>      return res;
> 
> };
> 
> String.prototype.matchAll = function(reg) { return reg.execAll(this); };
> 
> 
> 
> that you can call on a regex and then use arbitrary (actual) Array 
> 
> methods on the result.
> 
> 
> 
> Greetings,
> 
>   Bergi

I see your point on the semantics of it, but with your approach it is not possible to abandon the iteration early--for example, as with "some()". Still, even having that limited functionality built in would be better than nothing.

[toc] | [prev] | [next] | [standalone]


#21090 — Recommendation for array-like methods on Iterator; Was Re: Recommendation for new RegExp (Array-extra-like) methods

Frombrettz9@gmail.com
Date2013-08-28 16:30 -0700
SubjectRecommendation for array-like methods on Iterator; Was Re: Recommendation for new RegExp (Array-extra-like) methods
Message-ID<abfb1b68-f762-46e2-9e27-06584069c788@googlegroups.com>
In reply to#21080
On Wednesday, August 28, 2013 11:21:55 PM UTC+8, bre...@gmail.com wrote:
> On Wednesday, August 28, 2013 11:16:25 PM UTC+8, Andreas Bergmaier wrote:
> 
> > brettz9@gmail.com schrieb:
> 
> > 
> 
> > 
> 
> > 
> 
> > > I have Array-extra-like methods added to RegExp.prototype at
> 
> > 
> 
> > > https://gist.github.com/brettz9/6366721
> 
> > 
> 
> > 
> 
> > 
> 
> > I don't see much reason to do so. The method names imply that you are 
> 
> > 
> 
> > iterating over the regex object, but that's not what you're doing.
> 
> > 
> 
> > 
> 
> > 
> 
> > To me it would make much more sense to have a simple method like
> 
> > 
> 
> > 
> 
> > 
> 
> > RegExp.prototype.execAll = function execAll(str) {
> 
> > 
> 
> >      var res = [],
> 
> > 
> 
> >          regex = this.global ? this : new RegExp(this, "g"),
> 
> > 
> 
> >          matches;
> 
> > 
> 
> >      while (matches = regex.exec(str))
> 
> > 
> 
> >          res.push(matches);
> 
> > 
> 
> >      return res;
> 
> > 
> 
> > };
> 
> > 
> 
> > String.prototype.matchAll = function(reg) { return reg.execAll(this); };
> 
> > 
> 
> > 
> 
> > 
> 
> > that you can call on a regex and then use arbitrary (actual) Array 
> 
> > 
> 
> > methods on the result.
> 
> > 
> 
> > 
> 
> > 
> 
> > Greetings,
> 
> > 
> 
> >   Bergi
> 
> 
> 
> I see your point on the semantics of it, but with your approach it is not possible to abandon the iteration early--for example, as with "some()". Still, even having that limited functionality built in would be better than nothing.

After considering the points you raised, I realized that I was really looking for the general ability to iterate in a functional manner (i.e., something like a "Object.forEach" but available on all iterators--or perhaps a subclass of iterators designated for those which can be treated like arrays).

    var obj = {'0': '0', a:3, b: 'b'};

    Iterator(obj).some(function (key, val, i) {
        return key === val;
    }); // true

    Iterator(obj, true).some(function (key, i) {
        return parseInt(key, 10) === i;
    }); // true

And then, the functional wrapping of the...

    while ((matches = regex.exec(str)) !== null) {

...ugliness could be achieved through the likes of a class method (again, my hope would be built-in):

    RegExp.iterator(/^(.*?): (.*)$/gm, 'key1: val1\nkey2: key2').some(function (n0, key, val, i) {
        return key === val;
    }); // true

A JavaScript 1.8 (Firefox) concept demo: https://gist.github.com/brettz9/6372591 (live at http://brett-zamir.me/tests/proposal-iterator-methods.html )

[toc] | [prev] | [next] | [standalone]


#21115

FromThomas 'PointedEars' Lahn <PointedEars@web.de>
Date2013-08-30 16:00 +0200
Message-ID<3573991.qt74eh6WeI@PointedEars.de>
In reply to#21077
brettz9@gmail.com wrote:

> I'd be interested to see a way to get over the requirement for writing
> patterns like this:
> 
>     while ((matches = regex.exec(str)) !== null) {

Why, and what is the use-case for your proposal?
 
> I envisioned solving this through an API that could work like this:
> 
> (/^(.*?): (.*)$/m).forEach('key1: val1\nkey2: key2',

Whereas your RegExp.prototype.forEach() is:

| function _mixinRegex (regex, newFlags, newLastIndex) {
|         newLastIndex = newLastIndex === undefined ? regex.lastIndex : 
| newLastIndex;

“undefined” is not read-only in less recent implementations.  Use the 
“typeof” operator instead.

|         newFlags = newFlags || '';

See below.

|         regex = new RegExp(
|             regex.source,
|             (newFlags.indexOf('g') > -1 ? 'g' : regex.global ? 'g' : '') +
|                 (newFlags.indexOf('i') > -1 ? 'i' : regex.ignoreCase ? 'i' 
| : '') +
|                 (newFlags.indexOf('m') > -1 ? 'm' : regex.multiline ? 'm' 
| : '') +
|                 (newFlags.indexOf('y') > -1 ? 'y' : regex.sticky ? 'y' : 
| '') // Non-standard but harmless if already being used
|         );
|         regex.lastIndex = newLastIndex;
|         return regex;
|     }
| 
| RegExp.prototype.forEach = function (str, cb, thisObj) {
|     var matches, n0, i = 0, regex = _mixinRegex(this, 'g');
|     thisObj = thisObj || null;

Should be

  if (typeof thisObj == "undefined")
  {
    thisObj = null;
  }

|     while ((matches = regex.exec(str)) !== null) {
|         n0 = matches.splice(0, 1);
|         cb.apply(thisObj, matches.concat(i++, n0));
|     }
| };

As you are wrapping RegExp.prototype.exec(), you have to create a new RegExp 
instance with _mixinRegx() in order to prevent this from running forever.  
Because you cannot rely on the global modifier being set on the “this” 
value, and you cannot set it afterwards (the “global” property is read-only, 
a poor design choice in ES, IMHO).  So on *each* call of your 
RegExp.prototype.forEach() there is yet another object created, and all the 
property fixes required then are performed, needlessly.

| function (key, val, i, n0) {
|     document.writeln('key is ' + key + ', value is ' + val + ', and the
|     iteration count is ' + i + '<br>');
| });
| 
| I have this and other such Array-extra-like methods added to
| RegExp.prototype at https://gist.github.com/brettz9/6366721 (live at
| http://brett-zamir.me/tests/proposal-regexp-extensions.html ) as a kind of
| proof-of-concept.

This is _not_ the Web.  It would have been better had you also posted some 
code here.
 
> Might something like this that makes global exec iteration more functional
> be possible as a new addition to the next ECMAScript standard?

Possible, yes; likely, no.  Consider that function calls are among the most 
expensive constructs, and there is no reason why a second RegExp instance is 
necessary here other than syntactic sugar.  [I think introducing 
Array.prototype.forEach() was a mistake to begin with.  It would have been 
better to adopt E4X's “for each”.  But when you have John Resig of all 
people as “JavaScript Evangelist” at Mozilla, what do you expect?  jQuery is 
strewn with inefficiencies like this, and so is all jQuery-based code.]

You might instead find my jsx.regexp.RegExp useful, which among other 
features allows you to name subpatterns and access by that name the 
corresponding matches:

  /* ?<Perl> style; also supports ?'Perl' style and ?P<ython> style */
  var regex = new jsx.regexp.RegExp("^(?<first>.+?): (?<second>.+)$", "mg"),
      str = "key1: val1\nkey2: key2",
      match;

  while ((match = regex.exec(str)))
  {
    console.log(match.groups["first"], match.groups["second"]);
    console.log(match[1], match[2]);
  }

“regex” holds an augmented RegExp instance then, whose inherited exec() 
method has been overridden in the instance to return an augmented array-like 
object if necessary (otherwise just the inherited method is called, and its 
return value returned):

      /**
       * @function
       */
      exec: (function () {
        var rx2;

        /**
         * @param {string} s
         * @param {jsx.regexp.RegExp} rx
         *   Instance to be tested
         * @return {boolean}
         *   <code>true</code> if <var>rx</var> has been constructed
         *   using this constructor, <code>false</code> otherwise.
         */
        function _exec (s, rx)
        {
          /* NOTE: Use passed expression only when called statically */
          if (_getClass(this) == "RegExp")
          {
            rx = this;
          }

          rx._realExec = (rx._oldExec || rx.exec);

          var matches = rx._realExec(s);

          if (matches && _RegExp2.isInstance(rx))
          {
            matches.groups = _getDataObject();

            if (rx.unicodeMode && !rx2)
            {
              rx2 = new _RegExp2("^\\W+", "u");
            }

            for (var i = 0, len = matches.length; i < len; ++i)
            {
              /* Trim leading \b matches */
              var patternGroup = rx._patternGroups[i];
              if (rx.unicodeMode
                  && patternGroup
                  && patternGroup.match(
                       /^(\((\?P?(<([^>]+)>|'([^']+)'))?)*\\b/))
              {
                matches[i] = matches[i].replace(rx2, "");
              }

              matches.groups[rx.groups[i] || i] = matches[i];
            }
          }

          return matches;
        }

        return _exec;
      }()),

See <http://PointedEars.de/scripts/test/regexp> for more.

-- 
PointedEars

Twitter: @PointedEars2
Please do not Cc: me. / Bitte keine Kopien per E-Mail.

[toc] | [prev] | [next] | [standalone]


#21138

FromThomas 'PointedEars' Lahn <PointedEars@web.de>
Date2013-09-01 15:25 +0200
Message-ID<1458401.hGLh0OfnHy@PointedEars.de>
In reply to#21115
Thomas 'PointedEars' Lahn wrote:

> You might instead find my jsx.regexp.RegExp useful, which among other
> features allows you to name subpatterns and access by that name the
> corresponding matches:
> 
>   /* ?<Perl> style; also supports ?'Perl' style and ?P<ython> style */
>   var regex = new jsx.regexp.RegExp("^(?<first>.+?): (?<second>.+)$",
>   "mg"),
>       str = "key1: val1\nkey2: key2",
>       match;
> 
>   while ((match = regex.exec(str)))
>   {
>     console.log(match.groups["first"], match.groups["second"]);
>     console.log(match[1], match[2]);
>   }
> 
> “regex” holds an augmented RegExp instance then, whose inherited exec()
> method has been overridden in the instance to return an augmented
> array-like object if necessary (otherwise just the inherited method is
> called, and its return value returned):
> 
>       /**
>        * @function
>        */
>       exec: (function () {
>         var rx2;
> 
>         /**
>          * @param {string} s
>          * @param {jsx.regexp.RegExp} rx
>          *   Instance to be tested
>          * @return {boolean}
                     ^^^^^^^^^
>          *   <code>true</code> if <var>rx</var> has been constructed
>          *   using this constructor, <code>false</code> otherwise.

JFTR: As you can see further below, this is a documentation bug that will be 
fixed in the next revision.  The return value of the custom method is 
exactly what the original RegExp.prototype.exec() method would have had 
returned (“null” or a reference to an Array-like object), augmented with 
custom properties if there is a match.

-- 
PointedEars

Twitter: @PointedEars2
Please do not Cc: me. / Bitte keine Kopien per E-Mail.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.javascript


csiph-web