How to Implement LINQ methods in JavaScript - Part 2
Photo by Daniil Silantev on Unsplash
In the previous post, I've covered most used LINQ methods and implemented them in JavaScript.
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
In this post, I will cover following methods.
[table id=2 /]
And I will try to stick to using Vanilla JS.
The sample collection used in this part is the same as the previous one but I will list them again.
C#
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) } | |
]; |
⚡ NOTE ⚡ : In all of examples, WriteLine
is used for printing result in console in both C# & JavaScript codes to make the code difference a bit easier to see.
In C#, it's statically imported as using static System.Console
. In JavaScript, it's an alias of console.log
declared as const WriteLine = console.log
.
🔴 Examples
🔸 Any
some is equivalent to Any in LINQ. They do the same thing and both LINQ & JavaScript might as well have an alias to both "some" and "any" as TSQL does.
private static void AnyDemo(List<Order> orders) | |
{ | |
var ordersMoreThanEqualToQuantity30Exists = orders.Any(order => order.Quantity >= 30); | |
WriteLine($"Are there orders with quantity great than and equal to 30? {ordersMoreThanEqualToQuantity30Exists}"); | |
var ordersBeforeYear2018 = orders.Any(order => order.OrderDate.Year < 2018); | |
WriteLine($"Are there orders ordered before 2018? {ordersBeforeYear2018}"); | |
var ordersWithIDGreaterThan100 = orders.Any(order => order.Id > 100); | |
WriteLine($"Do we have more than 100 Orders? {ordersWithIDGreaterThan100}"); | |
} |
function someDemo(orders) { | |
const ordersMoreThanEqualToQuantity30Exists = orders.some( | |
order => order.quantity >= 30 | |
); | |
WriteLine( | |
`Are there orders with quantity great than and equal to 30? ${ordersMoreThanEqualToQuantity30Exists}` | |
); | |
const ordersBeforeYear2018 = orders.some( | |
order => order.orderDate.getFullYear() < 2018 | |
); | |
WriteLine(`Are there orders ordered before 2018? ${ordersBeforeYear2018}`); | |
const ordersWithIDGreaterThan100 = orders.some(order => order.id > 100); | |
WriteLine(`Do we have more than 100 Orders? ${ordersWithIDGreaterThan100}`); | |
} |
Results
// result of "AnyDemo" | |
Are there orders with quantity great than and equal to 30? True | |
Are there orders ordered before 2018? False | |
Do we have more than 100 Orders? False | |
// result of "someDemo" | |
Are there orders with quantity great than and equal to 30? true | |
Are there orders ordered before 2018? false | |
Do we have more than 100 Orders? false |
The only differences in JavaScript code are:
some
is used instead ofAny
Year
is retrieved with a method call ,getFullyear()
🔸 Distinct
There are some equivalent methods in jQuery (unique) or in Lodash (uniqby) but I will show you two implementations in Vanilla JS.
private static void DistinctDemo(List<Order> orders) | |
{ | |
var distinctQuantityOrders = orders.Select(order => order.Quantity).Distinct(); | |
distinctQuantityOrders.ToList().ForEach(quantity => WriteLine($"Distinct Quantity: {quantity}")); | |
} |
function distinctDemo1(orders) { | |
const distinctQuantityOrders = orders | |
.map(order => order.quantity) | |
.filter((quantity, index, self) => self.indexOf(quantity) === index); | |
distinctQuantityOrders.forEach(quantity => | |
WriteLine(`Distinct Quantity: ${quantity}`) | |
); | |
} | |
/** | |
* An alternate method of implementing Distinct() LINQ method using new "Set" introduced in ES6 and the spread operator | |
*/ | |
function distinctDemo2(orders) { | |
const distinctQuantityOrders = [ | |
...new Set(orders.map(order => order.quantity)) | |
]; | |
distinctQuantityOrders.forEach(quantity => | |
WriteLine(`Distinct Quantity: ${quantity}`) | |
); | |
} |
Results
The results for all three methods are the same.
Distinct Quantity: 40 | |
Distinct Quantity: 20 | |
Distinct Quantity: 30 | |
Distinct Quantity: 10 |
distinctDemo1
in JavaScript code uses filter
to filter out records that does not show up as the first element in the list (Please refer to this StackOverflow question for more information for implementation details).
distinctDemo2
uses a Set object (sets by definition stores only unique values and is available from ES6) to store unique values in it, and uses a spread syntax to convert the Set
object instance to an array (You could use another new ES6 addition, Array.from if you think it's not readable enough, as shown below).
const distinctQuantityOrders = [...new Set(orders.map(order => order.quantity))]; | |
const distinctQuantityOrders = Array.from(new Set(orders.map(order => order.quantity))); |
🔸 Concat
Thankfully 🙏, JavaScript has a method named concat, which does what LINQ version does. ⚠️ WARNING: Beware of super contrived example used in this demo.
private static void ConcatDemo(List<Order> orders) | |
{ | |
var firstOrder = orders.Take(1); | |
var lastOrder = orders.TakeLast(1); | |
var firstAndLastOrders = firstOrder.Concat(lastOrder); | |
PrintOrders(firstAndLastOrders); | |
} |
function concatDemo(orders) { | |
const firstOrder = [orders[0]]; | |
const lastOrder = [[...orders].pop()]; | |
const firstAndLastOrders = firstOrder.concat(lastOrder); | |
printOrders(firstAndLastOrders); | |
} |
Results
Order ID: 1, Quantity: 40, Order Date: Thu Feb 01 2018 01:01:01 GMT-0500 (Eastern Standard Time) | |
Order ID: 5, Quantity: 20, Order Date: Tue Jun 05 2018 05:05:05 GMT-0400 (Eastern Daylight Time) |
Syntax is exactly the same 😄(except capitalization), so moving right along to the next example⤵️, SelectMany.
🔸 SelectMany
This example does exactly the same thing (and just as contrived) as Concat does but wanted to share a different way you can implement it in JavaScript.
private static void SelectManyDemo(List<Order> orders) | |
{ | |
var firstOrder = orders.Take(1); | |
var lastOrder = orders.TakeLast(1); | |
var firstAndLastOrders = new [] {firstOrder, lastOrder}.SelectMany(order => order); | |
PrintOrders(firstAndLastOrders); | |
} |
function spreadDemo(orders) { | |
const firstOrder = [orders[0]]; | |
const lastOrder = [[...orders].pop()]; | |
// spread operator "..." on each array does the same thing as "SelectMany" in LINQ. | |
const firstAndLastOrders = [...firstOrder, ...lastOrder]; | |
printOrders(firstAndLastOrders); | |
} |
Results
As you can see, the result is exactly the same as the one in Concat demo.
Order ID: 1, Quantity: 40, Order Date: Thu Feb 01 2018 01:01:01 GMT-0500 (Eastern Standard Time) | |
Order ID: 5, Quantity: 20, Order Date: Tue Jun 05 2018 05:05:05 GMT-0400 (Eastern Daylight Time) |
SelectMany
basically flattens multiple collections into a single one, while Spread syntax
in ES6 is used to flatten all arrays into a single one.
🔴 Closing Remark
I have selected frequently used LINQ methods (at least for me that is) and shown you the JavaScript implementations. I hope you found the mapping between LINQ to JavaScript code useful. Please let me know should you find any errors or improvements I can make to the codes.
The full source code and instructions on how to run them are on GitHub. (Same as the first part as demos are added onto existing source code)
Webmentions
Loading counts...