← Go Back

How to Implement LINQ methods in JavaScript - Part 3

Broken Post?Let me know

Photo by Chris Lawton on Unsplash

This is the 3rd part of the series. Here are the methods covered so far.

  1. Part 1 〰️ Select, Aggregate, Where, OrderBy (Ascending, Descending)
  2. Part 2 〰️ Any, Distinct, Concat, SelectMany
  3. Part 3 〰️ Reverse, Zip, Min/Max
  4. Part 4 〰️ Union, Intersect, Except
  5. Part 5 〰️ Sum, Average, Count
  6. Part 6 〰️ First, Last, DefaultIfEmpty, Skip, Take
  7. Part 7 〰️ Empty, Repeat, Range
  8. Part 8 〰️ All, Contains, SequenceEqual

🔴 Overview

Here are the methods covered.

[table id=3 /]

And I will try to stick to using Vanilla JS as I have so far.

The sample collection used in this part is the same as all previous ones but I will list 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)),
};
view raw Orders.cs hosted with ❤ by GitHub

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) }
];
view raw orders.js hosted with ❤ by GitHub

OK, now let's dive into some examples 👍.

🔴 Examples

🔸 Reverse

Ah yes. Yet again, there is another JavaScript method named same as in LINQ, reverse. ⚡ NOTE ⚡: Both in LINQ & JavaScript, reverse has a side effect; it alters current collection and doesn't return a new array.

private static void ReverseDemo(List<Order> orders)
{
int mid = orders.Count / 2;
var leftHalf = orders.Take(mid);
var rightHalf = orders.Skip(mid);
var combinedOrders = rightHalf.Concat(leftHalf);
const int indentyBy = 4; // indent sub result
PrintHeaderFooter("Reversing from this list", () => PrintOrders(combinedOrders, indentyBy), indentyBy, '*');
var reversedOrders = combinedOrders.Reverse();
PrintOrders(reversedOrders);
}
view raw ReverseDemo.cs hosted with ❤ by GitHub

function reverseDemo(orders) {
const mid = orders.length / 2;
const leftHalf = orders.slice(0, mid);
const rightHalf = orders.slice(mid);
const combinedOrders = rightHalf.concat(leftHalf);
const indentBy = 4;
printHeaderFooter(
"Reversing from this list",
() => printOrders(combinedOrders, indentBy),
indentBy,
"*"
);
const reversedOrders = combinedOrders.reverse();
printOrders(reversedOrders);
}
view raw reverseDemo.js hosted with ❤ by GitHub

Results

// LINQ result
==================== Reverse DEMO - Reverse elements ====================
******************** Reversing from this list ********************
Order ID: 3, Quantity: 30, Order Date: 03 Mar 2018 03:03 AM pst
Order ID: 4, Quantity: 10, Order Date: 04 Apr 2018 04:04 AM pst
Order ID: 5, Quantity: 20, Order Date: 05 May 2018 05:05 AM pst
Order ID: 1, Quantity: 40, Order Date: 01 Jan 2018 01:01 AM pst
Order ID: 2, Quantity: 20, Order Date: 02 Feb 2018 02:02 AM pst
Order ID: 2, Quantity: 20, Order Date: 02 Feb 2018 02:02 AM pst
Order ID: 1, Quantity: 40, Order Date: 01 Jan 2018 01:01 AM pst
Order ID: 5, Quantity: 20, Order Date: 05 May 2018 05:05 AM pst
Order ID: 4, Quantity: 10, Order Date: 04 Apr 2018 04:04 AM pst
Order ID: 3, Quantity: 30, Order Date: 03 Mar 2018 03:03 AM pst
// JavaScript Result
==================== Reverse (LINQ 'Reverse' Equivalent) DEMO - Reverse current data source elements ====================
==================== Reversing from this list ====================
Order ID: 3, Quantity: 30, Order Date: Tue Apr 03 2018 03:03:03 GMT-0400 (Eastern Daylight Time)
Order ID: 4, Quantity: 10, Order Date: Fri May 04 2018 04:04:04 GMT-0400 (Eastern Daylight Time)
Order ID: 5, Quantity: 20, Order Date: Tue Jun 05 2018 05:05:05 GMT-0400 (Eastern Daylight Time)
Order ID: 1, Quantity: 40, Order Date: Thu Feb 01 2018 01:01:01 GMT-0500 (Eastern Standard Time)
Order ID: 2, Quantity: 20, Order Date: Fri Mar 02 2018 02:02:02 GMT-0500 (Eastern Standard Time)
Order ID: 2, Quantity: 20, Order Date: Fri Mar 02 2018 02:02:02 GMT-0500 (Eastern Standard Time)
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)
Order ID: 4, Quantity: 10, Order Date: Fri May 04 2018 04:04:04 GMT-0400 (Eastern Daylight Time)
Order ID: 3, Quantity: 30, Order Date: Tue Apr 03 2018 03:03:03 GMT-0400 (Eastern Daylight Time)
view raw reverse results hosted with ❤ by GitHub

