Comparing underscore.js with Linq

If you are .NET developer you are probably spoiled by how it is easy to manipulate collections using LINQ. However, if you are full stack developer and write a lot of Javascript you know that is not elegant to manipulate collections in JavaScript. It is possible to use TypeScriptand use something like: https://github.com/kutyel/linq.ts. However, topic of this post is not about TypeScript and linq.ts library. It is another library often used by Javascript developer and it is called underscore.js.

Looking familiar?

1

2

3

4

5

6

7

8

9

10

11

12

import{List}from'linqts';

let arr=newList<number>([1,2,3,4,5])

.Where(x=>x>3)

.Select(y=>y *2)

.ToArray();// > [8, 10]

let query=people.Join(pets,

person=>person,

pet=>pet.Owner,

(person,pet)=>

({OwnerName:person.Name,Pet:pet.Name}));

Underscore.js has over 100 helper function that will help you in your Javascript development.

Lets see some of those functions and also try to compare it with it’s .NET version.

FirstOrDefault()/Find

1

2

3

4

LINQ:

varuser=users.Where(x=>x.Name="John").FirstOrDefault();

underscore.js

varuser=_.find(users,function(user){returnuser.Name=="John"});

FirstOrDefault() returns null if there is no match otherwise returns first user that match criteria. Similary, _.find will work the same way returning undefined if there is no match.

There are different styles of writing this code, using LINQ and undercore.js.

It was possible to write LINQ version like this:

1

2

3

4

LINQ:

varuser=users.FirstOrDefault(x=>x.Name="John");

It asmather of personal preference butIfind that first one reads more naturally.

Underscore offers two different syntax styles, a functional syntax and an object-oriented syntax. Again, it is mather of personal preference. Example from above would look like this if is was written using object-oriented syntax.

1

varuser=_(users).find(function(user){returnuser.Name=="John"});

Using ES6 this would be ever shorter:

1

varuser=_(users).find(x=>x.Name=="John"});

Notice, how by combining of using object-oriented undercore.js syntax together with ES6 code resambles C# code and LINQ but in this articles we will stick to ES5 as ES6 is supported by new browsers only.

Where()/ _.filter

1

2

3

4

.NET

varuser=users.Where(x=>x.Name=="John").ToList();

underscore.js

varuser=_.filter(users,function(user){returnuser.Name=="John"});

Where and ._filter return all users that match criteria. If any user match criteria empty list will be returned in both LINQ and underscore.js.

ToList() is not just about converting collection to List. It forces immediate query execution, so if for example users are fetched from the database ToList() will actually send query to database and convert result to List.

Select/ _.map

Select in LINQ and _.select in undrescore are completely different. While Select in LINQ is used for projections _.select in underscore.js is used for filtering. In fact _.select is just alias for _.filter.

So example from above example could look likes this:

1

2

3

4

underscore.js

varuser=_.filter(users,function(user){returnuser.Name=="John"});

or

varuser=_.select(users,function(user){returnuser.Name=="John"});

Lets imagine each user has Id,Name,SurName properties and we want to make list of all ids (project the list to the new one that contains only Id property. We would use Select in .NET

1

2

3

4

.NET

varids=users.Select(x=>new{x.Id}).ToList();

underscore.js

varids=_.map(users,function(user){returnuser.Id});

In underscore.js _.map alias _.collect is used.

SelectMany()/_.flatten and _.pluck

Let’s imagine each user from examples above has Products property(of type List in C# and Array in JavaScript). To get list of all products of all users you can flatten list of users like this:

1

2

3

4

.NET

varallProducts=users.SelectMany(x=>x.Products).ToList();

underscore.js

varallProducts=_.flatten(_.pluck(users,'Products'));

To achieve some functionality that SelectMany has first idea was to use just _.flatten like so:

1

2

underscore.js

varnotworking=_.flatten(users,'Products'));//NOT POSSIBLE

This is not possible because you can’t tell _.flatten on which property to flatten on but there is one function that does exactly that – it’s called _.pluck. “Problem” with _.pluck in this case is that does not change original structure when we pluck users. We are still getting list of users but we “removed” all other properties.

1

2

underscore.js

varusersWithJustProductsProperty=_.pluck(users,'Products'));//LIST OF //USERS WITH JUST PRODUCTS PROPERTY

Idea is to now use _.flatten to flatten list of users and get list of products:

