Wednesday, 12 July 2017

Using ES6 Generators to implement Where Skip Take

This article will present the use of ES6 Generators to implement in Javascript some handy functions for collections (arrays) that can filter and limit the size of that result. The technique will use the new ES6 generator functions.

ES6 Generators are functions of Ecmascript 6 that let's the programmer take control of iterators using the yield keyword and use custom logic.

We will use Traceur to support ES6 syntax. Traceur is a Javascript compiler in the form of a library. In production you should instead use a transpiler, that rewrites your ES6 syntax into ES2015, which is more supported by current web browsers. One transpiler is Babel.

First off we include Traceur into the Html code:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
        <script src="https://google.github.io/traceur-compiler/bin/BrowserSystem.js"></script>
        <script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
        
    </head>
    <body bgcolor="teal" style="font-family:Verdana, Courier,Helvetica, Arial">
        
        <h1 style="color:yellow;font-family:Arial">ES6 module demo</h1>



We now have the compiler loaded into our HTML page and can make use of ES6 Generator functions and other ES6 syntax. First lets look at the WHERE function in Javascript. Think of it as WHERE in Linq, we will also use ES6 Arrow functions here, which is an important functional programming concept and syntax that ES6 introduces.


   <script type="text/javascript">

         var where = function* (predicate, collection){
                for (let item of collection){                  
                    if (predicate(item)){                   
                        yield item;      
                    }
                }
            }




We assign where here to the function defined at the right hand side. Note the asterisk (star) here. This is what a generator function looks like in ES6. It must not be mixed with function pointers as in C++, the star here is just to separate generator functions with ordinary functions. On a operational level, generator functions are just like iterators in C# that hey hand off control from the function to the program that calls the functions. Generator functions are usually used in iteration loops, which will be seen later on in this article.

The where function passes in a predicate (an arrow function that returns a boolean which is true or false of course) and if that predicate on the current item of the collection is true, yield will return that item. The sample also uses the of operator of the collection passed in.

Next off, let us look at the take function. This function will take a desired number of the result set and end iteration early if a result set is found. On a very large collection, this is very efficient. Imagine searching a huge collection for a desired item defined by an arrow function and exit early if for example one item is found. Then take(1, mycoll) will return control to the calling program when that item is found.


            var take = function* (size, collection){

            if (size < 1) 
                return;

            let count = 0;

            for (let item of collection){
                yield item;
                count = count + 1;
                if (count >= size){
                    return;
                }
            }
        }



Make note that we end the looping here by doing a return when the amount of items return is the desired size specified. Next off, let us look at the Skip function. We could do a reserve on the passed in collection, and use the take function. Instead the Skip method will use tailored logic to iterate on the passed in collection from start to finish, skipping the specified number of items first.

  var skip = function* (size, collection){
         
            let skippedItems = 0;

            for (let item of collection){
                skippedItems = skippedItems + 1;
                //console.log(item);
                //console.log(skippedItems);
                
                if (skippedItems <= size){
                    continue;
                }
                else {
                    yield item;
                }
            }

        }
The sample HTML code below tests out the JS code above using ES6 arrow functions and ES 6 generator functions on a simple array and looping through with our new powerful functional programming constructs that let us search even huge collections with filtering and skipping or taking the desired result.

         
            var coll = [ "Tammy", "Tom", "Betty", "Marge", "Joey", "Tim", "Jane", "Tommy" ];

            //console.log(coll);
            
            for (let i of take(2, where(n => n.startsWith("T"),coll))){
                   $("body").append("<li>" + i + "</li>");
            }

Next off, we will combing all the uses of WHERE, SKIP and TAKE to both filter and provide paging functionality. Consider this code:


            var coll = [ "Tammy", "Tom", "Betty", "Marge", "Joey", "Tim", "Jane", "Tommy" ];

            console.log(coll);

            let pageSize = 1;
            let pageIndex = 1;
            
            for (let i of take(pageSize, skip(pageIndex*pageSize, where(n => n.startsWith("T"),coll)))) {
                   $("body").append("<li>" + i + "</li>");
            }
                  
Here we use the following to get a PAGE of our result by using the formula:

PAGE_INDEX = TAKE(PAGE_SIZE) of (SKIP(PAGE_INDEX * PAGESIZE) of COLLECTION WHERE Predicate(x)). It is impressive that we can implement Linq like functionality by using ES6 Generator functions in ES6 sticking to ES6 syntax. This promises that Javascript in the future will be a very versatile language when it comes to functional programming. We use Traceur in the meantime to support ES6 syntax in different web browsers. I have included a Plunk in the link below so that you can test this out yourself.

ES6 Generator functional programming sample

No comments:

Post a Comment