Implementing Prototype’s Array Methods in jQuery

read 11 comments

One of the biggest concerns I've heard mentioned from users of the Prototype library about jQuery is the lack of support for various array methods. The robust features Prototype provides for arrays is of great benefit to developers that do a lot of array manipulation in their JavaScript.

However, I find that after moving to jQuery, I do less array manipulation than I had done with Prototype. Perhaps jQuery has altered my development pattern so I no longer need array manipulation, or perhaps I have shifted most of my data manipulation to the server. Whatever the case, I have only on occasion missed the Prototype array methods.

While there is some overlap in the ways that jQuery and Prototype handle array manipulation, jQuery does a few things Prototype doesn't do, and Prototype does a number of things that jQuery doesn't. I began writing an article about these differences, but soon got side-tracked writing a jQuery plugin to mimic the array methods Prototype provides.

You can find the plugin at http://code.google.com/p/jquery-protify-js/

With this plugin, you can give a particular array all of the methods that Prototype adds to their Array and Enumerable objects. You can use the methods two different ways. The first does not extend the original array:

JavaScript:
  1. var arr = [1,2,3,4,5,6];
  2. var protArray = $.protify(arr);

This will return an array extended with the Prototype library's methods, but leave the original array untouched.

The second extends the original array by passing in true as the second parameter:

JavaScript:
  1. var arr = [1,2,3,4,5,6];
  2. $.protify(arr, true);

In either case, the JavaScript Array prototype is untouched. When you create new arrays, it will not have the new methods. This way of writing code adds the new methods when they are needed while leaving the underlying JavaScript prototypes untouched. The return value of the methods is an array with the extended methods so that they can be chained.

JavaScript:
  1. var arr = [1,null,2,3,4,5,6];
  2. var arr2 = $.protify(arr)
  3.               .compact()
  4.               .findAll(function(a) {
  5.                 return a>=3;
  6.               })
  7.               .first();  // 3

The above code first returns the arr array values in a new array with the Prototype methods, removes any null/undefined values (using compact), finds all the values in the array greater than or equal to 3 and returns an extended array of them (using findAll), and finally returns the first value of that array (using first). It does all of this without touching the prototype for all arrays.

Here are some useful Prototype array methods included in the plugin:

all()

returns true if every member of the array == true and returns false if even one member != true.

JavaScript:
  1. var arr = [1,2,3,4,5,6];
  2. $.protify(arr, true);
  3. arr.all();  // true

any()

returns true if even one member of the array == true and returns false if every member != true.

JavaScript:
  1. var arr = $.protify([1,2,3,4,5,6]);
  2. arr.any(); // true

map()

similiar to jQuery's $.map. It executes the function passed in on every of member of the array and returns an array of the results.

JavaScript:
  1. $.protify([1,2,3,4,5,6]).map(function(n) {
  2.   return n * 2;
  3. });  //  [2,4,6,8,10,12]

eachSlice(number)

slices the array into an array of arrays of the size passed in. There is an optional second parameter which is a function that acts on each array partition before placing it in the slice.

JavaScript:
  1. var arr = [1,2,3,4,5,6];
  2. $.protify(arr, true);
  3. arr.eachSlice(2);  // [[1,2],[3,4],[5,6]]
  4. arr.eachSlice(2, function (n) {
  5.   return n.map(function(item) {
  6.     return item * 2;
  7.   });
  8. }); // [[2,4],[6,8],[10,12]]

include(value)

returns true if the value is in the array (using == equality).

JavaScript:
  1. var arr = [1,2,3,4,5,6];
  2. $.protify(arr, true);
  3. arr.include(3); // true
  4. arr.include('pie'); // false

max()/min()

returns the maximum/minimum value of the array. Optional parameter is a function that can be used to define the comparison and what gets returned.

JavaScript:
  1. $.protify([{'name': 'frank', 'age': 10},{'name': 'joe', 'age': 12}])
  2.   .max(function(person) { return person.age });
  3. // returns 12

partition()

returns an array with two arrays in it. The first array contains the values that == true in the initial array, and the second all of the values != true. There is an optional parameter that is a function used to do the evaluation.

JavaScript:
  1. $.protify([1,2,3,4,5,6]).partition(function (n) {
  2.   return n <= 3;
  3. });
  4. // [[1,2,3],[4,5,6]]


pluck()

And finally, my favorite. The pluck() method iterates through every member of the array and returns an array of the value of the attribute name passed in.

JavaScript:
  1. var arr = [{'name': 'Frank', 'age': 10}, {'name': 'Joe', 'age': 12}];
  2. $.protify(arr).pluck('age'); // [10, 12]
  3. $.protify(arr).pluck('name'); // ['Frank, 'Joe']

