CoffeeScript Object Comprehensions (Excerpt from CoffeeScript in Action)
By Patrick Lee
CoffeeScript Object Comprehensions
This article is based on CoffeeScript in Action, to be published Summer 2012. It is being reproduced here by permission from Manning Publications. Manning early access books and ebooks are sold exclusively through Manning. Visit the book’s page for more information.
To add up all the view counts for every property on the views object, you need to use a comprehension. In this article, based on chapter 4 of CoffeeScript in Action, you will learn how to use comprehensions to transform the properties and values of an object into other values.
Imagine you have a website with two pages. You want to track how many views each page on your website gets and the total number of views for all pages. Your website pages live on specific URLs but are referred to simply as the pages ren and stimpy for the sake of clarity. If the ren page has so far received 30 views and the stimpy page 10 views then that can be represented as:
views =
'ren': 30
'stimpy': 10
Storing a property on the object is by assignment, so a count can be incremented using:
views.ren = views.ren + 1
How about adding up all the view counts for every property on the views object though? How is that done? By using a comprehension. In this article, you will learn how to use comprehensions to transform the properties and values of an object into other values.
Object comprehensions
Comprehensions allow you to deal the elements of an array without having to write a bunch of boilerplate and iterate manually through every single thing in the array one by one, again and again and again. Check it out:
number + 1 for number in [1,2,3,4,5] # [2,3,4,5,6]
Object comprehensions in CoffeeScript work similarly to how array comprehensions work. They also have a similar basic format to array comprehensions except they use the word of instead of in:
expression for property of object
What does it do? Listing 1 is a side-by-side comparison with the JavaScript’s for…in loop that CoffeeScript replaces with object comprehensions:
Listing 1 Comprehension compared to for…in loop
| CoffeeScript | JavaScript |
|---|---|
movie = title: 'From Dusk till Dawn' released: '1996' director: 'Robert Rodriguez' writer 'Quentin Tarantino' for property of movie console.log property |
var movie = {
title: 'From Dusk till Dawn',
released: '1996',
director: 'Robert Rodriguez',
writer: 'Quentin Tarantino'
}
for (var property in movie) {
} console.log(property);
|
Listing 2 Comprehension as expression
| CoffeeScript | JavaScript |
|---|---|
properties = (prop for prop of movie) |
var properties = {};
for (var prop in movie) {
properties.push(p);
}
|
The JavaScript version uses multiple statements and has to micromanage the state of the variables properties and p. The CoffeeScript version does not.
Comprehending properties
The property names of an object can be returned as an array using a comprehension:
name for name of {bob: 152, john: 139, tracy: 209} # ['bob', 'john', 'tracy']
Imagine now that your website has four pages named by the path to those pages. The object below shows an example:
views =
'/reviews/pool-of-radiance': 121
'/reviews/summer-games': 90
'/reviews/wasteland': 139
'/reviews/impossible-mission': 76
A list of pages from this object is obtained using the following comprehension:
url for url of views
Resulting in an array containing the page names:
[ '/reviews/pool-of-radiance'
'/reviews/summer-games'
'/reviews/wasteland'
'/reviews/impossible-mission' ]
Comprehending values
To get the property values from an object instead of the property names, use a slightly different comprehension format:
value for property, value of object
For example,
score for name, score of {bob: 152, john: 139, tracy: 209} # [152, 139, 209]
The number of views for each page is obtained using a comprehension
count for url, count of views
For the four pages described earlier results in an array containing the page views for those pages:
[ 121, 90, 139, 76 ]
Alternative format
In the total function the comprehension looks like
for property, value of object
expression
This works the same as the format
expression for property, value of object
The expression will be evaluated for each property in the object. The comprehension can be used to collect and sum the view count for all of the pages:
Example
| CoffeeScript | JavaScript |
|---|---|
properties = (prop for prop of movie) |
var properties = [];
for (var prop in movie) {
properties.push(prop);
}
|
To keep track of how many views each page gets, you will need a function to increment the value stored against an individual page. To get the total number of views for all pages, you will need a function that sums all of the values of the object. Listing 3 is a first implementation of this program. A detailed discussion follows the listing:
Listing 3 Page views
views = {} #A
views_increment = (key) -> #B views[key] ?= 0 #B views[key] = views[key] + 1 #B total = ->
sum = 0 #C
for own url, count of views #C
sum = sum + count #C
sum #C
#A The views object, created ex-nihilo
#B An increment helper function
#C A total function to add up the page views
Reduce!
If you were expecting the sum to be done inside the total function with a reduce on the array, then rest assured that you can and should use Array.reduce in CoffeScript.
To begin, create a views object ex nihilo:
views = {}
Dealing with undefined properties
The views object does not yet have any properties. If there is a page called donatello that receives a view, how do you increment a donatello property that does not exist? If the views object does not have a donatello property, then this must be the first view to the page donatello and so the value should be set to 1. The existential operator
can be used to determine if a property is defined:
if !views['donatello']?
views['donatello'] = 1
This is a common pattern, so CoffeeScript has a shorter version of it called existential assignment. Put an existential operator in front of the assignment operator:
views['donatello'] ?= 0
You will notice that the views_increment function takes the name of the property as the argument key, which it
uses as the property name. The specific case of donatello is then generalized:
views[key] =? 0
Updating values
Once the views object has a property, the value can be incremented:
views[key] = views[key] + 1
Getting the total
To compute the total number of views for all pages, you can now use a comprehension on the views object:
sum = 0 for own url, count of views sum = sum + count
Own properties
The total function adds an own to the comprehension, immediately after the for keyword: for own url, count of views
Objects can get properties that were not defined directly on them. In this case, it was important not to include any properties not defined directly on the object. Properties of an object that are not defined on the object come from prototypes.
TIP
If you are using an object as a key-value store, always use own inside comprehensions on that object.
Summary
Considering that objects as key-value stores provide code-as-data for CoffeeScript, you would expect an equally convenient way to do things with that data. Comprehensions provide dedicated syntax to do that. In this article, you learned how to write comprehensions for objects.
-
http://www.aaron-powell.com Aaron Powell
-
http://www.userinexperience.com Brandon Satrom
-
http://www.bglen-fan.net/ ビーグレン
@BrandonSatrom
- No public Twitter messages.
Tags
.net analogy architecture asp.net asp.net mvc BDD blog C# CoffeeScript communication composition conference conferences css3 ddd design development EA events fun google HTML5 javascript knockout language links microsoft mvc mvccontrib mvvm oss screencast series speaking tdd technology travel ux video WatiN web 2.0 wf win8 workflow writingCategories









