Click here to monitor SSC
  • Av rating:
  • Total votes: 17
  • Total comments: 4
Edward Charbeneau

Functionally Similar - Comparing Underscore.js to LINQ

12 August 2013

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.

  <!-- underscore -->
    <script src="/underscore-min.js"></script>

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.

//Functional syntax
_.all(articles, function(article) { return article.isRead });

//OOP Syntax
_(articles).all(function(article) { return article.isRead });

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.

//LINQ functional or method chaining syntax
//Similar to both Underscore styles
articles.All(article => article.IsRead);

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:

//Syntax
_.any(list, [iterator]) //returns boolean
_(list).any([iterator]) //returns boolean
_.some(list, [iterator]) //returns boolean
_(list).some([iterator]) //returns boolean

//Example: Check to see if the user has unread articles
function hasUnreadArticles(articles) {
    return _(articles).any(function (article) { return article.isRead});
}

LINQ:

//Syntax
public static bool Any<TSource>(this IEnumerable<TSource> source)

//Example: Check to see if the user has unread articles
public bool HasUnreadArticles(List<Article> articles)
{
     return articles.Any(article => !article.IsRead);
}

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:

//Syntax
_.all(list, [iterator]) //returns boolean
_(list).all([iterator]) //returns boolean
_.every(list, [iterator]) //returns boolean
_(list).every( [iterator]) //returns boolean

//Example: Check to see if the user has read all of the articles
function hasReadAllArticles(articles) {
    return _(articles).all(function(article) { return article.isRead});
};

LINQ:

//Syntax
public static bool All<TSource>(this IEnumerable<TSource> source)

//Example: Check to see if the user has read all of the articles
private bool HasReadAllArticles(List<Article> articles)
{
    return articles.All(article => article.IsRead);
}

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:

//Syntax
//_.each(list, iterator)
//_(list).each(iterator)

//Example: Mark all articles as read
function markAllAsRead(articles) {
    _(articles).each(function (article) { article.isRead = true; });
    return articles;
};

List<T> (System.Collections.Generic)

//Syntax
//public void ForEach(Action<T> action)

public void MarkAllAsRead(List<Article> articles)
{
    articles.ForEach(a => a.IsRead = true);
}

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:

//Syntax
_.sortBy(list, iterator [property name])
_(list).sortBy(iterator [property name])

//sort a list of articles by the author property
function sortArticlesByAuthors(articles) {
    return _.sortBy(articles, "author");
};

//sort a list of articles by the author property in decending order
function sortArticlesByAuthors(articles) {
    return _.sortBy(articles, "author").reverse();
};

LINQ:

//Syntax
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector
)

//sort a list of articles by the author property
private List<Article> SortArticlesByAuthor(List<Article> articles)
{
    return articles.OrderBy(article => article.Author).ToList();
}

//sort a list of articles by the author property in decending order
private List<Article> SortArticlesByAuthor(List<Article> articles)
{
    return articles.OrderByDecending(article => article.Author).ToList();
}

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:

//Syntax
_.select(list, iterator)
_.filter(list, iterator)
_(list).select(iterator)
_(list).filter(iterator)


//Returns articles within a given date range
function getArticlesByDateRange(articles, beginDate, endDate) {
    return _(articles).select(function (article) {
        var articleDate = Date.parse(article.pubDate);
        return articleDate >= beginDate && articleDate <= endDate;
    });
};

LINQ:

//Syntax
public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate
)

//Returns articles within a given date range
private List<Article> GetArticlesByDateRange(List<Article> articles, 
DateTime beginDate, DateTime endDate)
{
    return articles.Where(article => DateTime.Parse(article.PubDate) >=
          beginDate && DateTime.Parse(article.PubDate) <= endDate).ToList();
}

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:

//Syntax
_(list).where(properties)
_.where(list, properties)

//Returns unread articles
function getUnreadArticles(articles) {
    return _(articles).where({"isRead": false});
};

LINQ:

//Syntax
public static IEnumerable<TSource> Where<TSource>(this 
IEnumerable<TSource> source,    Func<TSource, bool> predicate)

//Returns unread articles
private List<Article> GetUnreadArticles(List<Article> articles)
{
    return articles.Where(article => article.IsRead == false).ToList();
}

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:

