Code Buckets

Buckets of code

.Net TypeScript

LINQ FirstOrDefault implementation in TypeScript

Proust looking of of the window thinking about LINQ operators

Confession: when working in JavaScript languages I kind of miss some C# things such as LINQ. A substantial part of my C# coding seemed to be chaining LINQ operators together. So rather than staring wistfully out of the window  in remembrance of things past – I thought I would just try to write one of my own. I’ve picked FirstOrDefault for this honour.

FirstOrDefault

For the uninitiated FirstOrDefault is a LINQ operator in C# that takes a function which resolves to a boolean – a predicate. It has the following characteristics

  1. Returns the first object in a collection that matches the predicate
  2. If it can’t find one then it will return null
  3. It won’t throw an exception if it can’t make the match – in contrast to it’s harsher sibling SingleOrDefault that will

TypeScript

I’m writing this In TypeScript – simply because that’s what I’m using at the moment. It would actually be easier to do this in plain JavaScript – the prototype will be the same and they would be no steps to hook it into the TypeScript interface. But we will do it in TypeScript

Implementation

Step 1: Extending Array Type

In C#, the LINQ operators are an extension of IEnumerable<T>. In TypeScript we will extend the Array object which is a similar principle. In Typescript type definitions go in .d.ts files. I’m working in Angular and there is handily a definition file already present – typings.d.ts. In that file put the following

interface Array<T> {
   firstOrDefault(predicate: Function): T;
}

This adds a firstOrDefault defintion to the Array type. It takes a function just like the C# first or default method. As we are using TypeScript we can specify a type of function for the parameter so we will get compile errors if we try to supply anything else to this method. Of course there is no implementation yet ….

Step 2: Implementing the LINQ operator

Create a file e.g. Linq.ts and enter the following

export module Linq {

Array.prototype.firstOrDefault = function(predicate: Function){
  return this.reduce( (accumulator, currentValue) => {

      if(!accumulator && predicate(currentValue)) 
        accumulator = currentValue;
      
      return accumulator;
    }, null);
  }
}

This method is going to do the work – it’s the meat and potatoes. We are leveraging the reduce operator. Reduce flattens a collection into a single value – a simple example would be totalling all the numeric values in an array i.e.

let total = this.reduce( (accumulator, currentValue) => accumulator + currentValue);

For us flattening the array means returning the first value that matches the predicate. So we iterate through until predicate(currentValue) evaluates to true then we stash the value in the accumulator object and once there we never write over it.

An interesting quirk to this is what happens on the first loop – if we call it like this

return this.reduce( (accumulator, currentValue) => {

  if(!accumulator && predicate(currentValue)) 
    accumulator = currentValue;
  return accumulator;
});

i.e. reduce with a single argument. In the first iteration the accumulator has the value of the first object in the array; the currentValue has the second. The first item is already stashed in the accumulator and the predicate is never checked – the first item is always returned no matter what the filter is. This is exactly what we don’t want. We need to supply a second argument to reduce …..

return this.reduce( (accumulator, currentValue) => {

  if(!accumulator && predicate(currentValue)) 
    accumulator = currentValue;
  return accumulator;
}, null);

By supplying a second argument we specify what the initial value of the accumulator is. We want a initial value of null so our LINQ operator will attempt the match each time until it succeeds.

As a side note – I’m aware that there are any number of ways to achieve this but I’ve a theory that all of the LINQ operators can be done with JavaScript reduce, map or filter methods so this is my first step to prove that theory out. It can be done and it will be done (if I get the time).

Step 3. Consuming the LINQ operator

Import the linq prototype and use thus

import('../utilities/linq');

let results = arrayUnderTest.firstOrDefault(x => x.Id === 1);

This returns the first object in the array with an Id of 1. This compares quite nicely with the C# FirstOrDefault method which is

var results = collectionUnderTest.FirstOrDefault(x => x.Id == 1);

Which looks pretty similar to me and works in the same way. It doesn’t do a great job with type checking though and since we are using TypeScript we can do a bit better.

Implementation with Generics

We can use generics in TypeScript to get type checking in the predicate and get a typed returned value. An improvement I think.

Step 1: Extending Array Type

Go to the d.ts file again and use this

type predicate<T> = (arg: T) => boolean;

Which defines a function that takes type T and returns a boolean – our filtering predicate.

interface Array<T> {
  firstOrDefault<T>(predicate: predicate<T>): T;
}

And a generic array extension which uses the typed predicate instead of the more generic Function type.

Step 2: Implementing the LINQ operator

Change the implementation to this

export module Linq {

  Array.prototype.firstOrDefault = function<T>(predicate: predicate<T>){
    return this.reduce((accumulator: T, currentValue: T) => {

      if(!accumulator && predicate(currentValue)) 
        accumulator = currentValue;
      
     return accumulator;
    }, null);
  }
}

Which uses the generic types throughout.

Step 3. Consuming the LINQ operator

If we were using a collection of persons, the new generic powered firstOrDefault would be called like this

let result: Person = persons.FirstOrDefault<Person>(x => x.Id === 1);

We can now get decent intellisense when filling in the filter conditions and type checking so we don’t compare the wrong types. It’s slightly more verbose than the first example but safer to use. I prefer it, but use whichever floats your TypeScript boat – both work.

Useful Links

https://codepunk.io/extending-strings-and-other-data-types-in-typescript/
More detail about extending built in types in TypeScript

https://danmartensen.svbtle.com/javascripts-map-reduce-and-filter
Nice comparison of the map, filter and reduce methods in JavaScript and guidance about which to use when.

https://www.tutorialspoint.com/linq/linq_query_operators.htm
Overview of LINQ operators. There’s more than you think.

https://stackoverflow.com/questions/7192040/linq-to-entities-vs-linq-to-objects-are-they-the-same
When I’m talking about LINQ I’m really talking about LINQ to objects. There is LINQ to SQL, LINQ to entities, LINQ to XML etc.. This SO question points out the difference.

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/query-syntax-and-method-syntax-in-linq
There are two styles of operators in LINQ – lambda and query syntax. Query syntax is

IEnumerable<int> numQuery1 = from num in numbers 
                             where num % 2 == 0 
                             orderby num s
                             elect num;

And the equivalent lamdba is

numQuery1.Where(num => num % 2 == 0).OrderBy(num => num);

I’m emulating the lamdba syntax here. I never use the query syntax – I think it was easy route to LINQ for people more familiar with SQL. The lamdba is a more functional approach and nearer to what the code is actually doing. Anyway the query operator was only ever a sub set of the lamdba.

2 COMMENTS

  1. Hello, thanks for this article.

    Apparently, the native Array.prototype.find() function could replace your firstOrDefault() – with the only (somewhat minor) difference that the former returns undefined in the event no entry matched the predicate while the latter returns null.

    let result: Person = persons.find(x => x.Id === 1);

    Cheers

LEAVE A RESPONSE

Your email address will not be published. Required fields are marked *