darq 0.3.1

  • Readme
  • Changelog
  • Example
  • Installing
  • 65

pub package

A port of .NET's LINQ IEnumerable functions to Dart. This library adds a powerful Enumerable collection type to Dart that greatly increases convenience when modifying a collection as well as performance when dealing with large collections and complex modifications.

API Reference #

Lazy Execution #

The power of Enumerable is that it supports modification to an underlying collection using lazy execution. This means that the enumerable only stores the data necessary to perform the modification and does not actually perform the modification until the enumerable is iterated over. No data is cached either, so unless you actually iterate over the enumerable, creating one is virtually free from both a memory and performance standpoint.

What's more, when multiple enumerable modifications are chained together, the iteration is done through them all simultaneously. This greatly eases the overhead of performing complex modifications on large collection sets.

Usage #

An Enumerable can be created out of any Dart collection type that extends Iterable. There are two ways to do so:

var sourceList = [0, 1, 2, 3, 4];

// Factory method
var factoryEnum = Enumerable.from(sourceList);

// Convenience method
var convenientEnum = E(sourceList);

You can also generate an Enumerable without needing a pre-existing collection using one of several factory methods:

// Creates an Enumerable with no values
var emptyEnum = Enumerable.empty();

// Creates an Enumerable containing 5 integers starting with the number 2
var rangeEnum = Enumerable.range(2, 5);

// Creates an Enumerable that contains 6 copies of the value 'a string'
var repeatEnum = Enumerable.repeat('a string', 6);

// Creates an Enumerable from a string, iterating over its characters
var stringEnum = Enumerable.fromString('abcdef');

// Creates an Enumerable using a generator function
var generatedEnum = Enumerable.generate(5, (index) => (index * 2).toString());

Once you have an Enumerable, you can call any of 50 different methods on it to modify or analyze it. For example, you can map to a new value with selectE:

var myEnum = E([1, 2, 3]);
var mappedEnum = myEnum.selectE((i) => i * 2);
// Values: [2, 4, 6]

...filter the elements with whereE:

var myEnum = E([1, 2, 3]);
var filteredEnum = myEnum.whereE((i) => i.isOdd);
// Values: [1, 3]

...get only unique values with distinctE:

var myEnum = E([1, 1, 1, 2, 2, 3, 4, 5, 5, 5, 5, 5]);
var uniqueEnum = myEnum.distinctE();
// Values: [1, 2, 3, 4, 5]

...or even group elements together using groupByE:

var myEnum = E([1, 2, 3, 4, 5, 6]);
var groupedEnum = myEnum.groupByE((i) => i % 2);
// Values: [[1, 3, 5], [2, 4, 6]]

What's more, you can chain methods together, enabling virtually endless possibilities in a concise chain of method calls:

var myEnum = E([1, 2, 3, 4, 5, 6]);
var resultEnum = myEnum.selectE((i) => i * 2)
                       .whereE((i) => i > 4)
                       .selectE((i) => i.toRadixString(16));
/// Values: ['6', '8', 'A', 'C']

To use the values, you can iterate over the Enumerable just like you would any other Iterable collection:

var myEnum = E([1, 2, 3]);
for (var value in myEnum) {
    print(value);
}

// Output:
// 1
// 2
// 3

You can also easily convert the Enumerable back into a Dart collection type using toListE, toMapE, or toSetE:

var myEnum = E([1, 2, 3]);
var myList = myEnum.ToList();
// myList is a List<int> with the values of myEnum

Full Function List #

[0.3.1]

  • Typedefs have been converted from a shorthand form to the full form. This should help with some type inference issues.

