#1105 new
William Warby

Feature Request: Enumerable.groupBy

Reported by William Warby | July 28th, 2010 @ 09:33 PM

I would love Prototype to have a groupBy method. "inGroupsOf" and "partition" are okay, but fairly inflexible in terms of the way they group members of the enumerable object. This is how I would see my proposed method working:

var foo = [
    {shape: 'circle',   size: 40},
    {shape: 'circle',   size: 110},
    {shape: 'square',   size: 250},
    {shape: 'square',   size: 320},
    {shape: 'triangle', size: 400}
];

//Use a property name from the enumerable object, kind of like how "invoke" uses a named property:
foo.groupBy('shape'); // --> [[{shape: 'circle', size: 40}, {shape: 'circle', size: 110}], [{shape: 'square', size: 250}, {shape: 'square', size: 320}], [{shape: 'triangle', size: 400}]]

//OR: use a function:
foo.groupBy(function(v) { return v.size < 100 ? 1 : v.size < 200 ? 2 : 3; }); // --> [[{shape: 'circle', size: 40}], [{shape: 'circle', size: 110}], [{shape: 'square', size: 250}, {shape: 'square', size: 320}, {shape: 'triangle',size: 400}]]

I need this method so I'm going to write it myself. I'm not expert in JS optimization but if I get a working version of it I'll post back as a starting point if there is any interest in implementing this as a feature of the Prototype library.

Comments and changes to this ticket

  • William Warby

    William Warby July 28th, 2010 @ 10:25 PM

    Array.prototype.groupBy = function(a) {
        var f = typeof a === 'function' ? a : function(p, v) { return v[p]; }.curry(a), r = [], b;
        for (var x = 0; x < this.length; x++) {
            b = f(this[x]);
            if (!isNaN(parseInt(b))) { b = 'group-' + b; }
            if (!r[b]) { r[b] = []; }
            r[b].push(this[x]);
        };
        return r;
    };
    

    There. I'm sure it's horrific in terms of efficiency and good coding practice, but functionally, it's exactly what I'm proposing - it's good enough for my purposes anyway.

  • KKI

    KKI July 30th, 2010 @ 02:31 AM

    Good idea.

    May I contribute ?

    Array.prototype.groupBy = function(iterator, context) {
        if (!iterator) {
            iterator = Prototype.K;
        } else if (typeof iterator != 'function') {
            iterator = function(p, v, index) { return v[p]; }.curry(iterator);
        }
        var h = $H();
        for (var i = 0, l = this.length; i < l; ++i) {
          k = iterator.call(context, this[i], i);
          if (!h.get(k)) { h.set(k,[]); }
          h.get(k).push(this[i]);
        };
        var r = [];
        // Ordered result
        var keys = $A(h.keys().sort(function(a,b) {
            return isNaN(a) || isNaN(b) ? a > b : parseFloat(a) - parseFloat(b);
        }));
        for (var i = 0, l = keys.length; i < l; ++i) {
          r.push(h.get(keys[i]));
        }
        return r;
      };
    

    Some examples :

      var foo = [
        {shape: 'square',   size: 250},
        {shape: 'circle',   size: 110},
        {shape: 'triangle', size: 250},
        {shape: 'square',   size: 320},
        {shape: 'circle',   size: 40}
        ];
    
        var grpb = foo.groupBy('shape');
        document.write("foo.groupByKKI('shape') -> " + grpb.toJSON() + "<BR/>");
        //-> [[{"shape": "circle", "size": 110}, {"shape": "circle", "size": 40}], [{"shape": "square", "size": 250.1}, {"shape": "square", "size": 320}], [{"shape": "triangle", "size": 250}]]   
    
        var grpb2 = foo.groupBy('size');
        document.write("foo.groupByKKI('size') -> " + grpb2.toJSON() + "<BR/>");
        //-> [[{"shape": "circle", "size": 40}], [{"shape": "circle", "size": 110}], [{"shape": "triangle", "size": 250}], [{"shape": "square", "size": 250.1}], [{"shape": "square", "size": 320}]]   
    
        var grpb3 = foo.groupBy(function(v) { return v.size < 100 ? 1 : v.size < 200 ? 2 : 3; });
        document.write("foo.groupByKKI(function(){...}) -> " + grpb3.toJSON() + "<BR/>");
        //-> [[{"shape": "circle", "size": 40}], [{"shape": "circle", "size": 110}], [{"shape": "square", "size": 250}, {"shape": "square", "size": 320}, {"shape": "triangle", "size": 250}]]
    
  • KKI

    KKI July 30th, 2010 @ 02:33 AM

    If you want an unordered result, replace :

        var r = [];
        // Ordered result
        var keys = $A(h.keys().sort(function(a,b) {
            return isNaN(a) || isNaN(b) ? a > b : parseFloat(a) - parseFloat(b);
        }));
        for (var i = 0, l = keys.length; i < l; ++i) {
          r.push(h.get(keys[i]));
        }
        return r;
    

    by

       return h.values();
    
  • Mad Alex
  • CodeCyan
  • CodeCyan
  • SAHIL SAIFI

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile ยป

The Prototype JavaScript library.

Shared Ticket Bins

People watching this ticket

Pages