{ sung.codes }

by dance2die
Blog
← Go Back

How to Implement LINQ methods in JavaScript - Part 6

Broken Post?Let me know

Photo by Baher Khairy on Unsplash I will cover methods for getting subset of sequence.

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

In this article, I will cover following methods.

[table id=6 /]

The sample collections used in this part are shown as below.

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)),
};
private static Order NullOrder = new Order(-1, 0, DateTime.MinValue);
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) }
];
const nullOrder = {
id: -1,
quantity: 0,
orderDate: new Date(-8640000000000000)
};
view raw orders.js hosted with ❤ by GitHub

🔴 Examples

🔸 First/FirstOrDefault

private static void FirsDemo(List<Order> orders)
{
var firstOrderAfterMarch = orders.First(order => order.OrderDate >= march);
PrintHeaderFooter("First order after March", () => PrintOrder(firstOrderAfterMarch, indentBy), indentBy);
var firstOrderAfterSeptember = orders.FirstOrDefault(order => order.OrderDate >= september);
PrintHeaderFooter("First or Default order after September", () => PrintOrder(firstOrderAfterSeptember, indentBy), indentBy);
}
view raw FirsDemo.cs hosted with ❤ by GitHub

Array.prototype.first = function(propertySelector = obj => obj) {
return this.filter(propertySelector)[0];
};
function firstDemo(orders) {
const firstOrderAfterMarch = orders.first(order => order.orderDate >= march);
printHeaderFooter(
"First order after March",
() => printOrder(firstOrderAfterMarch, indentBy),
indentBy
);
const firstOrderAfterSeptember =
orders.first(order => order.OrderDate >= september) || null;
printHeaderFooter(
"First or Default order after September",
() => printOrder(firstOrderAfterSeptember, indentBy),
indentBy
);
}
view raw firstDemo.js hosted with ❤ by GitHub

Results

// C#
==================== First/FirstOrDefault DEMO - Get First Order ====================
==================== First order after March ====================
Order ID: 3, Quantity: 30, Order Date: 03 Mar 2018 03:03 AM pst
==================== First or Default order after September ====================
<NULL>
// JS
==================== Filter (LINQ 'First/FirstOrDefault' Equivalent) DEMO - Get First Order ====================
==================== First order after March ====================
Order ID: 3, Quantity: 30, Order Date: Tue Apr 03 2018 03:03:03 GMT-0400 (Eastern Daylight Time)
==================== First or Default order after September ====================
<NULL>
view raw first demo result hosted with ❤ by GitHub

I've just used "first" in JavaScript to implement "First/FirstOrDefault". JavaScript is dynamically typed so it wouldn't know what default value is unless you specify unlike in C#.

🔸 Last/LastOrDefault

private static void LastDemo(List<Order> orders)
{
var lastOrderAfterMarch = orders.Last(order => order.OrderDate >= march);
PrintHeaderFooter("Last order after March", () => PrintOrder(lastOrderAfterMarch, indentBy), indentBy);
var LastOrderAfterSeptember = orders.LastOrDefault(order => order.OrderDate >= september);
PrintHeaderFooter("Last or Default order after September", () => PrintOrder(LastOrderAfterSeptember, indentBy), indentBy);
}
view raw LastDemo.cs hosted with ❤ by GitHub

Array.prototype.last = function(propertySelector = obj => obj) {
const filtered = this.filter(propertySelector);
return filtered[filtered.length - 1];
};
function lastDemo(orders) {
const lastOrderAfterMarch = orders.last(order => order.orderDate >= march);
printHeaderFooter(
"Last order after March",
() => printOrder(lastOrderAfterMarch, indentBy),
indentBy
);
const lastOrderAfterSeptember =
orders.last(order => order.orderDate >= september) || null;
printHeaderFooter(
"Last or Default order after September",
() => printOrder(lastOrderAfterSeptember, indentBy),
indentBy
);
}
view raw lastDemo.js hosted with ❤ by GitHub

Results

// C#
==================== Last/LastOrDefault DEMO - Get Last Order ====================
==================== Last order after March ====================
Order ID: 5, Quantity: 20, Order Date: 05 May 2018 05:05 AM pst
==================== Last or Default order after September ====================
<NULL>
// JS
==================== Filter (LINQ 'Last/LastOrDefault' Equivalent) DEMO - Get Last Order ====================
==================== Last order after March ====================
Order ID: 5, Quantity: 20, Order Date: Tue Jun 05 2018 05:05:05 GMT-0400 (Eastern Daylight Time)
==================== Last or Default order after September ====================
<NULL>
view raw last demo result hosted with ❤ by GitHub

Same as "first", I used "last" in JavaScript to implement both "Last/LastOrDefault"

🔸 DefaultIfEmpty