For a full listing and description of all the methods, see the Prototype documentation on arrays and enumerables.

Arrays: http://prototypejs.org/api/array

Enumerables: http://prototypejs.org/api/enumerable

The toJSON method has not yet been implemented because it depends on object methods that Prototype provides. Can you guess what my next project is?

comment feed

11 comments

  1. Or you could just use Xavier Shay's excellent jQuery Enumerable plugin. Although it doesn't have all those methods, it's syntax is alot simplier, IMHO:

    squares = $([1,2,3]).collect(function () {
      return this * this;
    } // => [1, 4, 9]
    
    sum = $([1,2,3]).inject(0, function (accumulator) {
      return accumulator + this;
    } // => 6
    
    // Sum is actually provided as a convenience function
    $([1,2,3]).sum() // => 6
  2. This is awesome! Very nice piece of work - thank you!

  3. Very nice! I can definitely see quite a few of these being useful! Thank you.

    Not sure if it's open to contributions but here's a slightly terser version of your min and max methods:

    
        max: function(fn) {
          return Math.max.apply(null, $.map(this, fn || function(v){return v;}));
        },
    
        min: function(fn) {
          return Math.min.apply(null, $.map(this, fn || function(v){return v;}));
        }
    
  4. Josh Powell

    @ Chris Lloyd - The syntax is simpler once you know what it does, but would be very difficult for someone else to figure out from just looking at the code. I can just imagine someone seeing it and looking through the jQuery documentation to figure out what it does and not getting anywhere.

    @John - Thanks :)

    @James - I'm definitely open to contributions, thank you.

  5. Very nice piece of work - thank you!

  6. Lee Roberson

    Am I using this wrong?

    
    var arr = [1,2,3,4,5,1,2,3,4,4,4,3,2,1];
    var protArray = $.protify(arr).uniq();
    

    Yields:
    array.include is not a function
    if (0 === index || (sorted ? arr...t() != value : !array.include(value))) {

    If I modify ln 297 to protify (array) permanently, it works fine.

    Great code. Thanks a million.

    • Josh Powell

      @Lee Roberson - That is the correct fix, that line wasn't doing anything before. Thanks for the contribution!

  7. Mina

    hi,I m neophytes in jquery so I have not much idea about array and I need to use indexes of array of data return by JSON, need some guidlines,,thanks

  8. If you find $.protify(...) a little long to sprinkle through your code, you might add this function:

    function $A(array) {
    return $.protify(array)
    }

    $A(myArray).findAll(...) looks a little better than $.protify(myArray).findAll(...)

  9. Thanks for sharing. I came across this website looking for solution about array created in JS and extraction data from this array in jquery. Question is: how to extract data in jquery from array like this: var tab = new Array(); tab['f1']="foo"; tab['f2']="bar"; is there any jquery function to read this array? I tried $.each, but not working ... :(

    • Hey Birthday Flowers,

      Two things. One, create arrays like this:

      var tab = [];

      instead of:
      var tab = new Array()

      Secondly, what you've created there looks like an associative array, but is actually an array with no members and the properties of f1 and f2. You can accomplish the same thing by creating an object and setting its properties.

      var tab = {};
      tab.f1 = "foo";
      tab.f2 = "bar";

      In other words the following two statements are identical:

      tab.f1 = "foo";
      tab['f1'] = "foo";

      The main difference in usage being that you can use a variable property name using the second notation.

      var baz = 'f1';
      var tab[bav] = 'foo'; //sets tab.f1 to "foo"

      Now, to answer your question, you can iterate through them by doing:

      for (each in tab) {
      console.log(each);
      console.log(tab[each]);
      }

      There are lots of quid pro quo's in doing that, just google around.

5 Pings

  1. [...] Read the original post: Implementing Prototype’s Array Methods in jQuery [...]

  2. [...] Implementing Prototype’s Array Methods in jQuery [...]

  3. [...] Implementing Prototype’s Array Methods in jQuery [...]

  4. [...] the content loads into the relevant container instead of having to navigate to another page. 84. Implementing Prototype’s Array Methods in jQuery – This technique gives a particular array all the methods that Prototype adds to their Array and [...]

  5. [...] the content loads into the relevant container instead of having to navigate to another page. 84. Implementing Prototype’s Array Methods in jQuery – This technique gives a particular array all the methods that Prototype adds to their Array and [...]

Leave a Comment

IMPORTANT:

  • If you wish to post code examples, please wrap them in <code> tags.
  • Multi-line code should be wrapped in <pre><code> </code></pre>
  • Use &lt; instead of < and &gt; instead of > in the examples themselves. Otherwise, you could lose part of the comment when it's submitted.