Functionally Similar – Comparing Underscore.js to LINQ

Underscore.JS is a useful JavaScript library for anyone familiar with the LINQ syntax. It is not a direct LINQ port like Rx or linq.js, but provides a very useful "LINQ-like" experience for anyone familiar with functional programming. Is it similar enough to LINQ to make it easy to transfer your existing skills?

Comparing Underscore.js to LINQ

Overview

When LINQ was added to C# in ASP.Net 3.0, it changed the way that we work with data collections. When working with server-side data in web-based applications, LINQ is an invaluable time saver. LINQ makes short work of iterative functions by means of a simple syntax that is easy to read and understand.

Unfortunately, server-side data is no longer enough. Users now expect the sort of fast and fluid web pages that require client side processing. To be able to work with data on the client-side, you need to be comfortable with writing JavaScript. If you don’t have a good understanding of JavaScript, the transition from writing server side C# to JavaScript can be challenging.

If you are already familiar with LINQ with C#, then it would help to be able to use a similar syntax with client-side data access. Underscore.js allows you to work in JavaScript with arrays and object-collections in a similar way to LINQ, but how similar? Let’s look at this in a bit more detail.

About Underscore.js

Underscore is an open source functional programming utility library for JavaScript. Underscore provides many useful functions for working with arrays or collections of JavaScript objects, including filtering, sorting and querying.

Using underscore is as simple as adding the JavaScript file to your project and referencing it on the page.

You can get the library file via NuGet, CDN or by downloading the file directly from the home page.

Usage

Underscore offers two different syntax styles, a functional syntax and an object-oriented syntax. If you were introduced to JavaScript by writing jQuery, then the object-oriented style might be a more comfortable choice because it follows a similar approach. The two are operationally equivalent, so picking which style to use is a matter of personal preference.

LINQ also has several syntax choices with its declarative query and functional (a.k.a. method chaining) syntaxes, but LINQ’s declarative query syntax has no equivalent in Underscore whereas both of Underscores styles are more representative of the functional syntax found in LINQ.

Underscore shares with LINQ an ability to iterate through a dataset and produce equivalent results but it is implemented in a very different way. While LINQ generally returns an IEnumerable<T>, Underscore will simply return the results as an array or array of objects. Because Underscore generally returns array values rather than an object it doesn’t provide method chaining by default, instead a special chain function must be called to wrap the results in a chainable API.

Several Underscore functions are almost identical in operation to LINQ, such as Any and, All and SortBy (OrderBy). While other functions like Select and Where aren’t used in the same way although they are in LINQ.

Let’s take a look at each of these functions in both C# and JavaScript to see how both are implemented and compare the similarities between them.

Any

Any which also goes by the alias of some in Underscore, will determine if any element in a list passes a test specified by the iterator of the function. Both LINQ and Underscore return a Boolean value and the enumeration is stopped once the test becomes true. You can see in the example below how both Any functions are used, the similarities are very close in either environment.

In the following example a list of articles is checked to see if the set contains any unread articles.

Underscore:

LINQ:

All

All, which also goes by the alias of every in Underscore, will determine if all element in a list passes a test specified by the iterator of the function. All will short-circuit when the outcome has been determined in both LINQ and Underscore. All is the logical opposite of Any.

In the following example, a list of articles is checked to see if all of the articles in the set have been read.

Underscore:

LINQ:

Each

With Underscore, ForEach loops can be simplified by using the each function. The each function will iterate through a list and perform an operation on each item in the set. There is no equivalent function in LINQ however there is a ForEach method that is part of the List object which operates much like its Underscore counterpart.

In the following example each we use the each and ForEach functions to mark all of the articles in a collection as read.

Underscore:

List<T> (System.Collections.Generic)

SortBy

You can sort lists of arrays or objects by using the SortBy function of Underscore. SortBy will return a copy of the list sorted in ascending order, so you will assign the return value of the function. SortBy can use an iterator, or property name of an object can be used by passing in the string name of an objects property. Although SortBy works similar to LINQs OrderBy method, there is no Underscore equivalent to get a reversed sort. Instead the results must be reversed using the JavaScript reverse function, whereas with LINQ you would use the OrderByDecending method.

In the examples below, a list of articles is sorted by author name in both ascending and descending order.

Underscore:

LINQ:

Select

The select function in Underscore is vastly different from the one in LINQ. The Select method in LINQ is used for projections, the operation of transforming an object into a new model that represents a subset of data for a specific use or view. Underscore’s select, however, is a filtering or restriction operator that returns a restricted result set of objects to satisfy a specified condition. This is why select in Underscore also goes by the alias of filter, since the function returns a filtered result set.

Because the select function of Underscore is a restriction operator, we will have to compare it to a similar restriction operator in LINQ. For this comparison, LINQ’s Where method is actually quite similar.

In the example below, Underscore and LINQ are used to filter a list of objects. In this case we filter a list of articles that fall within a specified date range. It’s important to remember that the result set that is returned is of the same object structure that the function was initially given.

Underscore:

LINQ:

Where

Using the where function in Underscore is similar in some respects Where in LINQ. In both LINQ and Underscore, where is a restrictive operator that returns a filtered set of data. The where function in Underscore is limited to one or more exact key-value pair matches, although LINQ is able to test a value by virtually any criteria. If you need more than key-value pair matches then the select or filter function should be used instead.

In the example below Underscore and LINQ are used to filter a set of data. In this case we filter a list of articles where the IsRead property has been set to false.

Underscore:

LINQ:

Map

The map function in Underscore is used for projections and is comparable to LINQ’s Select method. The result of map is a new array of values mapped by the iterators function.

In the following examples, map and Select are used to transform a set of larger article objects into a set of simplified article preview objects. The article preview contains the article’s title, author and description which is truncated to 160 characters (including the ellipsis). The transformation here is fairly simple, however we are not restricted to only simple operations and the iterator could be designed to do almost any operation.

Underscore:

LINQ:

Chain

Underscore’s functions, unlike LINQ, do not use fluent method-chaining by default. This is because Underscore returns a collection of objects whereas LINQ methods return an IEnumerable object. To enable chaining in Underscore the chain function can be called. Beginning an operation with chain will cause the return value to be a wrapped object. When the chain is complete then the value method can be called to return the final result.

Another significant difference between chaining in Underscore versus LINQ is how each method is executed. LINQ uses deferred execution, meaning that commands are stored in the result until, and not executed until, the result is iterated over in a For Each loop or ToList is called. When chaining in Underscore, every function call immediately executes and iterates through the data set returning a value. The value is then wrapped in an object to continue the chain.

The following example uses chaining to create a view based on a collection of articles. To do this, we will chain the where, map, and sortBy functions together. First the list of articles is filtered for items that have not been read, and then a simplified projection is created. Finally, the items are sorted by author name and returned.

Underscore:

LINQ:

LINQ

Underscore

Operator Category

Any

any

Quantifier

All

all

Quantifier

Each (not LINQ)

forEach

Iterator

OrderBy

sortBy

Ordering

Where

where (when value is an exact match)

Restriction

Where

filter or select

Restriction

Select

map

Projection

chain | value

Utility

ToList

Conversion

A quick reference list for the functions reviewed in this article.

Conclusion

Underscore is a great utility for working with sets of data in JavaScript. There are a few cases when a function name from Underscore doesn’t perform the same task as it does in LINQ, but this is rare. It is important to be mindful of their different implementations with respect to chaining and deferred execution. Even though Underscore isn’t identical to LINQ, they are close enough in operation that the learning curve for Underscore is very minimal.