GeneratorModule: Array Creation Inspired by Python\'s List Comprehension


/ Published in: JavaScript
Save to your folder(s)

The inspiration for this module comes from Python's list comprehension: [x for x in range(10)]. The idea is to replace a for-loop with something more condensed. However, since such syntax is completely foreign to Javascript, that operation looks more like: app.genList(app.rangeGenerator(0, 10)).

Requires ObjectBoilerPlateModule.

GeneratorModule comes with four "public" methods:

* getGeneratorModuleVersion - useful to test for existence of module.
* arrayGenerator - iterate over an array, optionally filtering items from the array with a predicate argument.
* rangeGenerator - iterate over a number sequence from min to max.
* genList - create an array using either an arrayGenerator or a rangeGenerator, optionally morphing elements from the generator into a new data type.

This is implemented using module pattern. To inject the functions into the global namespace, simply invoke the GeneratorModule method. Otherwise, the functions can be added to an existing object using GeneratorModule.call([object]).

Examples:

function test() {
"use strict";

function reportNumber(number) {
return number + " is " + (number % 2 === 0 ? "even" : "odd") + ".";
}

function reportLetter(letter) {
return "The letter is " + letter + ".";
}

function isMultipleOf3(number) {
return number % 3 === 0;
}

var app = {};
ObjectBoilerPlateModule.call(app);
GeneratorModule.call(app, app);
console.log(app.toList(app.range(0, 10)));
// [0,1,2,3,4,5,6,7,8,9]

console.log(app.toList(reportNumber, app.range(0, 15), isMultipleOf3));
// ["0 is even.", "3 is odd.", "6 is even.", "9 is odd.", "12 is even."]

console.log(app.toList(app.inArray(["a", "d", 'q'])));
// ["a", "d", "q"]

console.log(app.toList(reportLetter, app.inArray(["a", "d", 'q']), function (x) { return x === "a"; }));
// ["The letter is a."]
}

Limitations/Considerations:

* Like Python generators, only 1 iteration will function - the generators do not come with a "reset" method.
* The ArrayGenerator objects hold reference to "list" passed in the constructor. If there is code between creating the generator and invoking it (say with genList), it is possible the contents of "list" will change. (This could be mitigated by having the ArrayGenerator constructor perform a deep copy "list"...)


Copy this code and paste it in your HTML
  1. /*jslint plusplus: true, vars: true, browser: true, devel: false, maxerr: 5, maxlen: 140 */
  2.  
  3. //inspired by python list comprehension (x for x in range(10))
  4. function GeneratorModule(boilerMod) {
  5. "use strict";
  6.  
  7. if (!boilerMod || !boilerMod.inherit || !boilerMod.addMethod) {
  8. throw "boilerMod must be ObjectBoilerPlateModule with methods inherit and addMethod.";
  9. }
  10.  
  11.  
  12. function RangeGenerator(min, max) {
  13. this.current = min;
  14. this.max = max;
  15. }
  16. RangeGenerator.prototype.next = function () {
  17. var nn = this.current;
  18. if (nn === this.max) { return undefined; }
  19. this.current += 1;
  20. return nn;
  21. };
  22.  
  23. function InArrayGenerator(list) {
  24. RangeGenerator.call(this, 0, list.length);
  25. this.list = list;
  26. }
  27. InArrayGenerator.prototype = boilerMod.inherit(RangeGenerator.prototype);
  28. InArrayGenerator.prototype.next = function () {
  29. var nn = RangeGenerator.prototype.next.call(this);
  30. if (nn === undefined) { return undefined; }
  31. return this.list[nn];
  32. };
  33.  
  34.  
  35. // Generate an array from any generator with optional filtering by
  36. // predicate: function (item) { return boolean; } and with optional
  37. // morphing of the elements from the generator into another data type.
  38. // If !morph, return elements from the generator as is.
  39. function toList(morph, generator, predicate) {
  40. if (!predicate) { predicate = function (x) { return true; }; }
  41. if (!morph) { morph = function (x) { return x; }; }
  42.  
  43. var ll = [];
  44. while (true) {
  45. var gg = generator.next();
  46. if (gg === undefined) { break; }
  47. if (predicate(gg)) { ll.push(morph(gg)); }
  48. }
  49. return ll;
  50. };
  51.  
  52.  
  53. this.getGeneratorModuleVersion = function () {
  54. return 1.0;
  55. };
  56.  
  57. // Iterator for an array.
  58. this.inArray = function (list) {
  59. return new InArrayGenerator(list);
  60. };
  61.  
  62.  
  63. // Generate a sequence 0 <= x < max. Optionally filter sequence
  64. // by predicate: function (item) { return boolean; }
  65. boilerMod.addMethod(this, "range", function (max) { return new RangeGenerator(0, max); });
  66.  
  67. // Generate a sequence min <= x < max. Optionally filter sequence
  68. // by predicate: function (item) { return boolean; }
  69. boilerMod.addMethod(this, "range", function (min, max) { return new RangeGenerator(min, max); });
  70.  
  71. boilerMod.addMethod(this, "toList", function(generator) { return toList(null, generator); });
  72. boilerMod.addMethod(this, "toList", function(morph, generator) { return toList(morph, generator); });
  73. boilerMod.addMethod(this, "toList", function(morph, generator, predicate) { return toList(morph, generator, predicate); });
  74. }

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.