LINQ Equivalents in JavaScript
Move Seamlessly Between Languages
Originally posted on the dymaptic blog on June 16, 2023
When I'm working on GeoBlazor or other Blazor applications, I often have to switch between C# and JavaScript/TypeScript. While the two languages have a lot in common (C family syntax, async/await, lambda functions), one place I often get confused is when dealing with arrays or collections of items. In C#, the most straightforward way to do this is with LINQ queries. Most of these query methods work on any collection type, including Array, List, ReadOnlyList, HashSet, and the related interfaces. The root interface necessary is IEnumerable.
With ES6 and later versions, JavaScript has adopted many of these same approaches as methods on the Array class, using Array.prototype (prototype is the JavaScript concept of inheritance).

In this post, I am going to discuss all of the methods of IEnumerable in LINQ and their JavaScript equivalents. Some of these methods are very common, while others, such as the new .NET 6 ...By methods, are less well known. I'm also not going to deal with every possible parameter overload, as many LINQ methods allow for custom IComparer implementations or other optional parameters. In most cases, you will see that JavaScript has fewer methods than C#, but that they can be used in many of the same ways. It is also important to note that all IEnumerable methods return a new collection, and do not modify the original input collection. C# does have mutable collection methods, for example on the List class, such as Add, Remove, Sort, but these are kept distinct from the LINQ methods to make it clearer when you are mutating the original value. In JavaScript, both mutating and functional methods exist on Array.prototype. In fact, there is a recent collection of new methods that only return a new array, such as toSorted, toReversed, and toSpliced, but these are not yet implemented in all major browsers at the time of writing. In the comparison chart, I do use several mutating array methods, but only because there is no functional equivalent in JavaScript to compare to LINQ.
If you want to skip to the comparison table to look up a method, jump to the end of this post.
Creating a new Collection
In both languages, you can easily create a new collection. new List<T>() in C#, or [] in JavaScript, for example. C# also has the following convenience static methods on IEnumerable to generate a new collection.
C#
Empty- Creates a new, emptyIEnumerableinstance.Range- Creates a new collection of sequential intervals, with a given start index and count.Repeat- Generates a new collection with the same item repeated a givenCounttimes.
Finding a Single Record
C#
ElementAt- Finds the element at a particular index. Similar to using an indexer (e.g.,array[index]). Throws if the index is out of range.ElementAtOrDefault- LikeElementAtbut returnsdefault(nullfor nullable types) if the index is out of range.First- Finds the first item that matches the predicate. Throws on no match.FirstOrDefault- LikeFirstbut returnsdefaultif no match is found.Last- Finds the last item that matches the predicate. Throws on no match.LastOrDefault- LikeLastbut returnsdefaultif no match is found.Max- Returns the item with the highest numeric value.MaxBy- Returns the item with the highestKeyvalue according to the givenIComparer.Min- Returns the item with the lowest numeric value.MinBy- Returns the item with the lowestKeyvalue according to the givenIComparer.Single- LikeFirstbut also throws if multiple matches are found.SingleOrDefaultLikeFirstOrDefaultbut also throws if multiple matches are found.
As you can see, each method comes in two flavors, null-forgiving, and null-throwing. Like Nullable Reference Types, this is useful for explicitly declaring your intentions, and not accidentally returning a null where you really expected there to be a value. The Single methods also verify that you have exactly one match in your collection.
JavaScript
at- Finds the element at a particular index.find- Finds the first element that matches the predicate.findLast- Finds the last element that matches the predicate.
In JavaScript, we have fewer methods that return a single value. Unlike in C#, JavaScript never throws errors if it fails to find an item. Instead, all of these methods return an undefined value if a match is not found. There is also no equivalent of Single in JavaScript. Instead, you would have to filter and then throw if you found more than one match.
Finding or Filtering Multiple Records
C#
Distinct- Removes any duplicate entries and returns a collection in which each entry is unique.DistinctBy- Removes entries with duplicateKeyvalues.Except- Returns all items from the first collection that are not in the second collection.ExceptBy- Returns all items from the first collection that do not have a matchingKeyvalue in the second collection.Intersect- Compares two collections and returns all items that are present in both.IntersectBy- Compares two collections and returns all items that have matchingKeyvalues in both.OfType<T>- Filters the collection to only records of a particularType.Skip- Skips a specified number of records, and then returns the rest of the collection.SkipLast- Returns the collection minus the specified number of records at the end.SkipWhile- Skips forward over the collection until the predicate isfalse, and then returns the rest of the collection.Take- Returns the first specified number of elements in the collection.TakeLast- Returns the specified number of elements from the end of the collection.TakeWhile- Returns all elements from the start of the collection until the predicate isfalse.Where- Finds all matches to the predicate. If none found, returns an empty IEnumerable.
JavaScript
filter- Finds all matches to the predicate. If none found, returns an empty array.slice- Returns a sub-section of the collection by start index and optional end index.
Once again, C# has multipe methods that can filter a collection, whereas JavaScript has just the one. Yet you can easily filter on class type or the contents of a second array.
Sorting Records
C#
Order- Sorts the items by their default comparison in ascending order.OrderBy- Sorts the items by aKeyvalue in ascending order.OrderByDescending- Sorts the items by aKeyvalue in descending order.OrderDescending- Sorts the items by their default comparison in descending order.Reverse- Returns a collection in the opposite order from the original.ThenBy- Used to chain sorting calls with anyOrder,OrderByor otherThenBycall. Sorts by a newKeyascending.ThenByDescending- Used to chain sorting calls with anyOrder,OrderByor otherThenBycall. Sorts by a newKeydescending.
JavaScript
sort- Sorts the items by their default order or a comparison function. Mutates the original array.reverse- Returns the array in the opposite order from the original. Mutates the original array.
Combining or Adding to Collections
C#
Append- Adds a new item to the end of the collection.Concat- Adds a new collection to the end of the first collection.Join- Combines two collections, based on a definedKeyin each, and a custom function to join the two together.Prepend- Adds a new item to the beginning of the collection.Union- Combines two collections, excluding duplicates.UnionBy- Combines two collections, limiting eachKeyto a single instance.
JavaScript
concat- Adds a new array to the end of the first array. Does not alter the existing arrays.push- Adds a new item to the end of the array, and returns the newlength.unshift- Adds a new item to the beginning of the array, and returns the newlength.
Boolean Methods
These methods return a true or false depending on what is in the collection.
C#
All- Returnstrueif all items match the predicate.Any- Returnstrueif any item matches the predicate.Contains- Returnstrueif the item is in the collection.SequenceEqual- Compares each item in two collections, and returnstrueif they all match.
JavaScript
every- Returnstrueif all items match the predicate.includes- Returnstrueif the item is in the array.some- Returnstrueif any item matches the predicate.
Counting and Transforming Items
There are many transformations possible on a collection of items, especially on numeric types. These methods can be very powerful, and are in my opinion the hardest to keep straight in terms of different names. For example, Aggregate, and reduce seem like opposite terms, yet they are actually the same concept! Select and map are probably the most common transformation in each language, so it is important to know how to use them well.
C#
Aggregate- Applies anAccumulatorfunction to the collection, where each item and the accumulation are passed as parameters. Also supports giving a startingseedvalue.Average- Finds the numeric average of the collection values.Count- Returns the number of collection items as anint.LongCount- Returns the number of collection items as along.Select- Transforms each item in the collection via a custom function into a new value.SelectMany- Transforms each item in the collection into a new collection of values, which are then flattened into a single new collection.Sum- Adds all the values together for the collection.TryGetNonEnumeratedCount- Attempts to count the items in the collection without actually enumerating the items. Returns a boolean to indicate success, and has anoutparameter with the count value.Zip- Uses a custom function to combine each item in two collections together by index.
JavaScript
flatMap- Transforms each item in the collection into a new array of values, which are then flattened into a single array.length- Returns the number of items in the array.map- Transforms each item in the array via a custom function into a new value.reduce- Applies anaccumulatorfunction to the array, where each item and the accumulation are passed as parameters. Also supports aninitialValue.
C#-Only Type Transformations
These methods do not have a JavaScript equivalent because JS is a loosely-typed language. Instead, you can simply treat one type like another, and if it has the correct properties and methods, it will work. In C#, not only do you need to cast to the appropriate collection type for some usages, but since many LINQ methods use deferred execution, this also forces the query to actually run and produce an in-memory collection.
AsEnumerable- Returns the collection as anIEnumerable<T>.Cast<T>- Returns the collection with each item cast to theTvalue in anIEnumerable<T>.DefaultIfEmpty- Returns the original collection, or if the collection was empty, returns a new collection with a single, default value (e.g.,null).ToArray- Returns a fixed-length array.ToDictionary- Transforms a simple collection into a Key/Value pair Dictionary.ToHashSet- Returns a collection with no duplicates, similar toDistinct, but theHashSettype prevents adding duplicates in the future as well.ToList- Returns a mutable list that can be added to or removed from.ToLookup- Groups the collection underkeylookup values.
C#-Only Grouping Records
Chunk- Creates a collection of arrays, of a fixed maximum size, from the original collection.GroupBy- Creates a collection ofIGroupingelements, each of which is a collection of items from the original collection grouped by a predicate.GroupJoin- Creates a new collection, normally with a differentType, that contains the joined results of two collections.
Comparison Chart
In the following chart, I aimed for a 1-1 comparison whenever possible. If there was no JavaScript Array method, I tried to find the most succinct way of achieving the same result in code.
C# LINQ IEnumerable Method
JavaScript Array.prototype Method
Aggregate((acc, x) => function, seed)
reduce((acc, x) => function, seed)
All(x => predicate)
every(x => predicate)
Any(x => predicate)
some(x => predicate)
Append(item)
push(item)
AsEnumerable()
N/A
Average()
reduce((acc, x) => acc + x) / array.length
Cast<T>()
N/A
Chunk(size)
no simple equivalent
Concat(otherIEnumerable)
concat(otherArray)
Contains(item)
includes(item)
Count()
length
DefaultIfEmpty()
some() ? array : undefined
Distinct()
[...new Set(array)] 2
DistinctBy(x => x.Key)
no simple equivalent
ElementAt(index)
at(index)
ElementAtOrDefault(index)
at(index)
Empty<T>() 1
[] 2
Except(otherIEnumerable)
filter(x => !otherArray.includes(x))
ExceptBy(other, x => x.Key)
no simple equivalent
First(x => predicate)
find(x => predicate)
FirstOrDefault(x => predicate)
find(x => predicate)
GroupBy(x => predicate)
no simple equivalent 3
GroupJoin(inner, o => o.Key, i => i.Key, func, comparer)
no simple equivalent
Intersect(other)
filter(x => otherArray.includes(x))
IntersectBy(other, x => x.Key, comparer)
no simple equivalent
Join(inner, o => o.Key, i => i.Key, func)
no simple equivalent
Last(x => predicate)
findLast(x => predicate)
LastOrDefault(x => predicate)
findLast(x => predicate)
LongCount()
length
Max()
Math.max(...array) 2
MaxBy(x => x.Key)
sort((a, b) => b.Key - a.Key)[0]
Min()
Math.min(...array) 2
MinBy(x => x.Key)
sort((a, b) => a.Key - b.Key)[0]
OfType<T>()
filter(x => x instanceof T)
Order()
sort() 4
OrderBy(x => x.Key)
sort((a, b) => a.key - b.key) 4
OrderByDescending(x => x.Key)
sort((a, b) => b.key - a.key) 4
OrderDescending()
sort((a, b) => b - a) 4
Prepend(item)
unshift(item) 4
Range(start, count) 1
[...Array(count + start).keys()].slice(start) 2
Repeat(item, count) 1
fill(item, startIndex, endIndex) 4
Reverse()
reverse() 4
Select(x => function)
map(x => function)
SelectMany(x => function)
flatMap(x => function)
SequenceEqual(otherIEnumerable)
no simple equivalent
Single(x => predicate)
filter(x => predicate); result.length > 1 ? throw error;
SingleOrDefault(x => predicate)
filter(x => predicate); result.length > 1 ? throw error;
Skip(count)
slice(count)
SkipLast(count)
slice(0, -count)
SkipWhile(x => predicate)
no simple equivalent
Sum()
reduce((acc, x) => acc + x)
Take(count)
slice(0, count)
TakeLast(count)
slice(-count)
TakeWhile(x => predicate)
no simple equivalent
ThenBy(x => x.Key)
no simple equivalent
ThenByDescending(x => x.Key)
no simple equivalent
ToArray()
N/A
ToDictionary(x => function, x => function)
reduce((acc, x, i) => acc[functionK] = functionV)
ToHashSet()
new Set(array) 2
ToList()
N/A
ToLookup(x => function, x => function)
no simple equivalent
TryGetNonEnumerated(out int count)
no simple equivalent
Where(x => predicate)
filter(x => predicate)
Union(otherIEnumerable)
[...new Set(array.concat(otherArray))]
UnionBy(other, x => x.Key)
no simple equivalent
Zip(other, (a, b) => function)
map((a, i) => functionWithOther[i])
1 - static method on Enumerable
2 - not a method on Array.prototype
3 - group method is in experimental stage
4 - mutates the original array
Conclusion
I hope this deep dive into LINQ functional methods and their JavaScript Array counterparts was useful. If you are a .NET Blazor developer or interested in GeoSpatial Information Systems (GIS), checkout GeoBlazor and the dymaptic blog for more content!
Tim Purdum