Deequery is library for declarative/functional working with collections in Dart. Note: it used to be called Dart Query.


Query is a view on top of arbitrary Iterable. It itself can be iterated.

It is designed to be used in a "pipeline" -- that is, a query is created, some operations are applied to it and finally a result is obtained. It looks like this:

var q = query(collection)    // collection of numbers
  .filter((e) => e % 2 == 0) // only take even numbers
  .reject((e) => e < 0)      // get rid of negative numbers
  .map((e) => e * 2)         // double each number
  .skip(1)                   // get rid of the first element
  .take(2);                  // only take first 2 elements

In this case, the result is also a Query.

var n = query(collection)        // collection of numbers
  .filter((e) => e > 0)          // only take positive numbers
  .findFirst((e) => e % 2 != 0); // find the first that is odd

In this case, the result is one element of the collection.

var b = query(collection)   // collection of numbers
  .takeWhile((e) => e < 0)  // only take negative numbers from the beginning
  .some((e) => e % 2 == 0); // find out if some of them is even

And finally, in this case the result is a bool. So you see that Query is a pretty powerful vehicle for working with collections.

With matchers

In all the examples above, we were using plain predicates (Dart functions). But Deequery also supports matchers from the Darmatch library, which can make the code even easier to understand.

Here are the examples above, rewritten using matchers:

var q = query(collection)
  .map((e) => e * 2)

var n = query(collection)

var b = query(collection)

Lazy and eager

The query function we were using above is creating a lazy query -- meaning that everything is computed lazily, only when needed and not before. This is useful if the collection (or Iterable) is computed dynamically, read from file or network, etc. It also means that whatever changes you make to the collection after creating the query, they are always visible when the query is actually iterated.

On the other hand, you can use an eagerQuery function which performs all operations immediatelly. It copies the original collection so that it can modify it in place. For smaller collections that are already fully read into memory, this will usually be faster, but further changes to the collection won't be reflected.

(Note that sometimes, the lazy query can get converted to eager one. See the documentation for more informations about when this happens.)