How to Implement LINQ methods in JavaScript - Part 1
Photo by Simon Migaj on Unsplash LINQ has been around since 2007. It made the code more readable as you specify what you do with a collection not how to process it.JavaScript is the de facto standard so you should be working with it often if you are doing web development.
I will share some "approximate" JavaScript equivalents below for those working primarily with .NET but want to use the existing skill in JavaScript.
Since each method do not match exactly one to one functionality wise, I will drop "approximate" hereafter.
Here are the methods covered so far.
- Part 1 〰️ Select, Aggregate, Where, OrderBy (Ascending, Descending)
- Part 2 〰️ Any, Distinct, Concat, SelectMany
- Part 3 〰️ Reverse, Zip, Min/Max
- Part 4 〰️ Union, Intersect, Except
- Part 5 〰️ Sum, Average, Count
- Part 6 〰️ First, Last, DefaultIfEmpty, Skip, Take
- Part 7 〰️ Empty, Repeat, Range
- Part 8 〰️ All, Contains, SequenceEqual
🔴 Overview
Here are the examples I will show you in this article. [table id=1]
I tried to match the code look similar to each other (as you can see the full source codes in LINQ & JavaScript on GitHub).
Here are the sample collections I will use to demo each method.
CSharp
private static List<Order> Orders = new List<Order>{ | |
new Order(id: 1, quantity: 40, orderDate: new DateTime(2018, 1,1,1,1,1,1)), | |
new Order(id: 2, quantity: 20, orderDate: new DateTime(2018, 2,2,2,2,2,2)), | |
new Order(id: 3, quantity: 30, orderDate: new DateTime(2018, 3,3,3,3,3,3)), | |
new Order(id: 4, quantity: 10, orderDate: new DateTime(2018, 4,4,4,4,4,4)), | |
new Order(id: 5, quantity: 20, orderDate: new DateTime(2018, 5,5,5,5,5,5)), | |
}; |
JavaScript
const orders = [ | |
{ id: 1, quantity: 40, orderDate: new Date(2018, 1, 1, 1, 1, 1) }, | |
{ id: 2, quantity: 20, orderDate: new Date(2018, 2, 2, 2, 2, 2) }, | |
{ id: 3, quantity: 30, orderDate: new Date(2018, 3, 3, 3, 3, 3) }, | |
{ id: 4, quantity: 10, orderDate: new Date(2018, 4, 4, 4, 4, 4) }, | |
{ id: 5, quantity: 20, orderDate: new Date(2018, 5, 5, 5, 5, 5) } | |
]; |
🔴 Examples
🔸 Select
map
is the equivalent of Select
.
private static void SelectDemo(List<Order> orders) | |
{ | |
var quantities = orders.Select(order => order.Quantity); | |
quantities.ToList().ForEach(quantity => System.Console.WriteLine($"Quantity: {quantity}")); | |
} |
function mapDemo(orders) { | |
const quantities = orders.map(order => order.quantity); | |
quantities.forEach(quantity => console.log(`Quantity: ${quantity}`)); | |
} |
Syntax-wise, it's basically one-to-one replacement from Select
to map
. I said "equivalent" but I lied. While map
returns a new array, Select
has a side effect (it can update the iterating collection). Just remember this difference for the rest of JavaScript methods.
Results of calling SelectDemo
and mapDemo
.
Quantity: 40 | |
Quantity: 20 | |
Quantity: 30 | |
Quantity: 10 | |
Quantity: 20 |
🔸 Aggregate
reduce
works the same way Aggregate
does.
private static void AggregateDemo(List<Order> orders) | |
{ | |
const int initialQuantity = 0; | |
var totalQuantities = orders.Aggregate(initialQuantity, (sum, order) => sum + order.Quantity); | |
// Same as Order simply use a convinient `Sum()` method. | |
// var totalQuantities = orders.Sum(order => order.Quantity); | |
System.Console.WriteLine($"Total Quantities: {totalQuantities}"); | |
} |
function reduceDemo(orders) { | |
const initialQuantity = 0; | |
const totalQuantities = orders.reduce((sum, order) => sum + order.quantity, initialQuantity); | |
console.log(`Total Quantities: ${totalQuantities}`); | |
} |
⚡ NOTE ⚡: While the initial value for the accumulator is passed as a first argument in Aggregate
, it's passed as the last one in reduce
.
Results of calling AggregateDemo
and reduceDemo
.
Total Quantities: 120 |
🔸 Where
As you might have guessed it 😉, Where
is for filtering records in a collection. So the equivalent is..., 🎉 filter
.
private static void WhereDemo(List<Order> orders) | |
{ | |
var ordersWithQuantityOver30 = orders.Where(order => order.Quantity > 30); | |
PrintOrders(ordersWithQuantityOver30); | |
} |
function filterDemo(orders) { | |
const ordersWithQuantityOver30 = orders.filter(order => order.quantity > 30); | |
printOrders(ordersWithQuantityOver30); | |
} |
Another drop-in replacement of filter
for Where
. The code so far looked the same thanks to the Lamda expression syntax in both C# & JavaScript (ES6).
Results of calling WhereDemo
and filterDemo
.
WhereDemo(Orders); | |
// returns | |
// Order ID: 1, Quantity: 40, Order Date: 01 Jan 2018 01:01 AM pst | |
filterDemo(orders); | |
// returns | |
// Order ID: 1, Quantity: 40, Order Date: Thu Feb 01 2018 01:01:01 GMT-0500 (Eastern Standard Time) |
🔸 OrderBy
Now here is where it gets tricky.
While there are two methods for ordering (ascending/descending) in LINQ, there is only one method in Javascript, sort
, which can handle both scenarios.
sort
is more roughly equivalent to .NET's Array.Sort method which accepts an object instance of type IComparer<T>
, which works the same as a callback in sort
.
In the examples below, if the callback, which requires two arguments, returns a value less than 0, then the left value comes before right value, and vice versa. For 0, the order is not changed.
That's why you can emulate OrderBy
and OrderByDescending
with only sort
.
1. Ascending Order
private static void OrderByDemo(List<Order> orders) | |
{ | |
var orderedOrders = orders.OrderBy(order => order.Quantity); | |
PrintOrders(orderedOrders); | |
} |
function sortByAscendingDemo(orders) { | |
// Sorting works by comparing two values in a callback. | |
// When o1.quantity < 0, then o1 comes before o2. | |
// When o2.quantity < 0, then o2 comes before o1. | |
// When o1.quantity === o2.quantity then no change is needed | |
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort | |
var oderedOrders = orders.sort((o1, o2) => o1.quantity - o2.quantity); | |
printOrders(oderedOrders); | |
} |
In sortByAscendingDemo
, if o1.quantity - o2.quantity
returns a negative value, then o1
comes before o2
and vice versa. If quantity in both objects are equal (the difference is 0) then the order is not changed.
Results of calling OrderByDemo
and sortByAscendingDemo
.
OrderByDemo(Orders); | |
/* Returns | |
Order ID: 4, Quantity: 10, Order Date: 04 Apr 2018 04:04 AM pst | |
Order ID: 2, Quantity: 20, Order Date: 02 Feb 2018 02:02 AM pst | |
Order ID: 5, Quantity: 20, Order Date: 05 May 2018 05:05 AM pst | |
Order ID: 3, Quantity: 30, Order Date: 03 Mar 2018 03:03 AM pst | |
Order ID: 1, Quantity: 40, Order Date: 01 Jan 2018 01:01 AM pst | |
*/ | |
sortByAscending(orders); | |
/* Returns | |
Order ID: 4, Quantity: 10, Order Date: Fri May 04 2018 04:04:04 GMT-0400 (Eastern Daylight Time) | |
Order ID: 2, Quantity: 20, Order Date: Fri Mar 02 2018 02:02:02 GMT-0500 (Eastern Standard Time) | |
Order ID: 5, Quantity: 20, Order Date: Tue Jun 05 2018 05:05:05 GMT-0400 (Eastern Daylight Time) | |
Order ID: 3, Quantity: 30, Order Date: Tue Apr 03 2018 03:03:03 GMT-0400 (Eastern Daylight Time) | |
Order ID: 1, Quantity: 40, Order Date: Thu Feb 01 2018 01:01:01 GMT-0500 (Eastern Standard Time) | |
*/ |
2. Descending Order
private static void OrderByDescendingDemo(List<Order> orders) | |
{ | |
var orderedOrders = orders.OrderByDescending(order => order.Quantity); | |
PrintOrders(orderedOrders); | |
} |
function sortByDescendingDemo(orders) { | |
// Switching o1 & o2 around has the same effect as sorting in descending order | |
var oderedOrders = orders.sort((o1, o2) => o2.quantity - o1.quantity); | |
printOrders(oderedOrders); | |
} |
In this JavaScript function, sortByDescendingDemo
, two objects are switched from sortByAscendingDemo
. To emulate the descending order, we simply switch o1
and o2
, which will negate the return values from the callback.
Result of OrderByDescendingDemo
and sortByDescendingDemo
.
OrderByDescendingDemo(Orders); | |
/* Returns | |
Order ID: 1, Quantity: 41, Order Date: 01 Jan 2018 01:01 AM pst | |
Order ID: 3, Quantity: 31, Order Date: 03 Mar 2018 03:03 AM pst | |
Order ID: 2, Quantity: 21, Order Date: 02 Feb 2018 02:02 AM pst | |
Order ID: 5, Quantity: 21, Order Date: 05 May 2018 05:05 AM pst | |
Order ID: 4, Quantity: 11, Order Date: 04 Apr 2018 04:04 AM pst | |
*/ | |
sortByDescendingDemo(orders); | |
/* Returns | |
Order ID: 1, Quantity: 40, Order Date: Thu Feb 01 2018 01:01:01 GMT-0500 (Eastern Standard Time) | |
Order ID: 3, Quantity: 30, Order Date: Tue Apr 03 2018 03:03:03 GMT-0400 (Eastern Daylight Time) | |
Order ID: 2, Quantity: 20, Order Date: Fri Mar 02 2018 02:02:02 GMT-0500 (Eastern Standard Time) | |
Order ID: 5, Quantity: 20, Order Date: Tue Jun 05 2018 05:05:05 GMT-0400 (Eastern Daylight Time) | |
Order ID: 4, Quantity: 10, Order Date: Fri May 04 2018 04:04:04 GMT-0400 (Eastern Daylight Time) | |
*/ |
🔴 Closing Remark
In this article, I've shown examples of each LINQ method (approximate) equivalents in JavaScript.
Please feel free to leave a feedback on errors you might have spotted 😎.
The full source code and instructions on how to run them are on GitHub.