From 549ccca5bf0daa742b688f27e60d5c874a4aeb65 Mon Sep 17 00:00:00 2001 From: Martin Chloride Date: Sun, 9 May 2021 03:47:23 -0700 Subject: [PATCH] Update README with API difference --- README.md | 118 +++++++++++++++++++++++++++++++++++++++++++++------ TODO | 8 ++-- package.json | 2 +- 3 files changed, 110 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 26edd48..f3169e6 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# ESLINQ - LINQ for ES2018+ +# ESLINQ - LINQ for ES2018 ## Abstract -This library provides a set of functional programming APIs with deferred execution, copied from [LINQ](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/) from the .NET world. These predefined functional programming APIs allows you to manipulate an [Iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) (available since ES2015) or an [AsyncIterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator) (available since ES2018) in an easy and maintainable shape. +This library provides a set of functional programming APIs with deferred execution, migrated from [LINQ](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/) from the .NET world. These predefined functional programming APIs allows you to manipulate an [Iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) (available since ES2015) or an [AsyncIterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator) (available since ES2018) efficiently in an easy and maintainable shape. -This library attempts to provide exact the same API defined in [Enumerable](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable) of .NET with TypeScript types. On top of that, its attempts to provide consistent API experience between synchronous and asynchronous operations by leveraging TypeScript code generation. +This library attempts to provide exact the same API defined in [Enumerable](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable) of .NET with TypeScript types. On top of that, it attempts to provide consistent API experience between synchronous and asynchronous operations by leveraging TypeScript code generation. -There are still some missing or slightly different APIs from this port comparing to the original .NET implementation. That is either because some nature difference between JavaScript and .NET (such as lack of `default(T)`), or to avoid complicated method overloading. If you find missing methods or overloading signatures which do not land into any of those reasons, they will be supported in a future version of this library some day. +There are still a few missing or slightly different APIs from this port comparing to the original .NET implementation. Please refer to [Difference from .NET implementation](#difference-from-net-implementation) section for details. That is either because some nature difference between JavaScript and .NET (such as lack of `default(T)`), or to avoid complicated method overloading. If you find missing methods or overloading signatures which do not land into any of those reasons, they will be supported in a future version of this library some day. ## Usage @@ -15,7 +15,7 @@ Since this library is a ES2018 port of LINQ from .NET, please refer to [System.L ### Start using LINQ ```javascript -import { from } from "es-linq"; // Note: hasn't published to NPM yet! +import { from } from "es-linq"; console.log( from([-1, 0, 1, 2]) @@ -142,7 +142,7 @@ For example, a template implementation method, its generated asynchronous method ```typescript // Template implementation template class class EnumerableImplementationTemplate { - private async *where(predicate: (element: T) => AsyncOrSync): AsyncIterable { + public async *where(predicate: (element: T) => AsyncOrSync): WrapWithAsyncEnumerable { for await (const element of this.iterable) { if (await predicate(element)) { yield element; @@ -154,12 +154,12 @@ class EnumerableImplementationTemplate { // Generated asynchronous class class AsyncEnumerable { // A wrapper method will be generated - public const(predicate: (element: T) => AsyncOrSync): AsyncEnumerable { - return new AsyncEnumerable(this.constImpl(predicate)); + public where(predicate: (element: T) => AsyncOrSync): AsyncEnumerable { + return new AsyncEnumerable(this.whereImpl(predicate)); } // The template method will have a prefix "Impl" in its name - private async *constImpl(predicate: (element: T) => AsyncOrSync): AsyncIterable { + private async *whereImpl(predicate: (element: T) => AsyncOrSync): AsyncIterable { // Method body will be kept as is for await (const element of this.iterable) { if (await predicate(element)) { @@ -173,13 +173,13 @@ class AsyncEnumerable { class Enumerable { // A wrapper method will be generated // Asynchronous types will be removed from its signature or turned into its synchronous alternative - public const(predicate: (element: T) => boolean): Enumerable { - return new Enumerable(this.constImpl(predicate)); + public where(predicate: (element: T) => boolean): Enumerable { + return new Enumerable(this.whereImpl(predicate)); } // The template method will have a prefix "Impl" in its name // Asynchronous types will be removed from its signature or turned into its synchronous alternative - private *constImpl(predicate: (element: T) => boolean): Iterable { + private *whereImpl(predicate: (element: T) => boolean): Iterable { // Method body will *almost* be kept as is, except await keywords will be removed. for (const element of this.iterable) { if (predicate(element)) { @@ -190,5 +190,99 @@ class Enumerable { } ``` +## Difference from .NET implementation + +Missing methods due to lack of feature in JavaScript runtime: +```csharp +Cast(IEnumerable) // No runtime cast in JavaScript + +LongCount(IEnumerable) // No long in TypeScript +LongCount(IEnumerable, Func) // No long in TypeScript +``` + +Missing methods to be added later: +```csharp +ToLookup(IEnumerable, Func, Func) // Will add later +ToLookup(IEnumerable, Func) // Will add later +``` + +Missing overloads due to lack of feature in JavaScript runtime: +```csharp +Aggregate(IEnumerable, Func) // No `default(T)` in TypeScript + +Distinct(IEnumerable, IEqualityComparer) // JavaScript Set does not support comparers + +Except(IEnumerable, IEnumerable, IEqualityComparer) // JavaScript Set does not support comparers + +GroupBy(IEnumerable, Func, Func, Func,TResult>, IEqualityComparer) // JavaScript Map does not support comparers +GroupBy(IEnumerable, Func, Func, IEqualityComparer) // JavaScript Map does not support comparers +GroupBy(IEnumerable, Func, Func,TResult>, IEqualityComparer) // JavaScript Map does not support comparers +GroupBy(IEnumerable, Func, IEqualityComparer) // JavaScript Map does not support comparers + +GroupJoin(IEnumerable, IEnumerable, Func, Func, Func,TResult>, IEqualityComparer) // JavaScript Map does not support comparers + +Intersect(IEnumerable, IEnumerable, IEqualityComparer) // JavaScript Set does not support comparers + +Join(IEnumerable, IEnumerable, Func, Func, Func, IEqualityComparer) // JavaScript Set does not support comparers + +Max(IEnumerable) // No IComparable in JavaScript + +Min(IEnumerable) // No IComparable in JavaScript + +OfType(IEnumerable) // No generic type check method in JavaScript + +ToDictionary(IEnumerable, Func, Func, IEqualityComparer) // JavaScript Map does not support comparers +ToDictionary(IEnumerable, Func, IEqualityComparer) // JavaScript Map does not support comparers + +ToHashSet(IEnumerable, IEqualityComparer) // JavaScript Set does not support comparers + +ToList(IEnumerable) // Duplicate with ToArray in context of JavaScript + +ToLookup(IEnumerable, Func, Func, IEqualityComparer) // JavaScript Map does not support comparers +ToLookup(IEnumerable, Func, IEqualityComparer) // JavaScript Map does not support comparers + +Union(IEnumerable, IEnumerable, IEqualityComparer) // JavaScript Set does not support comparers +``` + +Missing overloads whose overload signature is too complicated to implement in JavaScript with other overloads: +```csharp +Aggregate(IEnumerable, TAccumulate, Func, Func) + +Average(IEnumerable, Func) + +GroupBy(IEnumerable, Func, Func,TResult>) + +Max(IEnumerable, Func) + +Min(IEnumerable, Func) + +Sum(IEnumerable, Func) + +``` + + +Methods and overloads who returns `undefined` instead of `default(T)` +```csharp +DefaultIfEmpty(IEnumerable) // Use undefinedIfEmpty instead + +ElementAtOrDefault(IEnumerable, Int32) // Use ElementAtOrUndefined instead + +FirstOrDefault(IEnumerable) // Use FirstOrUndefined instead +FirstOrDefault(IEnumerable, Func) // Use FirstOrUndefined instead + +LastOrDefault(IEnumerable) // Use LastOrUndefined instead +LastOrDefault(IEnumerable, Func) // Use LastOrUndefined instead + +SingleOrDefault(IEnumerable, Func) // Use SingleOrUndefined instead +``` + +Renamed methods: +```csharp +ToDictionary(IEnumerable, Func, Func) // Use toMap instead +ToDictionary(IEnumerable, Func) // Use toMap instead + +ToHashSet(IEnumerable) // Use toSet instead +``` + ## Prior art * https://github.com/balazsbotond/eslinq diff --git a/TODO b/TODO index 9d0410c..207c902 100644 --- a/TODO +++ b/TODO @@ -1,12 +1,10 @@ -P0 -- Document unsupported methods and overrides - P1 - Support method extension -- Support Lookup - Generate code with JSDoc - Performance warning for OrderBy with AsyncEnumerable and synchronous comparer -- Optimization for SortedEnumerable +- Support Lookup + +P2 - More validation and exceptions - Support full Aggregate, GroupBy signature set - Support comparer for set operations diff --git a/package.json b/package.json index 2e09fb4..5db23db 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "eslinq", + "name": "es-linq", "version": "1.0.0", "description": "LINQ for ES2018+", "author": "Martin Chloride ",