If you’re a developer in the .NET realm, you have undoubtedly learned to use and love LINQ for dealing with collections. If you haven’t, then what are you waiting for? In this age of AJAX powered sites, managing collections of JSON objects becomes a very common pattern. I found myself wanting to apply LINQ-like operations to those collections, and decided to see what was out there before writing my own.
Unsurprisingly, it turns out that others have addressed this need already, in various different flavors. One that caught my eye was JSLINQ, because it seemed like a pretty natural implementation. I admit that my research on this was pretty minimal, since after looking at a few libraries I decided to just try JSLINQ and run with it.
JSLINQ uses syntax that closely parallels what you would do in C# or VB.NET, and comes with a nice set of operators out of the box. For these examples, I will use the following data collection, borrowed (and simplified a little) from the JSLINQ examples:
var People =
[
{ ID: 1, FirstName: "Chris", LastName: "Pearson" },
{ ID: 2, FirstName: "Kate", LastName: "Johnson" },
{ ID: 3, FirstName: "Josh", LastName: "Sutherland" },
{ ID: 4, FirstName: "John", LastName: "Ronald" },
{ ID: 5, FirstName: "Steve", LastName: "Pinkerton" },
{ ID: 6, FirstName: "Katie", LastName: "Zimmerman" },
{ ID: 7, FirstName: "Dirk", LastName: "Anderson" },
{ ID: 8, FirstName: "Chris", LastName: "Stevenson" },
{ ID: 9, FirstName: "Bernard", LastName: "Sutherland" },
{ ID: 10, FirstName: "Kate", LastName: "Pinkerton" }
];
Also, I will define this helper function for using a JavaScript alert to show what the results of an expression are, by showing a concatenated string of the first names in the collection:
function testLinq(people)
{
alert(
people
.Select(function (person) { return person.FirstName; })
.ToArray()
.join(',')
);
}
To show an example using the existing operators, I’ll start things off with the Where and OrderBy operators. We are going to select all people who have a first name longer than 4 characters, and then order them alphabetically:
// Example 1: Where/OrderBy
testLinq(
JSLINQ(People)
.Where(function (person) { return person.FirstName.length > 4; })
.OrderBy(function (person) { return person.FirstName; })
);
Output:
Bernard,Chris,Chris,Katie,Steve
It gives you your normal LINQ look and feel in JavaScript, which is exactly what I wanted. If you look at the source, the code behind it is very simple, which illustrates the point that this is simply a syntactic sugar that lets you write much more expressive code. Isn’t that why we all use LINQ in the first place?
After playing with the default operators for a bit, I found myself wanting to extend them a bit with some new ones. I will demonstrate here how to extend JSLINQ to have LINQ operators for Take, TakeWhile, Skip, SkipWhile, and ForEach.
Take and TakeWhile
Take and TakeWhile seemed like good starting points for extending JSLINQ because they are very simple. Here is the code I came up with for Take:
JSLINQ.fn.Take = function(count)
{
var newArray = new Array();
for (var i = 0; i < count && i < this.items.length; i++)
{
newArray[newArray.length] = this.items[i];
}
return new JSLINQ(newArray);
};
Like I said, it’s very simple. It simply takes in an integer, and returns that many elements from the collection, starting from the beginning. Just like the .NET LINQ Take operator, if you pass in a value larger than the length of the collection, you will simply get the entire collection back. You can also see that it returns a JSLINQ object of the new array, to give you that nice fluent behavior.
// Example 2: Take
testLinq(
JSLINQ(People)
.OrderBy(function (person) { return person.FirstName; })
.Take(5)
);
Output:
Bernard,Chris,Chris,Dirk,John
With that implemented, it was only natural to implement the TakeWhile operator as well:
JSLINQ.fn.TakeWhile = function(clause)
{
for (var count = 0; count < this.items.length; count++)
{
var item = this.items[count];
if (clause.call(item, item) === false)
{
return this.Take(count);
}
}
return this;
};
Starting from the beginning of the collection, it will return items until the supplied clause fails. It calls out to the Take method to do the actual dirty work, since the behavior at that point is the same.
// Example 3: TakeWhile
testLinq(
JSLINQ(People)
.OrderBy(function (person) { return person.FirstName; })
.TakeWhile(function (person) { return person.FirstName.charAt(0) < 'J'; })
);
Output:
Bernard,Chris,Chris,Dirk
Skip and SkipWhile
Now that Take and TakeWhile are implemented, it only seems natural to do their counterparts, Take and TakeWhile. Again, it is modeled after the .NET operators, and the implementation is pretty similar to what we just finished writing so I’ll quickly provide the code and sample usage:
JSLINQ.fn.Skip = function(count)
{
var newArray = new Array();
for (var i = count; i < this.items.length; i++)
{
newArray[newArray.length] = this.items[i];
}
return new JSLINQ(newArray);
};
// Example 4: Skip
testLinq(
JSLINQ(People)
.OrderBy(function (person) { return person.FirstName; })
.Skip(5)
);
Output:
John,Josh,Kate,Kate,Katie,Steve
ForEach
Ready for something more interesting? Great, so am I. Let’s take a look at what it would take to implement a ForEach operator, which does precisely what it implies: iterates through a collection and applies an operation to each element in it. Taking inspiration from the jQuery each operator, the operator will terminate if the function being called on each element returns false. I also used jQuery as inspiration for the means of implementation as well, so I give them most of the credit here for the body of that function. My version is essentially a simplified version of theirs, tuned for this particular use case:
JSLINQ.fn.ForEach = function(callback)
{
var i = 0;
for (var value = this.items[0];
i < this.items.length && callback.call(value, value, i) !== false;
value = this.items[++i])
{
}
return this;
};
Starting at the beginning, the function supplied as the callback parameter will be called for each element, via the JavaScript call method. It supplies the callback function with both the current element as well as the index within the collection. It also tells the call method to set the context of that function call to be the current element, meaning that if the callback function references “this,” it will get the current element.
Now let’s take a look at how to use the ForEach operator. This time we’ll take each person in the collection, and set their first name to be their last name:
// Example 6: ForEach
testLinq(
JSLINQ(People)
.ForEach(function(person) { person.FirstName = person.LastName; })
);
Output:
Pearson,Johnson,Sutherland,Ronald,Pinkerton,Zimmerman,
Anderson,Stevenson,Sutherland,Pinkerton
There’s still plenty more that can be done as far as using LINQ in JavaScript but this shows that it’s actually pretty easy to use those approaches to improving how you interact with JavaScript collections.