private static void DefaultIfEmptyDemo(List<Order> orders)
{
var ordersAfterSeptember = orders.Where(order => order.OrderDate >= september).DefaultIfEmpty(NullOrder);
PrintOrders(ordersAfterSeptember);
}

function defaultIfEmptyDemo(orders) {
const filteredOrders = orders.filter(order => order.OrderDate >= september);
const ordersAfterSeptember =
filteredOrders.length === 0 ? [nullOrder] : filteredOrders;
printOrders(ordersAfterSeptember);
}

Results

// C#
==================== DefaultIfEmpty DEMO - Get Order or Default if Empty ====================
Order ID: -1, Quantity: 0, Order Date: 01 Jan 0001 12:00 AM pst
// JS
==================== DefaultIfEmpty DEMO - Get Order or Default if Empty ====================
Order ID: -1, Quantity: 0, Order Date: Mon Apr 19 -271821 20:00:00 GMT-0400 (Eastern Daylight Time)

In JavaScript, filter returns an array of size 0 if no record is returned. So I am checking for the size and return nullOrder if the length is 0.

🔸 Skip/SkipWhile

private static void SkipDemo(List<Order> orders)
{
var lastTwoOrders1 = orders.Skip(orders.Count - 2);
PrintHeaderFooter("Last Two Orders - Skip", () => PrintOrders(lastTwoOrders1, indentBy), indentBy);
var lastTwoOrders2 = orders.SkipWhile((order, index) => index < orders.Count - 2);
PrintHeaderFooter("Last Two Orders - SkipWhile", () => PrintOrders(lastTwoOrders2, indentBy), indentBy);
}
view raw SkipDemo.cs hosted with ❤ by GitHub

Array.prototype.skip = function(count) {
return this.filter((_, i) => i >= count);
};
Array.prototype.skipWhile = function(predicate) {
return this.filter((_, i) => !predicate(_, i));
};
function skipDemo(orders) {
const lastTwoOrders1 = orders.skip(orders.length - 2);
printHeaderFooter(
"Last Two Orders - Skip",
() => printOrders(lastTwoOrders1, indentBy),
indentBy
);
const lastTwoOrders2 = orders.skipWhile(
(order, index) => index < orders.length - 2
);
printHeaderFooter(
"Last Two Orders - SkipWhile",
() => printOrders(lastTwoOrders2, indentBy),
indentBy
);
}
view raw skipDemo.js hosted with ❤ by GitHub

Results

// C#
==================== Skip/SkipWhile DEMO - Skip Orders ====================
==================== Last Two Orders - Skip ====================
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
==================== Last Two Orders - SkipWhile ====================
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
// JS
==================== Skip/SkipWhile DEMO - Skip Orders ====================
==================== Last Two Orders - Skip ====================
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)
==================== Last Two Orders - SkipWhile ====================
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)
view raw Skip result hosted with ❤ by GitHub

The callback in Array.prototype.filter also has an access to current index being process. So the implementation for skip simply filters out records below the given count while skipWhile filters out records that do not match the predicate.

🔸 Take/TakeWhile

private static void TakeDemo(List<Order> orders)
{
var firstTwoOrders1 = orders.Take(2);
PrintHeaderFooter("First Two Orders - Take", () => PrintOrders(firstTwoOrders1, indentBy), indentBy);
var firstTwoOrders2 = orders.TakeWhile((order, index) => index <= 1);
PrintHeaderFooter("First Two Orders - TakeWhile", () => PrintOrders(firstTwoOrders2, indentBy), indentBy);
}
view raw TakeDemo.cs hosted with ❤ by GitHub

Array.prototype.take = function(count) {
return this.filter((_, i) => i < count);
};
Array.prototype.takeWhile = function(predicate) {
return this.filter((_, i) => predicate(_, i));
};
function takeDemo(orders) {
const firstTwoOrders1 = orders.take(2);
printHeaderFooter(
"First Two Orders - Take",
() => printOrders(firstTwoOrders1, indentBy),
indentBy
);
const firstTwoOrders2 = orders.takeWhile((order, index) => index <= 1);
printHeaderFooter(
"First Two Orders - TakeWhile",
() => printOrders(firstTwoOrders2, indentBy),
indentBy
);
}
view raw takeDemo.js hosted with ❤ by GitHub

Results

// C#
==================== Take/TakeWhile DEMO - Take Orders ====================
==================== First Two Orders - Take ====================
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
==================== First Two Orders - TakeWhile ====================
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
// JS
==================== Take/TakeWhile DEMO - Take Orders ====================
==================== First Two Orders - Take ====================
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)
==================== First Two Orders - TakeWhile ====================
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)
view raw gistfile1.txt hosted with ❤ by GitHub

Take is the opposite of skip so the boolean conditions are reversed.

🔴 Closing Remark

JavaScript is dynamically typed so returning a "default" value is tricky as a variable type is defined upon initialization. I've skipped implementing "OrDefault" methods for that reason.

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.