Reverse is different from OrderByDescending that you don't specify which element to reverse with. It simply reverses the  current sequence in opposite order.

So to make the demo show how reverse works, I flipped first half and last half of the orders and reversed the list.

And now let's move onto the next example, Zip, which is you might use seldom.

🔸 Zip

There is no one-to-one equivalence in JavaScript but you can emulate one. Note that Lodash has zipWith method which works the same as the LINQ version (Maybe I will write LINQ -> Lodash equivalent methods as a next series 😉).

private static void ZipDemo(List<Order> orders)
{
var orderNumbersInText = new[] { "One", "Two", "Three", "Four", "Five" };
orders
.Zip(orderNumbersInText, (order, text) => $"Quantity of Order {text}: {order.Quantity}")
.ToList()
.ForEach(sentence => WriteLine(sentence));
}
view raw ZipDemo.cs hosted with ❤ by GitHub

function zipDemo(orders) {
const orderNumbersInText = ["One", "Two", "Three", "Four", "Five"];
orders
.map((order, index) => {
const text = orderNumbersInText[index];
return `Quantity of Order ${text}: ${order.quantity}`;
})
.forEach(sentence => WriteLine(sentence));
}
view raw zipDemo.js hosted with ❤ by GitHub

Results

// Same result in both LINQ and JavaScript
Quantity of Order One: 40
Quantity of Order Two: 20
Quantity of Order Three: 30
Quantity of Order Four: 10
Quantity of Order Five: 20

As Zip returns one element from each collection in order, we can emulate the functionality in JavaScript with map, which accepts a callback with an index. We can simply retrieve the matching second collection's item (const text = orderNumbersInText[index]) with the callback's index. Refer to a simpler case on this StackOverflow answer.

🔸 Min/Max

Although Lodash has a similar methods, minBy and maxBy that works the same way as LINQ, I will show you a way to implement it using Math.min and Math.max. (I've combined two methods since both min and max are called the same way)

private static void MinMaxDemo(List<Order> orders)
{
var minimumQuantity = orders.Min(order => order.Quantity);
var maximumQuantity = orders.Max(order => order.Quantity);
WriteLine($"Minimum Quantity: {minimumQuantity}");
WriteLine($"Maximum Quantity: {maximumQuantity}");
}
view raw MinMaxDemo.cs hosted with ❤ by GitHub

function minMaxDemo(orders) {
const minimumQuantity = Math.min(...orders.map(order => order.quantity));
const maximumQuantity = Math.max(...orders.map(order => order.quantity));
WriteLine(`Minimum Quantity: ${minimumQuantity}`);
WriteLine(`Maximum Quantity: ${maximumQuantity}`);
}
view raw minMaxDemo1.js hosted with ❤ by GitHub

Results

// Same for both LINQ & JavaScript versions
Minimum Quantity: 10
Maximum Quantity: 40
view raw Result of min, max hosted with ❤ by GitHub

You can use "Math.min/max" functions to emulate LINQ versions of Min/Max.

Notice that spread syntax (...orders) is used within Math.min/max functions because those functions accept a list of values, not an array, So the array destructuring is required.

If you use "min/max" frequently or make it look just like the LINQ version, you can simply extend the Array prototype as shown below.

Array.prototype.min = function(propertySelector) { return Math.min(...this.map(propertySelector)) };
Array.prototype.max = function(propertySelector) { return Math.max(...this.map(propertySelector)) };
function minMaxDemo(orders) {
const minimumQuantity = orders.min(order => order.quantity);
const maximumQuantity = orders.max(order => order.quantity);
WriteLine(`Minimum Quantity: ${minimumQuantity}`);
WriteLine(`Maximum Quantity: ${maximumQuantity}`);
}
view raw minMaxDemo2.js hosted with ❤ by GitHub

Now the it looks the same as the LINQ version.

🔴 Closing Remark

I've chosen some random LINQ methods this time but I will show you how to convert LINQ methods dealing with set operations (union, intersect, etc) in JavaScript.

The full source code and instructions on how to run them are on GitHub.