October 19, 2017
Map, Reduce, and Filter functions in ColdFusion
Comments
(3)
October 19, 2017
Map, Reduce, and Filter functions in ColdFusion
(3)

Functional programming has gained popularity in the recent past. Using functional programming techniques, you can write code that is relatively bug-free, easier to debug and test, and easier to refactor.

One of the reasons why functional programming has become popular is its ability to manipulate data structures easily and efficiently when compared to traditional methods.

In this blog, we shall take a close look at the pillars of functional programming, map, reduce, and filter, and how you can use these functions to write better and cleaner code in ColdFusion.

Note: We have used arrays for the examples.

Map

The Map function operates on all elements of an array and produces an array of the same dimension with transformed values. To illustrate the point, the code below uses a for loop to iterate through an array and returns a transformed array with new values.

Before using map

<cfscript>
     animals=["cat","dog","horse","lion","tiger"];
     for (i=1;i<=arrayLen(animals);i++){
           myArray[i]="This is a " & animals[i];
     }
     writeDump(myArray);
</cfscript>

While this is a perfectly acceptable approach, depending on the size of the data structure and transformation rules, the code might leave a large footprint and become difficult to manage. The map() function reduces all that and more, and makes your code a lot less clunky. Let’s see map in action:

After using map

<cfscript>
     animals=["cat","dog","horse","lion","tiger"];
     myArray=animals.map(function(item){
           return "This is a " & item;
     });
     writeDump(myArray);
</cfscript>

Right off the bat, you will notice that the code is much shorter and uses fewer variables. What you see here is that we have created an anonymous function that performs an operation on each element of the input array and returns a transformed array. Take another example:

<cfscript>
   animals=["cat","dog","horse","lion","tiger"];
   function getSize(word){
   return len(word);
}
   writeDump(animals.map(getSize));
</cfscript>

You can use map for structs and lists as well. For more information, refer StructMap and ListMap.

Filter

Filter, like map, operates on all elements of an array, and returns a filtered, subsetted array, depending on one or more conditions. Think of WHERE clause in SQL, and you get the picture.

Traditionally, using for-loop, you would write the following code:

<cfscript>
      superheroes=[
           {"name":"Iron Man","member":"Avengers"},
           {"name":"Wonder Woman","member":"Justice League"},
           {"name":"Hulk","member":"Avengers"},
           {"name":"Thor","member":"Avengers"},
           {"name":"Aquaman","member":"Justice League"}
     ];

     for (i=1;i<=arrayLen(superheroes);i++){
           if (superheroes[i].member=="Avengers"){
                avengers[i]=superheroes[i];
           }
     }
     writeDump(avengers);
</cfscript>

The code above, though functional, will grow as the size of the array grows along with if-else statements. The complexity of the code also increases with increase in the number of loops and the time taken to iterate over each loop.

After using filter

In ColdFusion, you can use the filter() function to substantially reduce the code footprint and reduce the complexity.

The filter method accepts a callback function. In that callback function, we examine each element of the array, and return TRUE if we want the element to pass the filter. The elements that have passed the filter are then inserted into a new array.

You can rewrite the code as shown below:

<cfscript>
     superheroes=[
           {"name":"Iron Man","member":"Avengers"},
           {"name":"Wonder Woman","member":"Justice League"},
           {"name":"Hulk","member":"Avengers"},
           {"name":"Thor","member":"Avengers"},
           {"name":"Aquaman","member":"Justice League"}
     ];

     avengers=superheroes.filter(function(item){
           return item.member=="Avengers";
     });
     writeDump(avengers);
</cfscript>

As with arrays, you can also use the filter method for structs and lists. Refer StructFilter and ListFilter.

Reduce

Reduce “reduces” an array to a single value. Let us write two examples validating this point, without using the reduce function.

<cfscript>
     // array of numbers
     numarray=[1,2,3,4,5];
     sum=0;
     for (i=1;i<=arrayLen(numarray);i++){
           sum+=numarray[i];
     }
     writeoutput(sum);
</cfscript>

Similarly,

<cfscript>
     // array of strings
     wordarray=["I","love","ColdFusion"];
     message="";
     for (i=1;i<=arrayLen(wordarray);i++){
           message&=wordarray[i];
     }
     writeoutput(message);
</cfscript>

By now, you must have noticed a certain pattern here. You start with a collection of numbers/strings, initialize a variable (zero or empty string), and iterate over the collection to return a single value to the variable.

Again, the implementation is not the greatest and there will be complexities with the growth in the code footprint.

Fortunately, using reduce, you can make the code a lot simpler. In the callback, reduce uses the following parameters.

  • The current value
  • The previous value
  • The current index
  • The array you called reduce on

After using filter

<cfscript>
       data = [1,2,3,4,5,6];
       sum=data.reduce(function(previous,next) {
       return previous+next;
       },0);
       writeOutput(sum); // Output is 21
</cfscript>

That’s it. We have simply reduced the array of numbers to the variable sum.

Let’s explain the code- The reduce function takes a callback function and an initial value as parameters. The callback function uses the parameters previous and next to add a current array element to its preceding element. When the function reaches the end of the array, the output is the sum of all elements in the input array.

Similarly, you can use the reduce function for structs and lists. Refer StructReduce and ListReduce.

We hope that this blog has given you enough information to get you started on map, reduce, and filter on arrays, and extend the same principles to lists and structs. In subsequent blogs, we shall show how you can use the functions on query objects.

 

3 Comments
2019-02-13 20:17:05
2019-02-13 20:17:05

One thing that I found that isn’t documented in the Adobe Help.

In ArrayMap (which may operate differently from just plain map), Adobe shows only (item) which is the individual data in the iteration.  But, if you use (item,index), you also get the index of the array.

animals=[“cat”,”dog”,”horse”,”lion”,”tiger”];
arrayMap(animals,function(item,index){
writeOutput(“Animal number ” & index & ” is a ” & item & “<br />”);
});

Like
()
2017-10-25 15:45:09
2017-10-25 15:45:09

This is very useful and efficient functionality.

It’s worth pointing out that the callback function can be defined first and then multiple Map, Reduce, and Filter functions can be called with just the callback function name.

Like
(1)
(1)
>
hemsmarki
's comment
2017-10-25 15:48:03
2017-10-25 15:48:03
>
hemsmarki
's comment

Eddie,

Thanks for your comment. I shall update the blog shortly.

Thanks,
Saurav

Like
()
Add Comment