LINQ is a technology which enables developers to efficiently write queries against ubiquitous data sources in a SQL-like declarative style. It’s an impressive technology, especially to developers who aren’t acquainted with functional programming techniques. Wouldn’t it be nice to have something like LINQ implemented in Actionscript 3?

At first, I started experimenting a bit with “select” and “where” statements, then “join” and “groupBy” and not before long, FINQ was born.

What is FINQ and how to use it?

FINQ (Flex Integrated Query) is an implementation of LINQ to Objects (LINQ is basically made up of LINQ to Objects and LINQ to SQL). It is entirely written in AS3 and as such can be very easily integrated with any Flex or Flash program. Download the source code here. To use FINQ in your code or Flex project, just unpack the downloaded file and place the ‘finq’ folder inside the ‘src’ folder of your project then simply import the ‘finq’ package in your code.

As an example of how FINQ code looks like, consider the following example which outputs the number of occurrences for each element in a collection of integers:

How is FINQ designed and organized?

The design is very simple. It consists of one interface: IEnumerable and one class: FinqObj. IEnumerable defines all the extension methods that normally ship with LINQ plus some few others of my own to mimic the versatility of AS3’s arrays. FinqObj is a class which extends AS3’s Array class and implements IEnumerable. The new method definitions added to IEnumerable are:

forEach

getElementKeys

getElementKeysDescending

nonPrimitives

pop

primitives

print

push

toArrayCollection

toFinqObj

All IEnumerable method definitions were implemented except for the following:

asQueryable

longCount

toList

toLookup

selectMany

It is worth noting that I could have added the extension methods using Array’s prototype object but chose not to because Flex builder 3’s IntelliSense doesn’t know as yet what to do with them, besides I wanted to explicitly introduce the IEnumerable interface.

So why didn’t I do a ‘FINQ to SQL’?

Well, after digging a bit into the inner workings of LINQ, I discovered that LINQ to SQL could not work without so called expression trees. In a .NET language complier, when LINQ query code is parsed, it is converted into an intermediate form known as an expression tree. At run time, when the query is about to execute, its corresponding expression tree is used to construct the SQL query string that is passed to the backing database engine for execution. Thus in order to emulate LINQ to SQL, we have to at least provide some sort of mechanism to parse FINQ queries into expression trees. That seems to be a lot of work.

This might be done in Flex builder 3. The idea is to run some kind of a script or a program (possibly written in Java) prior to compiling the AS3 source code, which generates a text file (in a pre-build step), and save the file into the source code project folder. This file would contain all the textual code definitions of all the FINQ queries that are found in the source during the pre-build step (the file itself can be deployed as an asset in the complied swf file). At runtime, before a certain FINQ query is executed, its related code text is read from the embedded text file and then parsed whereby the expression tree is generated. To perform this last step, we could use something like tamarin or this cool AS3 eval complier. I am not sure if there is a better idea, but this seems to be at least doable.

In both cases, the select statement applies the inner anonymous function (referred to as closure from now on) to every element of data resulting in a new IEnumerable collection. In each closure above, the variable x represents an individual element of data. Most FINQ functions (at least those who return IEnumerable) are non-destructive to the original data meaning that the output is a completely new object.

The output of the second select statement is a bit odd, length was printed before type, this is because the order of <key, value> pairs is not guaranteed in AS3 objects and associative arrays. To straighten this out, we could use:

Just like select(), where() also returns an IEnumerable collection. In general, all the functions with a return value type of IEnumerable can be chained together to construct more sophisticated queries:

But before we proceed any further, I want to introduce a useful little naming convention regarding argument closures. If you look at the method signatures in IEnumerable, you will notice that most closure argument names end with one of the following strings:

selector

aggregator

comparer

equlalityComparer

predicate

The convention is that those closures are considered to have types according to:

For example, the orderBy() function takes a keySelector and an optional keyComparer. The keySelector specifies which key the ordering should be done relative to and the keyComparer specifies how two keys should be compared relative to each other. According to our naming convention, keySelector’s name ends with “selector” and therefore it is a function which takes one argument (of any type) and returns a dynamic type (in the example below, it returns a String). Likewise, keyComparer’s name ends with “comparer” and as such it is a function which takes two arguments (also of any type) and returns an integer. Checkout the following example for an illustration:

To be more concise, I will refer from now on to closure types by their ending string (a keySelector is a selector, a keyComparer is a comparer, etc…).

If you look more closely at the signature of orderBy(), you’ll notice oddly that it specifies keySelector as a dynamic type (indicated by using a wildcard) instead of a Function. When a wildcard is used to specify the argument type, it means that the argument can be either a closure or a string specifying the the element key.

A comparer usually returns and integer value of –1, 0 or +1 but it is annoying to write tests (as in the above code) each time we want to supply a comparer. That’s why I’ve introduced the option of a comparer returning a boolean (ex: return { x < y; }), the FinqObj implementation is smart enough to interpret the difference. Thus our previous orderBy() example can been written more succinctly as follows:

When the query is executed, groupBy() applies its input keySelector argument to every element in people and creates a <key, value> pair accordingly. The key part in the pair holds the result of keySelector and the value part of the pair holds an IEnumerable list of elements having all the same key as computed by keySelector for that pair.

groupBy() is a peculiar command, it can be used to implement complicated queries. For example, suppose we need the names of people sorted in three distinct age groups:

Here we have used an elementSelector as well. When the list of results is constructed for each key, elementSelector is applied to every element (that matches the key) prior to its addition to the list.

Now we come to the venerable join(). Consider the classical example of customers and orders:

inner is the IEnumerable collection to join with outer. The two selectors outerKeySelector and innerKeySelector are used to get the corresponding keys from outer and inner to be matched in the join operation. The final aggregator closure resultAggregator is simply applied to each matching element pair (from inner and outer) in order to compute the resulting element.

Suppose now we wanted to group our last join result by customerName. We can of course append a simple groupBy() query to join(), but there is shorter way to do it using groupJoin(). The signature of groupJoin() is exactly the same as the signature of join() and operates in almost the same way. The only difference is that instead of joining outer and inner by directly matching keys, first innerKeySelector is used in a groupBy() like operation on inner then the result is matched with outer using outerKeySelector. Check out the following code which demonstrates the concept:

There are still other FINQ functions such as union(), intersection(), max(), min() and many more. All of those are fairly similar to their LINQ counterparts and relatively easy to understand.

Some caveats

There are a few things native to LINQ which aren’t easily portable to FINQ: type inference and IntelliSense support (this requires changes at the compiler and IDE levels), lambda expressions (admittedly lambda expressions allow more succinct queries but with closures they are not absolutely necessary), and as mentioned at the beginning of this post, expression trees.

Hi there,thanks a lot for sharing this! A very handy tool. How about putting it somewhere like Sourceforge or Google Code and creating its own website so that it can be found even better by the flex community? I think finq is a must for every efficient developer...Stefan