1

varallProducts=_.flatten(_.pluck(users,'Products'));

OrderBy/ _.sort

OrderBy in LINQ and _.sort in underscore.js work similar to each other. Both will return a copy of list sorted in ascending order. However, there is no underscore.js alternative to OrderByDescending but javascript’s reverse() method can be used to achieve similar result.

1

2

3

4

.NET

varsortedUsers=users.OrderBy(x=>x.Name);

underscore.js

varsortedUsers=_.sortBy(users,'Name');

To sort users by Name in descending order:

1

2

3

4

.NET

varsortedUsers=users.OrderByDescending(x=>x.Name);

underscore.js

varsortedUsers=_.sortBy(users,'Name').reverse();

ForEach/ _.each

ForEach and _.each are very similar. Both can perform an operation on each item in te list. They don’t returning anything.

1

2

3

4

.NET

users.ForEach(x=>x.Deleted=true);

underscore.js

_.each(users,function(user){user.Deleted=true});

Intersect/ _.intersection

Both methods return new list that contains all elements that exist in each individual list. For example:

_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);=> [1, 2]

1

2

3

4

.NET

varsharedUsers=users.Intersect(users2List);

underscore.js

varsharedUsers=_.intersection(users,users2List);

If we want to make intersection between multiple list syntax is then little bit different. Underscore.js accepts the list of lists while in LINQ we have to chain Intersect calls.

1

2

3

4

.NET

varsharedUsers=users.Intersect(users2List).Intersect(users3List);

underscore.js

varsharedUsers=_.intersection(users,users2List,users3List);

Except/ _.difference

Except and _.difference are opposite of Intesect and _.intersection. They return the values from array that are not present in the other arrays.

DistinctBy (not exist, but…)/ _.uniq

DistinctBy and _.uniq remove duplicates from the list. Unfortunatelly DistincyBy does not exist in LINQ although it is easy to write own extension method on List. Other solution would be to use more linq which has that method or combine other LINQ functions together:

1

2

3

4

List<User>distinctPeople=users

.GroupBy(p=>p.Id)

.Select(g=>g.First())

.ToList();

_.uniq works this way:

Produces a duplicate-free version of the array, using === to test object equality. In particular only the first occurence of each value is kept. If you know in advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iteratee function.

1

2

3

4

5

6

7

8

9

.NET

List<User>distinctPeople=users

.GroupBy(p=>p.Id)

.Select(g=>g.First())

.ToList();

vardistinctPeople=_.uniq(users,function(user,key,a){

returnuser.Id;

});

FindIndex/ _.indexOf

FindIndex searches for an element that matches the conditions defined by a specified predicate, and returns the zero-based index of the first occurrence within the List or a portion of it. This method returns -1 if an item that matches the conditions is not found. This method performs a linear search; therefore, this method is an O(n) operation, where n is Count. _.indexOf works similary and can also accept third argument of value true if we know in advance that list is sorted to use faster binary search. Similar to _.indexOf,_.findIndex returns the first index where the predicate truth test passes; otherwise returns -1.

1

2

3

4

5

6

.NET

varindex=users.FindIndex(x=>x.Name=="John");

varindex=_.findIndex(users,function(user){

returnuser.Name=="John";

});

Now when we know how _.pluck and ._indexOf work this can be achieved also by combining those two functions:

1

varindex=_.indexOf(_.pluck(users,'Name'),"John");

Aggregate/ _.reduce

_.reduce is similar to _.map except that instead of producing another functor _.reduce produces a single result that may be of any type. Let’s is it’s definition from undercore.js documentation:

Also known as inject and foldl, reduce boils down a list of values into a single value. Memo is the initial state of the reduction, and each successive step of it should be returned by iteratee. The iteratee is passed four arguments: the memo, then the value and index (or key) of the iteration, and finally a reference to the entire list.

C# version of _.reduce is Aggregate function. It is very similar to _.reduce in a way that first parametar of iterator function is memo and second is current item of the list.

1

2

3

4

5

6

.NET

vartotalPoints=users.Aggregate((memo,user)=>memo+user.Points);

vartotalPoints=_.reduce(users,function(memo,user){

returnmemo+user.Name;

},0);

There are more of LINQ and underscore.js operators and similarities between them but I think I covered methods that are most often used. As you can see there are lot of similarities between them and knowing LINQ will help you learn underscore.js faster.