//Syntax
_(list).map(iterator)
_.map(list, iterator)

function getArticlePreviews(articles) {
    return _(articles).map(function (a) { 
        return { 
            'title': a.title, 
            'author': a.author, 
            'shortDescription': a.description.substring(0, 157) + "..."
        }
    })
};

LINQ:

//Syntax
public static IEnumerable<TResult> Select<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TResult> selector
)

private List<ArticlePreview> GetArticlePreviews(List<Article> articles)
{
    return articles.Select(article => new ArticlePreview()
    {
        Title = article.Title,
        Author = article.Author,
        SortDescription = article.Description.Length > 157 ?
          article.Description.Remove(157) + "..." : article.Description
     }).ToList();
}

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:

function getReadingList(articles) {
    return _.chain(articles)
        .where({ "isRead": false })
        .map(function (a) {
            return {
                'title': a.title,
                'author': a.author,
                'shortDescription': a.description.substring(0, 157) + "..."
            }
        })
        .sortBy("author").value();
};

LINQ:

public List<ArticlePreview> GetReadingList(List<Article> articles)
   {
        return articles.Where(article => !article.IsRead)
            .Select(article => new ArticlePreview()
                {
                    Title = article.Title,
                    Author = article.Author,
                    SortDescription = article.Description.Length > 157 
                      ? article.Description.Remove(157) + "..." : article.Description
                })
            .OrderBy(item => item.Author).ToList();
    }

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.

Edward Charbeneau

Author profile:

As a Corporate Web Developer/Webmaster with Sypris Solutions, Inc., Ed Charbeneau designs and develops internal web based applications for business, manufacturing and systems integration. Ed’s experience includes all aspects of web development from programming, database and ORM, to graphics design, and advertising/analytics. His skills includes C#, Asp.Net/MVC, HTML/CSS, Photoshop, JavaScript as well as web advertising and analytics.

Search for other articles by Edward Charbeneau

Rate this article:   Avg rating: from a total of 17 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: Helpful article
Posted by: JoNSmith (view profile)
Posted on: Sunday, August 25, 2013 at 11:57 AM
Message: Thanks Edward. As a hard core c# programmer who has embraced JavaScript for web apps I have already found underscore's library really useful. Your article has helped me to see more options that I had missed. Thanks.

Subject: Does what it says on the tin
Posted by: roundand (view profile)
Posted on: Wednesday, August 28, 2013 at 3:02 AM
Message: Good content, well explained.

As an old-time server-side JavaScript programmer coming up to speed with current libraries and frameworks this has helped me realise why I should invest some time and attention in adopting underscore.

(BTW, should "differed" be "deferred"?)

Subject: @roundand
Posted by: Dave Convery (view profile)
Posted on: Wednesday, August 28, 2013 at 3:30 AM
Message: Good spot on the typo - that's fixed now.

Subject: Thank you
Posted by: EdCharbeneau (view profile)
Posted on: Thursday, August 29, 2013 at 11:22 AM
Message: @JoNSmith & @roundand Thanks for the feedback.

 

Top Rated

Acceptance Testing with FitNesse: Multiplicities and Comparisons
 FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style... Read more...

Acceptance Testing with FitNesse: Symbols, Variables and Code-behind Styles
 Although FitNesse can be used as a generic automated testing tool for both applications and databases,... Read more...

Acceptance Testing with FitNesse: Documentation and Infrastructure
 FitNesse is a popular general-purpose wiki-based framework for writing acceptance tests for software... Read more...

TortoiseSVN and Subversion Cookbook Part 11: Subversion and Oracle
 It is only recently that the tools have existed to make source-control easy for database developers.... Read more...

TortoiseSVN and Subversion Cookbook Part 10: Extending the reach of Subversion
 Subversion provides a good way of source-controlling a database, but many operations are best done from... Read more...

Most Viewed

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Calling Cross Domain Web Services in AJAX
 The latest craze for mashups involves making cross-domain calls to Web Services from APIs made publicly... Read more...

Web Parts in ASP.NET 2.0
 Most Web Parts implementations allow users to create a single portal page where they can personalize... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.