[0.3.0]

  • Unit tests have been added to ensure Iterable built-in functions behave as expected.
  • Default reducers for methods where a comparator is optional (such as aggregatorE, sumE, and orderByE) have been extended to also support Duration and BigInt.
  • Custom error types have been overhauled to utilize default Dart error types:
    • UnexpectedStateError, KeyExistsError, and IntegerOverflowError now extend StateError instead of Error.
    • ConversionError now extends CastError instead of Error.
    • IncompatibleTypeError now extends UnsupportedError instead of Error.
    • NullEnumerableError, EmptyEnumerableError, and ElementNotFoundError have been merged into EnumerableError which extends StateError.
    • OperationError has been removed. Code that threw OperationError now throws StateError with a descriptive message.
  • Added subtype support for ValueEnumerable for specialized behavior when the source Iterable supports various actions. (Currently the only specialized subtype is ListIterator which enables short-circuiting for countE and elementAtE methods to call the list's length property and indexer, respectively.)
  • Changed countE to call the underlying iterable's length property when condition is not specified. This should enable O(1) length calculations for default Dart collections that internally implement EfficientLengthIterable.
  • Changed EqualityComparer to enable global setting of default comparers of new types. For example, for a custom type Foo, the static method EqualityComparer.addDefaultEqualityComparer<Foo>(comparer) will set the global default comparer for Foo to the passed comparer values. Following this call, all calls to LINQ methods that take an optional EqualityComparer<Foo> will default to using the previously registered comparer when a comparer is not specified.

Known Issues:

  • joinE and groupJoinE have a problem with the type inference of the parameters, causing the types to default to dynamic. When calling these methods while specifying an EqualityComparer, it may be necessary to explicitly state the type parameters as well as the types of parameters in any closure functions to get type inference to work properly.

[0.2.0]

  • The naming convention of the enumerable methods has been changed so that they are suffixed by the letter "e" (such as selectE, joinE, whereE). This is an attempt at a compromise between keeping the names of the methods consistent with their .NET origins while avoiding conflicts with existing Iterable methods.
  • Added two new factory methods to Enumerable: fromString and generator. fromString converts a String into an Enumerable that iterates over each individual character, while generator allows an iterable to be created of the specified length using a given generator function.
  • countIfE has been merged with countE so that countE now has a Condition as an optional parameter. If the Condition is provided, the behavior is identical to countIfE, and if not, the behavior is identical to the original countE.
  • Fixed an issue where several of the enumerable methods were not asserting on required parameters.

[0.1.2]

  • Addressing pub description warnings.

[0.1.1]

  • Addressing pub formatting warnings and issues.

[0.1.0]

  • Initial release.

example/main.dart

import 'package:darq/darq.dart';

//void main() {
//  benchmark();
//}

typedef T2 Foo2<T1, T2>(T1 a);
typedef T3 Foo3<T1, T2, T3>(T1 a, T2 b);

class Bar<A> {
  final List<A> outer;
  Bar(this.outer);

  void run<B, C, D>(
    List<B> inner,
    Foo2<A, C> outerSelector,
    Foo2<B, C> innerSelector,
    Foo3<A, B, D> resultSelector,
  ) {
    print([A, B, C, D]);
    print([
      outer.runtimeType,
      inner.runtimeType,
      outerSelector.runtimeType,
      innerSelector.runtimeType,
      resultSelector.runtimeType
    ]);
  }
}

class Person {
  final String name;
  Person(this.name);
}

class Pet {
  final String name;
  final String owner;
  Pet(this.name, this.owner);
}

main() {
  final map = {'a': 1, 'b': 2};
  print(map.runtimeType);
  print(map.keys.runtimeType);
  print(map.values.runtimeType);
}

void benchmark() {
  final source = List.generate(1000000, (i) => i);
  final iterations = 100;
  final benchmarks = List<double>(iterations);

  // LINQ style
  for (int i = 0; i < iterations; i++) {
    final start = DateTime.now();

    // ======================BENCHMARK START=============================
    final result =
        E(source).groupByE((i) => i % 3).selectE((g) => g.averageE());
    for (var _ in result) {
      // Do something with the value
    }
    // ======================BENCHMARK END===============================

    final end = DateTime.now();

    benchmarks[i] =
        (end.microsecondsSinceEpoch - start.microsecondsSinceEpoch) / 1000000;
  }

  print(
      'Average execution time in seconds (LINQ): ${E(benchmarks).averageE()}');

  // Vanilla Style
  for (int i = 0; i < iterations; i++) {
    final start = DateTime.now();

    // ======================BENCHMARK START=============================
    final result = <List<int>>[[], [], []];
    for (var i in source) {
      result[i % 3].add(i);
    }
    for (var g in result) {
      var total = 0;
      for (var i in g) {
        total += i;
      }
      final _ = total / g.length;
      // Go something with the value
    }
    // ======================BENCHMARK END===============================

    final end = DateTime.now();

    benchmarks[i] =
        (end.microsecondsSinceEpoch - start.microsecondsSinceEpoch) / 1000000;
  }

  print(
      'Average execution time in seconds (Vanilla): ${E(benchmarks).averageE()}');
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  darq: ^0.3.1

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter pub get

Alternatively, your editor might support pub get or flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:darq/darq.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
31
Health:
Code health derived from static analysis. [more]
98
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
65
Learn more about scoring.

We analyzed this package on Nov 8, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.6.0
  • pana: 0.12.21

Platforms

Detected platforms: Flutter, web, other

No platform restriction found in primary library package:darq/darq.dart.

Health suggestions

Fix lib/src/enumerable.dart. (-2.48 points)

Analysis of lib/src/enumerable.dart reported 5 hints:

line 248 col 9: DO use curly braces for all flow control structures.

line 250 col 9: DO use curly braces for all flow control structures.

line 252 col 9: DO use curly braces for all flow control structures.

line 921 col 9: DO use curly braces for all flow control structures.

line 964 col 9: DO use curly braces for all flow control structures.

Format lib/src/typedefs.dart.

Run dartfmt to format lib/src/typedefs.dart.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
Dev dependencies
pedantic ^1.7.0
test ^1.6.4