Javascript functional programming koans

        /* TOC goes here */
    

    Double-click the code to edit the tutorial and try your own code.
    John Resig increased his karma by creating this tutorial framework

    Our Goal
    Our Goal > Enlightenment through functional programming
    
    /*
     *   Today we will expand our awareness of:
     *
     *   (1) First class functions
     *
     *   (2) Higher order functions and collections
     *
     *   (3) Continuations
     *
     *   by meditating on a series of koans
    */
        
    What are these koans of which you speak master?
    //Koans help us contemplate truth by testing reality.
    assert(false, "This should be true" );
    
    //To understand reality, we must compare our assertions against reality.
    assert( ( 1 + 1 ) === FILL_ME_IN);
    
    /*
     * Double click here to edit reality
     *
     * Then click [Meditate] to test whether you have gained enlightenment
     */
        
    More on how koans work...
    //Sometimes we just want to see what's going on
    var theAnswer = 6 * 7;
    log( "Just a simple log", "of", "values.", theAnswer );
    
    //And other times we want to avoid causing an error
    if (6 * 7 !== FILL_ME_IN ) {
        error( "I'm an error!" );
    } else {
        assert(true);
    }
        
    First class functions
    Let us meditate on first class functions
    /*
    *  A language supports first-class functions when:
    *
    * (1) You can bind an identifier to the function,
    *     i.e. you can give it a name
    *
    * (2) You can store the function in a data structure (e.g: a list)
    *
    * (3) You can pass the function as an argument in another function call
    *
    * (4) You can return a function from another function
    */
        
    Giving functions names
    var add = function(x, y) {
      return x + y;
    };
    
    var result = add(2, 3);
    assert(result === FILL_ME_IN, "Execute a named function using ()");
    
    var alsoAdd = add;
    
    assert(alsoAdd(1, 6) === FILL_ME_IN, "Named functions behave just like other variables");
    
    Storing functions in data structures
    var result = 0;
    
    var add = function(x, y) {
      return x + y;
    };
    
    var subtract = function(x, y) {
        return x - y;
    };
    
    var operations = [add, subtract, add, subtract, subtract, add, add];
    
    for (i in operations) {
        result = operations[i](result,5);
        // log("result is now:", result);
    }
    
    assert(result === FILL_ME_IN, "what is the final result?");
    
    Passing a function as an argument to another function call
    var afterMeditation = function(knowledge) {
        assert(knowledge === FILL_ME_IN, "what knowledge has been accumulated");
    };
    
    var thinkLongAndHard = function(fact1, fact2, afterThinking) {
        var result = fact1 + ';' + fact2;
        afterThinking(result);
    };
    
    thinkLongAndHard('wind', 'rain', afterMeditation);
    
    You can return a function from another function
    var powerFactory = function(power) {
            return function(number) {
                var result = 1;
                for(var i = 0; i < power; i++) {
                    result = result * number;
                }
                return result;
            }
        };
    
    var squared = powerFactory(2);
    var cubed = powerFactory(3);
    
    assert(squared(2) + cubed(3) === FILL_ME_IN, "functions can create new functions");
    
    Higher order functions
    First class functions make common operations with collections easy
    /*
     * The following are known as higher order functions:
     *
     * (1) each
     * (2) map
     * (3) filter
     * (4) reduce
     * (5) all
     * (6) any
     * (7) range
     * (8) flatten
     *
     * because they apply a function to a list in a common way.
     * (We use the underscore.js library to ensure these are
     *  applied consistently across all browsers)
     *
    */
    
    Each
    /* Iterates over a list of elements, yielding each in turn to an iterator function. */
    
        var numbers = [1,2,3];
        var msg = "";
        var isEven = function(item) {
          msg += (item % 2) === 0;
        };
    
        _.each(numbers, isEven);
    
        assert(msg === FILL_ME_IN, "isEven was called once for each element");
        assert(numbers === FILL_ME_IN, "but the original list wasn't touched");
    
    Map
    /* Produces a new array of values by mapping each value in list through a transformation function (iterator). */
    
        var numbers = [1, 2, 3];
        var numbersPlus1 = _.map(numbers, function(x) { return x + 1 });
    
        assert(numbersPlus1 === FILL_ME_IN, "each element is the result of the function applied to the original list");
        assert(numbers === FILL_ME_IN, "but the original list wasn't touched");
    
    Filter
    /* Looks through each value in the list, returning an array of all the values that pass a truth test (iterator) */
    
        var numbers = [1,2,3];
        var odd = _(numbers).filter(function (x) { return x % 2 !== 0 });
    
        assert(odd === FILL_ME_IN, "should only contain odd");
        assert(numbers === FILL_ME_IN, "but the original list isn't touched");
    
    Reduce
    /* Reduce boils down a list of values into a single value. */
    
        var numbers = [1, 2, 3];
        var reduction = _(numbers).reduce(
                function(/* result from last call */ memo, /* current */ x) { return memo + x }, /* initial */ 0);
    
        assert(reduction === FILL_ME_IN, "should be the cumulative sum");
        assert(numbers === FILL_ME_IN, "but the original list isn't touched");
    
    All
    /* Returns true if all of the values in the list pass the iterator truth test */
    
        var onlyEven = [2,4,6];
        var mixedBag = [2,4,5,6];
    
        var isEven = function(x) { return x % 2 === 0 };
    
        assert(_(onlyEven).all(isEven) === FILL_ME_IN, "are _all_ of the list items even?");
        assert(_(mixedBag).all(isEven) === FILL_ME_IN, "leave the original list alone!");
    
    Any
    /* Returns true if any of the values in the list pass the iterator truth test.
     * Short-circuits and stops traversing the list if a true element is found.
     */
    
        var onlyEven = [2,4,6];
        var mixedBag = [2,4,5,6];
    
        var isEven = function(x) { return x % 2 === 0 };
    
        assert(_(onlyEven).any(isEven) === FILL_ME_IN, "are any of the list items even?");
        assert(_(mixedBag).any(isEven) === FILL_ME_IN, "leave the original list alone!");
    
    Range
    /* A function to create flexibly-numbered lists of integers, handy for each and map loops. */
    
        assert(_.range(3) === FILL_ME_IN, "should have 3 elements");
        assert(_.range(1, 4) === FILL_ME_IN, "should start at 1 and end at 4 (exclusive)" );
        assert(_.range(0, -4, -1) === FILL_ME_IN, "should increment in steps of -1");
    
    Flatten
    /* A function to create flexibly-numbered lists of integers, handy for each and map loops. */
        expect(_([ [1, 2], [3, 4] ]).flatten() === FILL_ME_IN);
    
    Chain
    /* Returns a wrapped object.
     * Calling methods on this object will continue to return wrapped objects until value is used.
     */
    
        var result = _([ [0, 1], 2 ]).chain()
                           .flatten()
                           .map(function(x) { return x+1 } )
                           .reduce(function (sum, x) { return sum + x })
                           .value();
    
        assert(result === FILL_ME_IN);
    
    Continuations
    Define what happens later
    var doWork = function(amount, onDone, onError) {
        var workOutput = _.range(amount)
                          .map(function(number) {
                                if (number > 10)
                                    return 'ERROR';
                                return number;
                          });
        if (_.any(workOutput, function(item) { return item === 'ERROR'; }))  {
            onError(amount + ' is larger than 10');
        } else {
            onDone(workOutput);
        }
    };
    
    var result = '';
    
    var whenDone = function(workOutput) { result = 'All done'; };
    var logError = function(msg) { result = msg; };
    
    doWork(5, whenDone, logError);
    assert(result === FILL_ME_IN, "which 'continuation' function gets called?");
    
    doWork(20, whenDone, logError);
    assert(result === FILL_ME_IN, "which 'continuation' function gets called now?");
    
    
    Continuations enable async programming
    /* In Javascript you can only make asynchronous network calls
    
       The library function
    
            CIAPI.services.GetPriceBars(marketId, priceBars, success, error)
    
       makes a network call to the CityIndex API to fetch some historic price data
    
    */
    
        //Meditate on the following
        CIAPI.services.GetPriceBars(51, 2,
            function(data) { log(data); },
            function(errorData) { error(errorData); }
        );
    
    /* **************************************************************
        Now, how would you fetch a collection of 10 market's price data,
        and identify which ones 3 consecutive rising bars.
       ************************************************************* */
    
        //hint - map() a range() to CIAPI.services.GetPriceBars() and
        //do your processing in the success() continuation
    
    Applying our enlightenment
    Applying our enlightenment
        var products = [
           { name: "Sonoma", ingredients: ["artichoke", "sundried tomatoes", "mushrooms"], containsNuts: false },
           { name: "Pizza Primavera", ingredients: ["roma", "sundried tomatoes", "goats cheese", "rosemary"], containsNuts: false },
           { name: "South Of The Border", ingredients: ["black beans", "jalapenos", "mushrooms"], containsNuts: false },
           { name: "Blue Moon", ingredients: ["blue cheese", "garlic", "walnuts"], containsNuts: true },
           { name: "Taste Of Athens", ingredients: ["spinach", "kalamata olives", "sesame seeds"], containsNuts: true }
        ];
    
       /* Given I'm allergic to nuts and hate mushrooms, find a pizza I can eat (imperative) */
    
        var i,j,hasMushrooms, productsICanEat = [];
    
        for (i = 0; i < products.length; i+=1) {
            if (products[i].containsNuts === false) {
                hasMushrooms = false;
                for (j = 0; j < products[i].ingredients.length; j+=1) {
                   if (products[i].ingredients[j] === "mushrooms") {
                      hasMushrooms = true;
                   }
                }
                if (!hasMushrooms) productsICanEat.push(products[i]);
            }
        }
    
        assert(productsICanEat.length == FILL_ME_IN);
    
        /* now solve in a functional style */
    
        productsICanEat = [];
    
        /* try using filter() & all() / any() */
    
        assert(productsICanEat.length === FILL_ME_IN);
    
    
    Go forth
      /* Congratulations grasshopper, you have reached the first level of
       * functional enlightenment!
       *
       * Remember to take this new awareness with you when solving
       * everyday problems - you will find that many of your
       * existing tools contain functional elements, and that these
       * can greatly simplify a certain class of problems
       */