101 LINQ to Objects Samples: From C# to APL+Win

Ajay Askoolum
Language Integrated Query (LINQ) is an extension to C#, available from version 3.01, that is, with Visual
Studio 2008; LINQ is a standard for querying and updating data. The syntax of LINQ integrates with that of the
host language, C# or Visual Basic.Net. Here, I am interested in LINQ to Objects—the other implementations
are LINQ to SQL, LINQ to Entities, LINQ to DataSet, and LINQ to XML—that deals with in-memory data or,
more generally, collections.
APL+Win, A Programming Language (APL) has been about language integrated data manipulation—
arrays, vectors, and nested or jagged arrays—throughout its history2. Here I am interested in LINQ to Objects
as a formal deliverable from APL+Win. With its strong/unique integration of Component Object Model (COM)
technology into its language features, APL+Win delivers LINQ functionality either in conjunction with C# as a
COM Server or on a standalone basis.
In this article, I present LINQ to Objects implemented in C# and APL+Win side by side. Above all, LINQ
provides a platform for extolling the capabilities of APL+Win using the same vocabulary as a C# developer!
The small price of this opportunity is the acquisition of a grasp of the LINQ to Objects concepts.
I urge you to read the ‘Partitioning Operators’ section even if LINQ to Objects is not within your sphere of
interest right now. For APL+Win users, this group of operators will be very familiar. That C# is now
incorporating these features into the language does indeed suggest that APL+Win is a language that is ahead
of time.

Table of Contents
Table of Contents ........................................................................................................................ 1
1.1 The Agenda ...................................................................................................................... 5
1.1.1 The terms of reference...............................................................................................................6
1.1.2 The payoff for general APL+Win programming .........................................................................6
1.1.2.1 Replicating the experience...................................................................................................................7
1.1.3 APL+Win and Dot Net Accessibility ...........................................................................................8
1.1.4 Why LINQ to Objects? ...............................................................................................................9

1.2 Getting Started.................................................................................................................. 9
1.2.1 Understanding the code.............................................................................................................9
1.2.1.1 Language symbols .............................................................................................................................10
1.2.1.2 What is the question?.........................................................................................................................11

1.3 Restriction Operators...................................................................................................... 11
1.3.1 Where - Simple 1 .....................................................................................................................11
1.3.1.1 C# code ..............................................................................................................................................11
1.3.1.2 APL+Win code ..................................................................................................................................12
1.3.1.3 Understanding the standard result .....................................................................................................12
1.3.1.4 Method Signature ..............................................................................................................................13
1.3.1.5 Further tests .......................................................................................................................................14
1.3.2 Where - Simple 2 .....................................................................................................................14
1.3.3 Where - Simple 3 .....................................................................................................................16

1
2

Dot Net Framework 3.5.
Unfortunately, LINQ and APL+Win use different jargon to describe identical concepts; I expect that the LINQ vocabulary will prevail.

© Ajay Askoolum

1.3.4 Where - Drilldown ....................................................................................................................17
1.3.5 Where - Indexed ......................................................................................................................18

1.4 Projection Operators....................................................................................................... 19
1.4.1 Select - Simple 1......................................................................................................................19
1.4.1.1 Linq6 in focus....................................................................................................................................19
1.4.2 Select - Simple 2......................................................................................................................20
1.4.3 Select - Transformation............................................................................................................20
1.4.4 Select - Anonymous Types 1 ...................................................................................................21
1.4.5 Select - Anonymous Types 2 ...................................................................................................21
1.4.6 Select - Anonymous Types 3 ...................................................................................................22
1.4.7 Select - Indexed .......................................................................................................................22
1.4.8 Select - Filtered........................................................................................................................23
1.4.9 SelectMany - Compound from 1 ..............................................................................................23
1.4.10 SelectMany - Compound from 2 ............................................................................................24
1.4.11 SelectMany - Compound from 3 ............................................................................................24
1.4.12 SelectMany - from Assignment ..............................................................................................25
1.4.13 SelectMany - Multiple from ....................................................................................................26
1.4.14 SelectMany - Indexed ............................................................................................................27

1.5 Partitioning Operators..................................................................................................... 28
1.5.1 Take - Simple...........................................................................................................................28
1.5.2 Take - Nested ..........................................................................................................................28
1.5.2.1 First two customers in Washington and all their orders ....................................................................29
1.5.2.2 First 3 customers in Washington and their first 5 orders...................................................................29
1.5.3 Skip - Simple............................................................................................................................29
1.5.4 Skip - Nested ...........................................................................................................................29
1.5.5 TakeWhile - Simple..................................................................................................................30
1.5.6 TakeWhile - Indexed ................................................................................................................30
1.5.7 SkipWhile - Simple...................................................................................................................31
1.5.8 SkipWhile - Indexed .................................................................................................................31

1.6 Ordering Operators......................................................................................................... 31
1.6.1 OrderBy - Simple 1 ..................................................................................................................31
1.6.2 OrderBy - Simple 2 ..................................................................................................................32
1.6.3 OrderBy - Simple 3 ..................................................................................................................32
1.6.3.1 Where is the difference? ....................................................................................................................32
1.6.4 OrderBy - Comparer ................................................................................................................33
1.6.5 OrderByDescending - Simple 1 ...............................................................................................33
1.6.6 OrderByDescending - Simple 2 ...............................................................................................33
1.6.7 OrderByDescending - Comparer .............................................................................................34
1.6.8 ThenBy - Simple ......................................................................................................................34
1.6.9 ThenBy - Comparer .................................................................................................................35
1.6.10 ThenByDescending - Simple .................................................................................................35
1.6.11 ThenByDescending - Comparer ............................................................................................35
1.6.12 Reverse..................................................................................................................................36

1.7 Grouping Operators ........................................................................................................ 36
1.7.1 GroupBy - Simple 1..................................................................................................................36
1.7.2 GroupBy - Simple 2..................................................................................................................37
1.7.3 GroupBy - Simple 3..................................................................................................................37
1.7.4 GroupBy - Nested ....................................................................................................................38
1.7.5 GroupBy – Comparer...............................................................................................................38
1.7.6 GroupBy – Comparer, Mapped................................................................................................38

1.8 Set Operators ................................................................................................................. 39
1.8.1 Distinct - 1 ................................................................................................................................39
1.8.2 Distinct - 2 ................................................................................................................................39
1.8.3 Union - 1 ..................................................................................................................................40
1.8.4 Union - 2 ..................................................................................................................................40
1.8.5 Intersect - 1 ..............................................................................................................................41
1.8.6 Intersect - 2 ..............................................................................................................................41
1.8.7 Except - 1.................................................................................................................................42
1.8.8 Except - 2.................................................................................................................................42
Page 2 of 69

1.9 Conversion Operators..................................................................................................... 43
1.9.1 To Array ...................................................................................................................................43
1.9.2 To List ......................................................................................................................................43
1.9.3 To Dictionary............................................................................................................................43
1.9.4 OfType .....................................................................................................................................44

1.10 Element Operators........................................................................................................ 44
1.10.1 First - Simple..........................................................................................................................44
1.10.2 First – Condition (missing at URL) .........................................................................................45
1.10.3 First - Indexed ........................................................................................................................45
1.10.4 FirstOrDefault - Simple ..........................................................................................................45
1.10.5 FirstOrDefault - Condition ......................................................................................................46
1.10.6 FirstOrDefault - Indexed.........................................................................................................46
1.10.7 ElementAt ..............................................................................................................................47

1.11 Generation Operators ................................................................................................... 48
1.11.1 Range ....................................................................................................................................48
1.11.2 Repeat ...................................................................................................................................48

1.12 Quantifiers .................................................................................................................... 49
1.12.1 Any - Simple...........................................................................................................................49
1.12.2 Any - Indexed.........................................................................................................................49
1.12.3 Any - Grouped........................................................................................................................49
1.12.4 All - Simple.............................................................................................................................49
1.12.5 All - Indexed ...........................................................................................................................50
1.12.6 All - Grouped..........................................................................................................................50

1.13 Aggregate Operators .................................................................................................... 50
1.13.1 Count - Simple .......................................................................................................................50
1.13.2 Count - Conditional ................................................................................................................51
1.13.3 Count - Indexed .....................................................................................................................51
1.13.4 Count - Nested.......................................................................................................................52
1.13.5 Count - Grouped ....................................................................................................................52
1.13.6 Sum - Simple .........................................................................................................................52
1.13.7 Sum - Projection ....................................................................................................................53
1.13.8 Sum - Grouped ......................................................................................................................53
1.13.9 Min - Simple ...........................................................................................................................53
1.13.10 Min - Projection ....................................................................................................................54
1.13.11 Min - Grouped ......................................................................................................................54
1.13.12 Min - Elements .....................................................................................................................54
1.13.13 Max - Simple ........................................................................................................................55
1.13.14 Max - Projection ...................................................................................................................55
1.13.15 Max - Grouped .....................................................................................................................55
1.13.16 Max - Elements ....................................................................................................................56
1.13.17 Average - Simple .................................................................................................................56
1.13.18 Average - Projection ............................................................................................................56
1.13.19 Average - Grouped ..............................................................................................................57
1.13.20 Fold - Simple........................................................................................................................57
1.13.21 Fold - Seed ..........................................................................................................................57

1.14 Miscellaneous Operators .............................................................................................. 58
1.14.1 Concat - 1 ..............................................................................................................................58
1.14.2 Concat - 2 ..............................................................................................................................58
1.14.3 EqualAll - 1.............................................................................................................................58
1.14.4 EqualAll - 2.............................................................................................................................59
1.14.4.1 EqualAll - 2 : Extension ..................................................................................................................59

1.15 Custom Sequence Operators ....................................................................................... 59
1.15.1 Combine.................................................................................................................................60
1.15.1.1 C#:APL+Win comparison ...............................................................................................................60

1.16 Query Execution ........................................................................................................... 61
1.16.1 Deferred .................................................................................................................................61
1.16.2 Immediate ..............................................................................................................................61
1.16.3 Query Reuse..........................................................................................................................62
Page 3 of 69

1.17 Time for 101 APL+Win samples? ................................................................................. 62
1.17.1 Some eligible topics ...............................................................................................................62
1.17.1.1 Example 1 - Return the sign of all elements of a numeric vector/array ..........................................62
1.17.1.2 Example 2 - Return the magnitude indicator of all elements of a numeric vector/array.................62
1.17.1.3 Example 3 – Apply the percentage of increase ...............................................................................62
1.17.1.4 Example 4 – Re-calculate the diagonal elements of a numeric array..............................................63
1.17.1.5 Example 5 – Return the powers of 2 of each element of a numeric vector/array............................63

References ................................................................................................................................. 63
Appendix A – Product & Customer Lists ................................................................................ 65
Index ........................................................................................................................................... 67
Code Listings............................................................................................................................. 69

Page 4 of 69

101 LINQ to Objects Samples: From C# to APL+Win
Ajay Askoolum
1.1 The Agenda
The agenda is set at the following Universal Resource Locator (URL):

http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx

Alas, the material at his URL does not refer to APL+Win at all, as to be expected!

© Ajay Askoolum

A much more serious criticism is that the URL does not provide complete code samples. A number of
the code snippets do not lend themselves to simple copy & paste into another project; some of the
sample codes are also ‘incorrect’3. Although the basic code listings are useful, the supporting code
4
dependencies are missing .

The code samples simply demonstrate concepts5: the results of sample queries (written to the
console) shown at the URL provide valuable insight into the functionality, especially from the point of
view of replicating them in APL+Win.

If you count the number of samples, there are fewer than the 101 suggested by the title; and a
number of them—such as Linq60—do not work. This is not only misleading but also highly
unsatisfactory, not least because of the pedigree of its origin—Microsoft—and the fact that this passes
for reference material.

In spite of all the shortcomings, this provides an excellent opportunity for exploring a brand new feature of
the contemporary flagship language; ore to the point, this provides the terms of reference for illustrating
APL+Win techniques that have been part of the language from the outset. I have included the missing
samples in this article.

1.1.1 The terms of reference
The code samples at the URL have the data that they manipulate either hard-coded or drawn from supporting
functions. The functions are not parameterised.
My objective is to write a COM server in C# and use it with APL+Win, and to compare the results from C#
with those from APL+Win, in the APL+Win immediate mode. In order to extend the scope of this exercise,
some of the methods in C# and APL also accept arguments in client/server mode.
This serves several purposes.

C# uses complex data types such as the product and customer lists; although these are akin to
APL+Win’s nested arrays, there is an important difference. C# refers to the data items by name,
something that is not possible in APL+Win. In the APL+Win environment, I refer to the data items by
their position or cardinal order in the nested array. Therefore, I also need to coax C# to supply the
data in a simpler nested/jagged array format for use by APL+Win.

A number of refinements in the C# examples are not obvious from the examples given. For example,
UNION disregards duplicated elements in its result; appropriate arguments to such functions can
highlight such features.

Overall, this demonstrates C# and APL+Win in a fully functional client/server configuration—an option
for any application.

1.1.2 The payoff for general APL+Win programming
LINQ techniques are interesting from point of view of APL+Win for several reasons.

All data in APL+Win are objects or, for a simplistic view, arrays; LINQ deals with such objects.

3

For example, the code for the class CustomSequenceOperators is incorrect; Linq96 shows wordsA.EqualAll(wordsB) which should be
wordsA.SequenceEqual(wordsB).
4
For example, functions such as GetProductList and GetCustomerList, are missing.
5
The code outputs results to the console: as such, modifications are required to incorporate them in class libraries so that they can take arguments and
return results.

Page 6 of 69

Although C# and APL+Win operate in disparate environments, this paper demonstrates how well the
two can work together (the prospects for Visual APL which works from the same environment as C#
are even better!).

I have not developed optimised code for my purpose or designed code for re-use. However, there are
subtle opportunities to harvest APL+Win idioms. The APL+Win code samples generally use onedimensional arrays (vectors or nested vectors): they can be generalised for multi-dimensional arrays.

There is an opportunity to familiarise yourself with another jargon, that of LINQ (and C#): this will be
useful for the future in many ways, even if this is not apparent right now.

1.1.2.1 Replicating the experience
I am using APL+Win version 9, with Visual Studio 2008 with Service Pack 1, under Windows XP Professional
with Service Pack3.
The APL+Win workspace, LINQ to OBJECTS.W3, is accessible with version 9 of the interpreter.
If you have Visual Studio 2008 (any hybrid), one of your options is to attempt to load the project and
recompile it: I have not tested this project with every version of Visual Studio 2008. This will register the COM
Dynamic Library (DLL) as required, without further intervention from you.
6

If you do not have Visual Studio 2008 , you will need to do the following:

Download and install Dot Net Framework 3.5

Register the COM DLL, LINQ.DLL, as follows:

????
Depending on how you use the supplied code, you may receive the following (or similar) error:
ŒWI ERROR: mscorlib exception 80070003 Could not find a part of the path
'C:\AJAY\C#\VS2008\VS2008\LINQ to Objects\LINQ\customers.xml'.
This arises as a result of a hard-coded path reference is DATA.CS. The resolution is to ensure that the
7
path specified reflects the location of CUSTOMERS.XML in your preferred directory structure .

The freely downloadable XML NOTEPAD 2007, see below, is a useful tool for creating and examining
XML files interactively. You may want to use this tool to examine/audit the results of LINQ queries that use
CUSTOMERS.XML.

6
7

This presents the same scenario as that when deploying your own Dot Net DLL.
The project needs re-compilation after any changes to the code.

Page 7 of 69

1.1.3 APL+Win and Dot Net Accessibility
An often raised complaint, perhaps more often than necessary given the respective infrastructure and lineage
of APL+Win and C#, is that APL+Win does not, but should, implement a system function, ŒNET, to enable
access to the Dot Net Libraries. I do not subscribe to this view for the following reasons:

8

Such an accessibility function will not enhance or optimise the existing legacy of APL+Win
applications such that they can benefit from enhancements in the technology that Dot Net heralds. It
might be useful for future development: in this context, Visual APL offers better prospects for return on
investment since it is closely aligned to APL+Win and is a true Dot Language, not just a Dot NET
‘aware’ one.

Such a function allows access to the Dot NET foundation class libraries only and not to Dot NET
languages such as C#8. Quite often, it is necessary to write APL Code to glue calls to the foundation
class libraries. This implies that a managed and strongly typed environment (Dot Net) is coerced into
an un-managed and loosely typed environment (APL). This has strong implications for application
debugging and maintenance. Besides, in such as scenario APL is always playing catch up to
improvements in the Dot Net framework.

APL+Win offers a robust COM interface that enables any user-built DLL to be deployed from within
APL+Win. Therefore, the Dot NET environment can be kept separate from the APL+Win environment
with several advantages: first, it is not the APL developer who has to code the Dot Net functionality—a
Dot Net expert can do it independently of APL—and second, each environment can be debugged
separately or together. Play the media file APLWINDEBUGSC#.WMV for an illustration.

Remember that LINQ fits within a Dot NET language, such as C# or Visual Basic.NET.

Page 8 of 69

1.1.4 Why LINQ to Objects?
LINQ to Objects is especially interesting from an APL point of view.

It is a set of standard operators that eliminates the need for the developer to write loops

It standardises the means of doing routine tasks relating to data in memory and has a syntax that is
very much like that of the host language.

Is that not what APL+Win is (has always been) about?

1.

1.2 Getting Started
The general format of the illustrations below is to state the purpose of the code sample, list the C# code
sample, list the APL+Win function and then to illustrate and discuss the results.
The 101 code samples are named Linq1 to Linq101, as seen at the URL; I have retained this convention in
coding the APL+Win counterpart functions in order to maintain a 1:1 correspondence.

For some functions, there is an extension function, named LinqnEx (n designates the numeric
sequence of the code sample): I have used this to elaborate on extensions to the functionality in
question.

The source data for the code samples, where required, is internally supplied as complex data types to
the C# functions: GetProductList and GetCustomerList are two such examples. I have coded
GetProductListEx and GetCustomerListEx which supply the same data as a simple type to APL+Win.
9
Please refer to the end of this document for a complete listing of the C# code—EXPLORE.CS and
DATA.CS—that underlies the COM DLL.

For the sake of clarity as well as completeness, I recommend that you refer to the code sample at the URL
and this document concurrently and make appropriate allowance for the errors and omission I have
highlighted.
Given that the C# COM DLL, LINQ.DLL, is registered, the following function establishes an instance
thereof; it runs as the latent expression on loading the workspace LINQ to OBJECTS.W3 and should persist
for the duration of the session:
[1]
[2]

’ LINQ
© Ajay Askoolum
Œwself„'lq' Œwi 'Create' 'LINQ.Explore'

The first example, discussed next, is deliberately verbose: this is to familiarise you with the pattern for all
the other samples. Therefore, you should study this example first and then may navigate to any other sample,
if necessary.

1.2.1 Understanding the code
These pages do not contain a tutorial on either C# or APL+Win: such a task is beyond the scope of this
exercise. However, the VS2008 project and the APL+Win workspace contain all the code used herein.
When working through these pages, it may be useful to bear in mind the following:

9

Unlike APL+Win which has index origin default to zero, C# always works with index origin set to
zero—there is no other option. The APL+Win code I have used here is index-origin independent.

I printed the code from Visual Studio 2008 and appended to this PDF.

Page 9 of 69

Both APL+Win and C# are case sensitive.

Both APL+Win and C# use square brackets for indexing; for indexing multi-dimensional arrays,
APL+Win uses semi-colon and C# uses comma to separate the co-ordinates.

The syntax for localisation or scope determination is different in the two languages. With APL+Win,
any name included in the header of a function and separated by a semi-colon has the effect of
restricting the scope of that name to that function only10. In C#, any name included within a pair of
opening and closing braces is local.

In C#, a comma separates elements of an array/vector; the same is true (but optional) for APL+Win
numeric elements (although it is more conventional to use spaces) of an array/vector.

Unlike APL+Win which uses single and double quotes—in pairs—interchangeably, C# uses double
quotes for literals or strings and single quotes for data type char.

C# uses \ as an escape character, embedded in literals; for example, “Prime\r\nExample” indicates
that carriage return and line feed separate the two words. C# has verbatim strings; these are prefixed
by @ and simply instruct C# to take a literal at face value. For example, with @“Prime\r\nExample”,
C# will not interpret \r\n as carriage return and line feed. If you specify a string, say a path as c:\ajay,
the options are to write either “c:\\ajay” or @”c:\ajay”. APL+Win does not have embedded escape
sequence characters but can pass literals to C# using the C# conventions.

How many statement delimiters does APL+Win have? It might surprise you to learn that there are two:
diamond (ª) and linefeed. C# has a single statement delimiter, semi-colon; statements may span
several lines. Unlike APL+Win where these delimiters are optional, the C# delimiter is mandatory.

If you are lured into the belief that C# is difficult, it might be an opportune moment to remind yourself how
difficult APL has been between the time when you started to learn it and the present, when you have
mastered the language.
C# is a pleasant experience. From my point of view, C# is no more difficult than APL+Win and the two
languages share a common attribute, namely, it is possible to write code without understanding all the
subtleties of the language. Unlike APL+Win, C# has a wealth of sources for worked examples and general
reference; therefore, in theory, C# should be easier to learn.

1.2.1.1 Language symbols
This article involves two languages, C# and APL+Win. The context involves the examination of the code sideby-side. As an aid to understanding the code, the following table is useful.
APL+Win

=
¬
<
ˆ
>

|

10

C#
=
==
!=
<
<=
>
>=
%

Description
Comes from or becomes
Equality comparison
Not equal to
Less than
Less than or equal to (not greater than)
Greater than
Greater than or equal to (not less than)
Modulus

In order to keep this simple, I will not elaborate on dynamic localisation here; this does not exist in C#.

Page 10 of 69

1.2.1.2 What is the question?
The question mark is part of the C# language; it is like an APL+Win primitive.
double?[] doubles = { 1.7, 2.3, 4.1, 1.9, null, 2.9 };
In the above line, the question mark indicates that any element of the vector can have a null value.
Sign = (MyVal == 0) ? 0 : (MyVal < 0) ? -1 : 1;
In the latter example, the question mark indicates ‘if’: read the line as “the sign of a scalar MyVal is 0 if it is
zero, else if it is less than one, the sign is -1 else it has a default value of 1”
C# has a data type that is also of interest as it occurs in the code samples: null. C# is a strongly typed
language; APL+Win is a type-inferred language. The null data type is a difficult concept; technically, it means
a missing value or that a variable has been declared but not assigned a value.

APL cannot have a variable that is unassigned since it is not a declarative language. However, in some
contexts, 0/0 or 0/’’ are appropriate to emulate a null value in APL+Win—it also has a null value.

1.3 Restriction Operators
This group of operators apply relational criteria to data and return the elements that satisfy the criteria; the key
concept is to elicit results using relational operators on lists, or vectors, or arrays, numeric and character.
Another way to understand this group of operators is to think of them as filtering an existing object, the
argument, and creating a new object, the result.
APL+Win supports the full set of relational operators found in C#; the difference is that LINQ also has
names for some of the operators whereas APL+Win has primitives.

1.3.1 Where - Simple 1
This example shows the selection, from a given vector, of all elements whose value is greater than five.

1.3.1.1 C# code
At the URL, this sample is listed as follows:
public void Linq1()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var lowNums = from n in numbers
where n < 5

Page 11 of 69

select n;
Console.WriteLine("Numbers < 5:");
foreach (var x in lowNums)
{
Console.WriteLine(x);
}
}
The following is apparent:

The data that passes for the argument to the function is hard-coded, making it inconvenient, if not
impossible, to test the function with alternative data.

The function does not return a result; it writes to the console. Therefore, it would be impossible to
return results to or pass data from APL+Win.

Therefore, in order to overcome these limitations, I have modified the code to read as follows:
public int[] Linq1(int[] numbers, int threshold)
{
var lowNums = from n in numbers
where n < threshold
select n;
return lowNums.ToArray();
}
Now the function will

Accept two arguments, the data and the threshold that is used to filter the data.

Return a result instead of printing it to the console.

1.3.1.2 APL+Win code11
All the APL+Win functions assume that the instance of the C# COM DLL exists already and that it is the
default left-hand argument of Œwi the function.
[1]
[2]
[3]
[4]
[5]
[6]

’ Z„Linq1;L;R;Linq;APL
© RESTRICTION OPERATOR: Where - Simple1
L„¹(2?10)?¨15 © With possible replication
R„?10
Linq„Œwi 'Linq1' L R
APL„(L<R)/L
Z„•œ(›¨'L' 'R' 'LINQ' 'APL' 'Match?'),¨L R Linq APL (Linq−APL)

1.3.1.3 Understanding the standard result
The first objective must be to reproduce the results from C#:
Œwi 'Linq1' (5 4 1 3 9 8 6 7 2 0) (5)
4 1 3 2 0
The APL+Win result, side by side, is:

11

It would be nice if APL+Win retained syntax colouring with copy & paste, as C#!

Page 12 of 69

The sample shows the results as follows:

On the face of it, the sample shows the result as an n by 1 array whereas the APL equivalent shows it as
a vector of n elements. In fact, the sample is appending a carriage return12 after each element: the results
are identical.

1.3.1.4 Method Signature
The previous example illustrates how a method inside the COM is callable from APL+Win with arbitrary
arguments. However, you should verify how the signature of a method—how it is coded—before attempting a
call.
The syntax of a method may be established by a visual examination of the code—refer to Appendix A
either for code listings—or as follows:
Œwi '?Linq1'
XLinq1 method:
Result@Array_Long „ ŒWI 'XLinq1' numbers@Array_Long threshold@Long

12

WriteLine appends \r\n (carriage return and line feed) to its argument.

Page 13 of 69

Note that this query returns both the name and type of each argument, and indicates the type of the result
returned, if any.

1.3.1.5 Further tests
The APL+Win function, Linq1, creates random arguments—see lines [2]-[3]—and permits tests to be run
quickly.
The following illustrates the results of three further tests.

The argument numbers is listed first (as L), followed by the argument threshold (as R). Then the result
returned by the COM DLL is listed (as Linq), followed by the corresponding result from APL+Win (as APL).
The label Matched? shows 1 when the results from C# and APL+Win match else it shows 0.
This concludes the basic guide to reading each of the remaining examples/samples13.

1.3.2 Where - Simple 2
This query lists all the products that are out of stock. The list or product, unlike the list of customers held in an
external XML file, is built using code in DATA.CS. The first two products are built with these lines of code:

C# accesses the individual elements of data by name, shown here in the VS2008 Immediate Window.

13

This is LINQ! an advancement to C#. .APL+Win has done this from inception, has it not?

Page 14 of 69

This is a complex C# data type; it comprises of several simple C# types. The data structure Product is
build as follows:
public class Product
{
public int ProductID;
public string ProductName;
public string Category;
public decimal UnitPrice;
public int UnitsInStock;
}
In APL+Win, the simplest approach is to code the C# COM DLL to supply the data14. Everything is
available except that the names of the individual elements.

14

This avoids transcription errors and facilitates comparisons.

Page 15 of 69

Finally, let us see the results of the LINQ Query in APL+Win:
’ Z„Linq2;L;R;Linq;APL;products
© RESTRICTION OPERATOR: Where - Simple 2
Linq„Œwi 'Linq2'
products„Œwi 'GetProductListEx' © ProductID ProductName Category
UnitPrice UnitsInStock
[4]
L„¹¯1†¨products
© Last element is UnitsInStock
[5]
APL„(Œio+1)œ¨(L=0)/products
[6]
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

[1]
[2]
[3]

The results match!

1.3.3 Where - Simple 3
This query selects items from the product list where the number of items in stock is non-zero and the item's
unit price is greater than 3.00.

Page 16 of 69

The results match; they are produced by the following function:
[1]
[2]
[3]
[4]
[5]
[6]
[7]

’ Z„Linq3;L;R;Linq;APL;products
© RESTRICTION OPERATOR: Where - Simple 3
Linq„Œwi 'Linq3'
products„Œwi 'GetProductListEx' © ProductID ProductName Category
UnitPrice UnitsInStock
L„¹(Œio+3)œ¨products
© Fourth element is UnitPrice
R„¹¯1†¨products
© Last (fifth) element is UnitsInStock
APL„(Œio+1)œ¨((R>0)^L>3)/products
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

I am using a screenshot to capture the results of functions, especially where the results are verbose, as in
this instance. This is purely a convenience for formatting the content of this document. For your exploration,
you may choose to stop the functions after the penultimate line and then examine or debug the individual
components of the results at your leisure; the last line shows the complete results.
How does it work? With C# a two-dimensional string array, potentially, each row has a different length: the
length of the number of characters in it. With APL+Win, all rows have the same length—they are padded with
trailing space making up a length equal to the longest string in the array. However, with a nested string vector,
each element occupies only enough space to accommodate its own length irrespective of the length of coexisting elements; this feature simplifies string relational comparisons.

1.3.4 Where - Drilldown
This query prints a list of customers from the state of Washington along with their orders. The list of customers
holds customers details and their orders.
LINQ

Linq4
LAZYK Lazy K Kountry Store
TRAIH Trail's Head Gourmet Provisioners
WHITC White Clover Markets

APL

LAZYK Lazy K Kountry Store
TRAIH Trail's Head Gourmet Provisioners
WHITC White Clover Markets

10482
10545
10574
10577
10822
10269
10344
10469
10483
10504
10596
10693
10696
10723
10740
10861
10904
11032
11066
10482
10545
10574
10577
10822
10269
10344
10469
10483
10504
10596
10693
10696
10723
10740

21/03/1997
22/05/1997
19/06/1997
23/06/1997
08/01/1998
31/07/1996
01/11/1996
10/03/1997
24/03/1997
11/04/1997
11/07/1997
06/10/1997
08/10/1997
30/10/1997
13/11/1997
30/01/1998
24/02/1998
17/04/1998
01/05/1998
21/03/1997
22/05/1997
19/06/1997
23/06/1997
08/01/1998
31/07/1996
01/11/1996
10/03/1997
24/03/1997
11/04/1997
11/07/1997
06/10/1997
08/10/1997
30/10/1997
13/11/1997

Page 17 of 69

10861
10904
11032
11066
Match?

30/01/1998
24/02/1998
17/04/1998
01/05/1998
1

The function is defined as follows:
’ Z„Linq4;Linq;APL
© RESTRICTION OPERATOR: Where - DrillDown
Linq„œœ¨¨Œwi 'Linq4'
APL„Œwi 'GetCustomerListEx'
© CustomerID 1,CompanyName
2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone 8,Fax 9,Orders
10
[4]
APL„(((Œio+4)œ¨APL)¹›'WA')/APL
© Just those in the WA Region <Region> is the fifth element
[5]
APL„(Œio+0 1 9)œ¨¨›APL
© <CustomerID>, <CustomerName> &
<Orders> i.e. First, Second & Tenth element
[6]
((Œio+2)œAPL)„œ¨1 1 0/¨¨(Œio+2)œAPL © <OrderId> & <OrderDate> i.e First
& Second element of <Orders>, now the third element
[7]
APL„³œAPL
[8]
Z„('LINQ' 'APL' 'Match?'),[Œio+0.5] Linq APL (Linq−APL)

The comments in this function provide valuable clues on the computation of the result. If the logic is not
self-evident, you need to examine the function line by line.
[1]
[2]
[3]

The COM method GetCustomerListEx hides a subtlety: because APL+Win does not support the datetime
data type, the method returns the date information as a string, formatted in short date regional format. I am
using the UK regional format, that is, dd/mm/yyyy15. For reporting purposes, this technique is adequate, as it
does not involve any date arithmetic.

1.3.5 Where - Indexed
This query uses an indexed Where clause to print the name of each number, from 0-9, where the length of the
number's name is shorter than its value. In this case, the code is passing a lamda expression which is
converted to the appropriate delegate type. The body of the lambda expression tests whether the length of the
string is less than its index in the array.
At this point, the reality of the C# jargon hits home; as mentioned, it is beyond the scope of the present
objective. Briefly, a lambda expression is an anonymous (one without a name) function and contains
statements like any function. For the lambda operator => read “goes to”. A good reference on C# 3.0 is a
prime requisite for understanding the Linq to Objects samples: at the very least you need to read the words
accompanying each example at the URL http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx.
Linq5
LINQ
five six
APL
five six
Match?
1

seven
seven

eight
eight

nine
nine

The function is defined as follows:
[1]
[2]

’ Z„Linq5;Linq;APL
© RESTRICTION OPERATOR: Where - Indexed
Linq„Œwi 'Linq5'

15

Depending on your locale, you may see different results in your session; however, the C# and APL+Win results will both be consistent with your
locale.

Page 18 of 69

[3]

APL„"zero" "one" "two" "three" "four" "five" "six" "seven" "eight"
"nine"
APL„(¹(½¨APL)<(-Œio)+¼½APL)/APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

[4]
[5]

1.4 Projection Operators
Projection operators transform their argument into new objects, the results; the arguments and results may be
of different types. These operators often involve calculation within a selection.

1.4.1 Select - Simple 1
This query returns a sequence of integers one greater than those in an input array; it uses the expression in
the select clause to add one to each element in the new sequence. The C# code is:
public int[] Linq6()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var numsPlusOne = from n in numbers
select n + 1;
return numsPlusOne.ToArray();
}
APL+Win does not support the facility to select a number and perform an arithmetic operation on that
number in one expression. However, APL+Win can produce the same result.
Linq6
LINQ
6
APL
6
Match? 1

5
5

2
2

4
4

10
10

9
9

7
7

8
8

3
3

1
1

The APL function is defined as follows:
[1]
[2]
[3]
[4]

’ Z„Linq6;Linq;APL
© Projection Operators - Select Simple 1
Linq„Œwi 'Linq6'
APL„1+ 5 4 1 3 9 8 6 7 2 0
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.4.1.1 Linq6 in focus
The alternative or conventional C# way of achieving the same result is:
public int[] Linq6alt()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int[] res = new int[numbers.Length];
for (int i = 0;i< numbers.Length;i++)
{
res[i]=numbers[i]+0;
}
return res;
}
It is now apparent that this LINQ query simply saves the developer two things: first, an explicit loop and
second the facility to return a subset of the argument vector by the use of a where clause, if necessary.
In fact, Linq6 hides a much more important subtlety: if, say, only even numbers are retained from the
argument, the alternative solution will not work since it is impossible to determine how many elements are
even without executing another loop to determine it first. Then, the solution to a simple problem is at risk of
becoming a clutter of code.

Page 19 of 69

16

Of course, the APL solution takes such a restriction in its stride :
1+(0=2|APL)/APL„5 4 1 3 9 8 6 7 2 0
5 9 7 3 1

1.4.2 Select - Simple 2

This query returns the name of every product in the product list. The matching APL+Win result comes from
the following function:
’ Z„Linq7;Linq;APL
© RESTRICTION OPERATOR: Where - Indexed
Linq„œŒwi 'Linq7'
APL„œ(Œio+1)œ¨Œwi 'GetProductListEx' © ProductID ProductName Category
UnitPrice UnitsInStock. ProductName is second element
[4]
Z„•(›¨'LINQ' 'APL' 'Match?'),[Œio-0.5]Linq APL (Linq−APL)

[1]
[2]
[3]

1.4.3 Select - Transformation
This query returns the name of each number in an integer array by indexing into a second array that contains
the names. The C# solution works because it works in index origin zero.
APL+Win can return the same result in index origin one or zero; the result drawn from the C# DLL work in
the only index origin possible, zero, irrespective of the setting in APL+Win.
Œio„1 ª Linq8
LINQ
five four
APL
five four
Match?
1
Œio„0 ª Linq8
LINQ
five four

16

one
one

three
three

nine
nine

eight
eight

six
six

seven
seven

two
two

zero
zero

one

three

nine

eight

six

seven

two

zero

Unlike C#, APL+Win can resize/reorder arrays dynamically.

Page 20 of 69

APL
five four
Match?
1

one

three

nine

eight

six

seven

two

zero

The APL+Win function is defined as follows:
’ Z„Linq8;Linq;APL
© Projection Operators: Select - Transformation
Linq„Œwi 'Linq8'
APL„5 4 1 3 9 8 6 7 2 0
APL„("zero" "one" "two" "three" "four" "five" "six" "seven" "eight"
"nine" )[APL+Œio]
[5]
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

[1]
[2]
[3]
[4]

1.4.4 Select - Anonymous Types 1
This query returns each element of its argument in uppercase and lowercase. With APL+Win, I will not use the
CharUpper or CharLower Application Programming Interface (API) calls in order to keep the comparison
strictly at the language level.
The solution is quite simple: substitute each of the 26 characters with one in the corresponding case.
[1]
[2]
[3]
[4]
[5]
[6]

’ R„L LCase R
© Return R in uppercase
L„Œio
:for i :in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
((i=,R)/,R)„'abcdefghijklmnopqrstuvwxyz'[L]
L„L+1
:endfor

’ R„L UCase R
© Return R in uppercase
L„Œio
:for i :in 'abcdefghijklmnopqrstuvwxyz'
((i=,R)/,R)„'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[L]
L„L+1
:endfor

The following APL+Win function produces a comparison of the solution:

[1]
[2]
[3]
[4]
[5]
[6]

’ Z„Linq9;Linq;APL
© Projection Operators - Select - Anonymous Type 1
Linq„Œwi 'Linq9'
APL„œ,¨/›¨¨(UCase ¨"aPPLE" "BlUeBeRrY" "cHeRry") (LCase ¨"aPPLE"
"BlUeBeRrY" "cHeRry")
[4]
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

[1]
[2]
[3]

The results match:
Linq9
LINQ
APPLE apple
APL
APPLE apple
Match?
1

BLUEBERRY blueberry
BLUEBERRY blueberry

CHERRY cherry
CHERRY cherry

1.4.5 Select - Anonymous Types 2
This query iterates over the elements of an input integer vector and returns the element’s name and parity,
that is, whether it is odd or even. It works because string vector holds the digit names in numerical sequence,
starting with zero—the index origin in C#.

Page 21 of 69

The APL+Win function—index origin independent—is:
’ Z„Linq10;Linq;APL
© Projection Operators: Select - Anonymous Types 2
Linq„Œwi 'Linq10'
APL„5 4 1 3 9 8 6 7 2 0
APL„(›¨("zero" "one" "two" "three" "four" "five" "six" "seven" "eight"
"nine")[Œio+APL]),¨ ›¨('even' 'odd')[Œio+2|APL]
[5]
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

[1]
[2]
[3]
[4]

1.4.6 Select - Anonymous Types 3
This query returns the name of every product in the product list along with the category of the product and its
unit price. The C# version renames UnitPrice to Price. As mentioned, APL+Win identifies elements of a
nested array by its cardinal order and not by name.

The APL+Win function that produces a matching result:
’ Z„Linq11;Linq;APL
© Projections Operators: Select - Anonymous Types 3
Linq„œŒwi 'Linq11'
APL„0 1 1 1 0/œŒwi 'GetProductListEx' © ProductID ProductName Category
UnitPrice UnitsInStock
[4]
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)
[5]
…0
[6]
(ProductName Category Price)„,›[Œio+1]³APL

Note line [6]: APL+Win can easily reclaim the names of the columns in the result: C# retains the names
intrinsically and APL+Win assigns them arbitrarily.
[1]
[2]
[3]

1.4.7 Select - Indexed
This query returns the value of each element in a vector of integers whether the element matches its index—
in index origin zero—in the vector. The index of each element is inferred rather than index in another vector
holding the actual indices.
LINQ
5
4
1
3
9
8

0
0
0
1
0
0

Linq12
APL
5
4
1
3
9
8

Page 22 of 69

0
0
0
1
0
0

Match?
1

6
7
2
0

1
1
0
0

6
7
2
0

1
1
0
0

Although the results match, there is a deviation in the result: 1 and 0 are shown instead of ‘true’ and
‘false’, respectively. The APL+Win function:
[1]
[2]
[3]
[4]

’ Z„Linq12;Linq;APL
© Projections Operators: Select Indexed
Linq„³œŒwi 'Linq12'
APL„³œAPL (APL=(-Œio)+¼½APL„5 4 1 3 9 8 6 7 2 0)
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.4.8 Select - Filtered
This query prints the name of each element of an integer vector that is less than five; it combines ‘select’ and
‘where’ to build a simple query that indexes into another string vector that contains the names of the elements.
All elements are less than nine.
LINQ

Linq13
APL

four
one
three
two
zero

Match?

four
one
three
two
zero

1

The function is:
’ Z„Linq13;Linq;APL
© Projection Operators: Select - Filtered
Linq„œŒwi 'Linq13'
APL„"zero" "one" "two" "three" "four" "five" "six" "seven"
"eight" "nine"
[4]
APL„œAPL[Œio+(5 4 1 3 9 8 6 7 2 0<5)/5 4 1 3 9 8 6 7 2 0]
[5]
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

[1]
[2]
[3]

1.4.9 SelectMany - Compound from 1
This example identifies ordered pairs of integers, such that the first number is an element of one integer
vector and the second number is an element of another integer vector where the first number is less than the
second number. The query uses a compound ‘from’ clause to compose a query that returns a sequence of the
pairs.
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]

’ Z„Linq14;Linq;APL;numbersA;numbersB;Select
© Projection Operators: SelectMany - Compound from 1
Linq„³œŒwi 'Linq14'
numbersA„0 2 4 5 6 8 9
numbersB„1 3 5 7 8
0 0½Œdef '’ Z„L Select R',Œtcnl,'[1]
Z„L/R ’'
APL„(›[Œio+1]numbersA°.<numbersB) Select ¨›numbersB
APL„numbersA,¨¨APL
APL„³†,/³¨œ¨(¹×½¨APL)/APL
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

The results match:

LINQ
0 1

APL
0 1

Match?

This query is more complicated than it seems at first sight.
1

Elements of the first vector, numbersA, are replicated as many

Page 23 of 69

0
0
0
0
2
2
2
2
4
4
4
5
5
6
6

3
5
7
8
3
5
7
8
5
7
8
7
8
7
8

0
0
0
0
2
2
2
2
4
4
4
5
5
6
6

3
5
7
8
3
5
7
8
5
7
8
7
8
7
8

times as there are elements in the second vector, numbersB.

The fact that both vectors are sorted in ascending order is a
coincidence; the order is inconsequential.

Elements from the first vector that are greater than all the
elements of the second vector are discarded.

Note the localised function, ‘Select’, that is used by the APL solution. In
order to explore the reason why this is necessary, replace ‘Select’ by ‘/’
on line [6] and investigate.
Read the result row by row: the first number is less than the second
number.

1.4.10 SelectMany - Compound from 2
This query returns all orders whose total is less that 500; each such order is identified by the CustomerID and
OrderID. The list of customers holds each CustomerID uniquely but the ‘orders’ details are recurring.
APL+Win solution is:
’ Z„Linq15;Linq;APL;Select;CustomerID;Orders
© Projection Operators: SelectMany - Compound from 2
Linq„œŒwi 'Linq15'
0 0½Œdef '’ Z„L Select R',Œtcnl,'[1]
Z„L/R ’'
APL„Œwi 'GetCustomerListEx'
© CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5]
CustomerID„Œioœ¨APL
© CustomerID
[6]
Orders„(››1 0 1) Select ¨¨(Œio+9)œ¨APL
© Orders(OrderID 1,
OrderDate 2, OrderValue 3) without OrderDate 2
[7]
APL„(¹¨500>¯1†¨¨Orders) Select ¨Orders
© Order Values
below 500
[8]
(CustomerID APL)„(›×†¨½¨APL) Select ¨CustomerID APL © Remove those
without qualifying OrderValue
[9]
APL„œ¨(›¨›¨CustomerID),¨¨APL
© Add CustomerID
[10] APL„œ,[Œio]/APL
© Return as a 3
column array
[11] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

[1]
[2]
[3]
[4]

The results match. The function Select is localised for the same reasons hinted in the previous example.
You did investigate why this is necessary, didn’t you?

1.4.11 SelectMany - Compound from 3
This query returns all orders made in 1998 or later.

Page 24 of 69

The APL+Win function that produces the matching result is:
’ Z„Linq16;Linq;APL;Select;CustomerID;Orders
© Projection Operators: SelectMany - Compound from 3
Linq„œŒwi 'Linq16'
0 0½Œdef '’ Z„L Select R',Œtcnl,'[1]
Z„L/R ’'
APL„Œwi 'GetCustomerListEx'
© CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5]
CustomerID„Œioœ¨APL
© CustomerID
[6]
Orders„(››1 1 0) Select ¨¨(Œio+9)œ¨APL
© Orders(OrderID 1,
OrderDate 2, OrderValue 3) without OrderValue 3
[7]
APL„(¯1†¨¨Orders)
© Extract the Order
Date UK Short date format DD/MM/YYYY
[8]
APL„100ƒ¨¨¨,¨¨¨´¨¨¨(›››100 100 10000)‚¨¨¨Œfi¨¨¨APL~¨¨¨'/' © Convert to
ISO format YYYYMMDD
[9]
APL„¹¨APL‰19980101
© Selection vector
[10] APL„APL Select ¨Orders
© Orders on or
after 1 January 1998
[11] (CustomerID APL)„(›×†¨½¨APL) Select ¨CustomerID APL © Remove those
without qualifying OrderValue
[12] APL„œ¨(›¨›¨CustomerID),¨¨APL
© Add CustomerID
[13] APL„œ,[Œio]/APL
© Return as a 3
column array
[14] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)
[15] …0
[16] This involves selection by date, a data type that APL+Win does not
support inherently
[17] As this involves a simple date comparison, it is easy to convert the
date to ISO format & use the results as integers

[1]
[2]
[3]
[4]

The LINQ query specifies all orders made in 1998: for APL+Win, I have interpreted this as meaning on or
after first of January 1998. This is necessary because APL+Win lacks the date data type and the supporting
functions that return the constituents of a date value such as year, month, etc. Refer to lines [16]-[17] for other
details. Although it is quite feasible to write the C# code to return the year for this example, I felt that it was
better to keep the function GetCustomerListEx generic—it returns the same result irrespective of the example
where it is used.

1.4.12 SelectMany - from Assignment
This query returns all orders where the order total is greater than 2000.00.

Page 25 of 69

The APL+Win function that returns the result is:
’ Z„Linq17;Linq;APL;Select;CustomerID;Orders
© Projection Operators: SelectMany - from Assignment
Linq„œŒwi 'Linq17'
0 0½Œdef '’ Z„L Select R',Œtcnl,'[1]
Z„L/R ’'
APL„Œwi 'GetCustomerListEx'
© CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5]
CustomerID„Œioœ¨APL
© CustomerID
[6]
Orders„(››1 0 1) Select ¨¨(Œio+9)œ¨APL
© Orders(OrderID 1,
OrderDate 2, OrderValue 3) without OrderDate 2
[7]
APL„(¹¨2000<¯1†¨¨Orders) Select ¨Orders
© Order Values
below 500
[8]
(CustomerID APL)„(›×†¨½¨APL) Select ¨CustomerID APL © Remove those
without qualifying OrderValue
[9]
APL„œ¨(›¨›¨CustomerID),¨¨APL
© Add CustomerID
[10] APL„œ,[Œio]/APL
© Return as a 3
column array
[11] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)
[12] …0
[13] How is the query paradigm different from Linq15? I've included it here
for completeness.

[1]
[2]
[3]
[4]

Note my comment on line [13].

1.4.13 SelectMany - Multiple from
The LINQ documentation for this example reads as follows: “This sample uses multiple from clauses so that
filtering on customers can be done before selecting their orders. This makes the query more efficient by not
selecting and then discarding orders for customers outside of Washington.” In other words, customers from
within Washington are selected first and then their orders.
The APL+Win function is:
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]

’ Z„Linq18;Linq;APL;Select;CustomerID;Orders
© Projection Operators: SelectMany - Multiple from
Linq„œŒwi 'Linq18'
0 0½Œdef '’ Z„L Select R',Œtcnl,'[1]
Z„L/R ’'
APL„Œwi 'GetCustomerListEx'
©
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode
8,Fax 9,Orders 10
APL„(((Œio+4)œ¨APL)¹›'WA')/APL
©
WA Region - <Region> is the fifth element
CustomerID„Œioœ¨APL
©
Orders„(››1 1 0) Select ¨¨(Œio+9)œ¨APL
©
OrderDate 2, OrderValue 3) without OrderValue 3
APL„(¯1†¨¨Orders)
©
Date UK Short date format DD/MM/YYYY

Page 26 of 69

CustomerID
6,Country 7,Phone
Just those in the
CustomerID
Orders(OrderID 1,
Extract the Order

[9]
[10]
[11]
[12]
[13]
[14]
[15]
[16]
[17]
[18]
[19]

APL„100ƒ¨¨¨,¨¨¨´¨¨¨(›››100 100 10000)‚¨¨¨Œfi¨¨¨APL~¨¨¨'/' © Convert to
ISO format YYYYMMDD
APL„¹¨APL‰19970101
© Selection vector
APL„APL Select ¨Orders
© Orders on or
after 1 January 1998
(CustomerID APL)„(›×†¨½¨APL) Select ¨CustomerID APL © Remove those
without qualifying OrderValue
APL„(››1 0) Select ¨¨APL
© Drop OrderValue
APL„œ¨(›¨›¨CustomerID),¨¨APL
© Add CustomerID
APL„œ,[Œio]/APL
© Return as a 2
column array
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)
…0
This involves selection by date, a data type that APL+Win does not
support inherently
As this involves a simple date comparison, it is easy to convert the
date to ISO format & use the results as integers


The results match:
Linq18
LINQ
LAZYK 10482
LAZYK 10545
TRAIH 10574
TRAIH 10577
TRAIH 10822
WHITC 10469
WHITC 10483
WHITC 10504
WHITC 10596
WHITC 10693
WHITC 10696
WHITC 10723
WHITC 10740
WHITC 10861
WHITC 10904
WHITC 11032
WHITC 11066

LAZYK
LAZYK
TRAIH
TRAIH
TRAIH
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC

APL Match?
10482
1
10545
10574
10577
10822
10469
10483
10504
10596
10693
10696
10723
10740
10861
10904
11032
11066

A simple reminder: short results are shown within the body of the document as text and results that are
long are shown partially as a screenshot—you are invited to run the functions to see the results in full. Also,
this document is best viewed with magnification, say 150%, as this renders the screenshots bigger and hence
readable with greater ease.

1.4.14 SelectMany - Indexed
This query returns the CustomerID and OrderID for every order in the database—the Customer list. This query
is interesting in that it returns the results as formatted text. What if the raw results are required?

Page 27 of 69

Superficially, the results do not match! However, if you examine the results more closely, C# returns the
same result as APL+Win; the difference is in the format. The APL+Win function is:
’ Z„Linq19;Linq;APL;Select;CustomerID;Orders
© Projection Operators: SelectMany - Indexed
Linq„œŒwi 'Linq19'
0 0½Œdef '’ Z„L Select R',Œtcnl,'[1]
Z„L/R ’'
APL„Œwi 'GetCustomerListEx'
© CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5]
CustomerID„Œioœ¨APL
© CustomerID
[6]
CustomerID„›¨,¨+\(½CustomerID)/1
© Give each
customer it own cardinal number
[7]
Orders„(››1 0 0) Select ¨¨(Œio+9)œ¨APL
© Orders(OrderID 1,
OrderDate 2, OrderValue 3) Keep OrderID only
[8]
(CustomerID Orders)„(›0¬¹½¨Orders) Select ¨CustomerID Orders © Remove
those without any Orders
[9]
APL„œ¨(›¨›¨CustomerID),¨¨Orders
© Add CustomerID
[10] APL„œ,[Œio]/APL
© Return as a 2
column array
[11] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

[1]
[2]
[3]
[4]

1.5 Partitioning Operators
This group of operators splice their arguments and return subset as their result

1.5.1 Take - Simple
This query uses ‘Take’ to generate a sequence of the first three elements of an integer vector; note that in this
instance, not only do C# LINQ and APL+Win share the jargon but the jargon has an identical meaning in both
contexts.
[1]
[2]
[3]
[4]

’ Z„Linq20;Linq;APL
© Partitioning Operators: Take - Simple
Linq„œŒwi 'Linq20'
APL„3†5 4 1 3 9 8 6 7 2 0
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

Of course, the results match!
Linq20
LINQ
APL Match?
5 4 1 5 4 1
1

1.5.2 Take - Nested
This query returns customer ID, order ID, and order date for the first three orders from customers in
Washington. The sample uses ‘Take’ to limit the sequence generated by the query expression to the first
three of the orders. The hidden complication is that some customers may have zero orders.
Linq21
LINQ
LAZYK 10482 21/03/1997
LAZYK 10545 22/05/1997
TRAIH 10574 19/06/1997

APL Match?
LAZYK 10482 21/03/1997
1
LAZYK 10545 22/05/1997
TRAIH 10574 19/06/1997

The function is:
[1]
[2]
[3]

’ Z„Linq21;Linq;APL;Select;CustomerID;Orders
© Projection Operators: Take - Nested
Linq„œŒwi 'Linq21'
0 0½Œdef '’ Z„L Select R',Œtcnl,'[1]
Z„L/R ’'

Page 28 of 69

[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]
[12]

APL„Œwi 'GetCustomerListEx'
© CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
APL„(((Œio+4)œ¨APL)¹›'WA')/APL
© Just those in the
WA Region - <Region> is the fifth element
CustomerID„Œioœ¨APL
© CustomerID
Orders„(››1 1 0) Select ¨¨(Œio+9)œ¨APL
© Orders(OrderID 1,
OrderDate 2, OrderValue 3) Keep OrderID, OrderDate only
(CustomerID Orders)„(›0¬¹½¨Orders) Select ¨CustomerID Orders © Remove
those without any Orders
APL„œ¨(›¨›¨CustomerID),¨¨Orders
© Add CustomerID
APL„œ,[Œio]/APL
© Return as a 3
column array
APL„œ3†›[Œio+1]APL
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

The APL+Win can compute other variations in this query that may also hide complications.

1.5.2.1 First two customers in Washington and all their orders
Assuming that there are more than two customers with orders in Washington:
[9.5]
APL„2†APL
[11] © APL„œ3†›[Œio+1]APL

1.5.2.2 First 3 customers in Washington and their first 5 orders
Assuming that there are more than three customers in Washington with orders: note that some customers
may have fewer than 5 orders.
[9.5]
[11]

APL„œ,[Œio]/((3,¨1‡¨ ½¨APL)˜½¨APL)†¨APL © First 3 orders of the first
3 customers in Washington APL
© APL„œ3†›[Œio+1]APL

The interesting challenge is in coding C# for these two variations!

1.5.3 Skip - Simple
This query uses ‘Skip’ to get all but the first 4 elements of the array.
Linq22
LINQ
APL Match?
9 8 6 7 2 0 9 8 6 7 2 0
1
The APL+Win solution:
[1]
[2]
[3]
[4]

’ Z„Linq22;Linq;APL
© Partitioning Operators: Skip - Simple
Linq„œŒwi 'Linq22'
APL„4‡5 4 1 3 9 8 6 7 2 0
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

If there are fewer than four elements in the argument, the C# and APL+Win solutions return matching
results; consider this as an exercise!

1.5.4 Skip - Nested
This query returns all but the first two orders from customers in Washington.
[1]
[2]
[3]

’ Z„Linq23;Linq;APL;Select;CustomerID;Orders
© Projection Operators: Skip - Nested
Linq„œŒwi 'Linq23'
0 0½Œdef '’ Z„L Select R',Œtcnl,'[1]
Z„L/R ’'

Page 29 of 69

[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]

APL„Œwi 'GetCustomerListEx'
© CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
APL„(((Œio+4)œ¨APL)¹›'WA')/APL
© Just those in the
WA Region - <Region> is the fifth element
CustomerID„Œioœ¨APL
© CustomerID
Orders„(››1 1 0) Select ¨¨(Œio+9)œ¨APL
© Orders(OrderID 1,
OrderDate 2, OrderValue 3) Keep OrderID, OrderDate only
(CustomerID Orders)„(›0¬¹½¨Orders) Select ¨CustomerID Orders © Remove
those without any Orders
APL„œ¨(›¨›¨CustomerID),¨¨Orders
© Add CustomerID
APL„2 0‡œ,[Œio]/APL
© Return as a 3
column array
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

Linq23
TRAIH
TRAIH
TRAIH
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC

10574
10577
10822
10269
10344
10469
10483
10504
10596
10693
10696
10723
10740
10861
10904
11032
11066

LINQ
19/06/1997
23/06/1997
08/01/1998
31/07/1996
01/11/1996
10/03/1997
24/03/1997
11/04/1997
11/07/1997
06/10/1997
08/10/1997
30/10/1997
13/11/1997
30/01/1998
24/02/1998
17/04/1998
01/05/1998

TRAIH
TRAIH
TRAIH
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC

10574
10577
10822
10269
10344
10469
10483
10504
10596
10693
10696
10723
10740
10861
10904
11032
11066

APL Match?
19/06/1997
1
23/06/1997
08/01/1998
31/07/1996
01/11/1996
10/03/1997
24/03/1997
11/04/1997
11/07/1997
06/10/1997
08/10/1997
30/10/1997
13/11/1997
30/01/1998
24/02/1998
17/04/1998
01/05/1998

1.5.5 TakeWhile - Simple
This query uses ‘TakeWhile’ to generate return all elements from a numeric vector until elements are less than
6. From an APL+Win point of view, this query is trivial.
Linq24
LINQ
APL Match?
5 4 1 3 5 4 1 3
1

[1]
[2]
[3]
[4]
[5]

’ Z„Linq24;Linq;APL
© Partitioning Operators: TakeWhile - Simple
Linq„œŒwi 'Linq24'
APL„5 4 1 3 9 8 6 7 2 0
APL„(^\APL<6)/APL
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.5.6 TakeWhile - Indexed17
This query uses ‘TakeWhile’ to return elements of a numeric vector, starting from the beginning of the vector
until a number is hit that is less than its position in the vector: remember that C# works in index origin 0.

17

This is missing in the links, shown at the start, at http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx

Page 30 of 69

LINQ APL Match?
5 4 5 4
1

[1]
[2]
[3]
[4]

’ Z„Linq25;Linq;APL
© Partitioning Operators: TakeWhile - Indexed
Linq„œŒwi 'Linq25'
APL„(^\APL>(APL¼APL)-Œio)/APL„5 4 1 3 9 8 6 7 2 0
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.5.7 SkipWhile - Simple
This query returns all of the elements of an integer vector, skipping elements until one of them is divisible by
three.
LINQ
3 9 8 6 7 2 0

[1]
[2]
[3]
[4]
[5]

APL Match?
3 9 8 6 7 2 0
1

’ Z„Linq26;Linq;APL
© Partitioning Operators: SkipWhile - Simple
Linq„œŒwi 'Linq26'
APL„5 4 1 3 9 8 6 7 2 0
APL„(Ÿ\0=3|APL)/APL
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.5.8 SkipWhile - Indexed
This query returns all of the elements of an integer vector, skipping elements until the element's value is less
that its position in the array: remember that C# works in index origin 0.
Linq27
LINQ
1 3 9 8 6 7 2 0

[1]
[2]
[3]
[4]

APL Match?
1 3 9 8 6 7 2 0
1

’ Z„Linq27;Linq;APL
© Partitioning Operators: SkipWhile - Indexed
Linq„œŒwi 'Linq27'
APL„(Ÿ\APL<(-Œio)+¼½APL)/APL„5 4 1 3 9 8 6 7 2 0
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.6 Ordering Operators
This group of operators re-order or sort their arguments according to different criteria. They include simple,
even trivial, examples that illustrate principles. Further refinement to the techniques is necessary to build reusable code that incorporates the same techniques.

1.6.1 OrderBy - Simple 1
This query demonstrates a simple alphabetical sort. Although the example does not demonstrate this, the
sorting is case insensitive: I have added a word in upper case to the argument to demonstrate.
LINQ

Linq28
APL

apple
ASKOOLUM
blueberry
cherry

[1]
[2]
[3]
[4]

apple
ASKOOLUM
blueberry
cherry

Match?
1

’ Z„Linq28;Linq;APL
© Ordering Operators: OrderBy - Simple 1
Linq„œŒwi 'Linq28'
APL„"cherry" "apple" "blueberry" "ASKOOLUM" © Case insensitive
APL„œAPL[“Œav¼œLCase ¨APL]

Page 31 of 69

[5]

Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.6.2 OrderBy - Simple 2
This query returns its vector argument sorted in ascending order of the length of each element.
LINQ

Linq29
APL

apple
cherry
blueberry

Match?

apple
cherry
blueberry

1

The function:
[1]
[2]
[3]
[4]
[5]

’ Z„Linq29;Linq;APL
© Ordering Operators: OrderBy - Simple 2
Linq„œŒwi 'Linq29'
APL„"cherry" "apple" "blueberry"
APL„œAPL[”' '+.=³œAPL]
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.6.3 OrderBy - Simple 3
This query returns all of the products sorted alphabetically by the product name.

This result is interesting: it does not match!
’ Z„Linq30;Linq;APL
© Ordering Operators: OrderBy - Simple 3
Linq„œŒwi 'Linq30'
APL„Œwi 'GetProductListEx' © ProductID ProductName Category UnitPrice
UnitsInStock
[4]
APL„(œAPL)[“Œav¼œ(Œio+1)œ¨APL;]
[5]
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

[1]
[2]
[3]

1.6.3.1 Where is the difference?
On stopping the function after line [4] and running a query to isolate the differences yields:

Page 32 of 69

The results match in terms of the number of rows. The difference lies with the character data. C# sorts the
results and passes it to APL+Win; it also passes the raw data to APL+Win. There is loss of accuracy arising
from the lack of support for Unicode in APL+Win; in contrast, C# uses Unicode. APL+Win is sorting the data
after the loss of accuracy whereas it is the C# result—not the data—that suffers the same loss.

1.6.4 OrderBy - Comparer
This example returns a string vector sorted according to a case insensitive alphanumeric sort.
Linq31
LINQ
AbAcUs aPPLE
APL
AbAcUs aPPLE
Match?
1

BlUeBeRrY bRaNcH cHeRry ClOvEr
BlUeBeRrY bRaNcH cHeRry ClOvEr

The APL+Win function:
[1]
[2]
[3]
[4]
[5]

’ Z„Linq31;Linq;APL
© Ordering Operators - OrderBy - Comparer
Linq„Œwi 'Linq31'
APL„"aPPLE" "AbAcUs" "bRaNcH" "BlUeBeRrY" "ClOvEr"
APL„APL[“Œav¼UCase œAPL]
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

"cHeRry"

In order to keep the results comparable, I have not used any API calls in the APL+Win solution.

1.6.5 OrderByDescending - Simple 1
This query returns a vector of floating point numbers sorted in descending order.
Linq32
LINQ
4.1
APL
4.1
Match? 1.0

2.9
2.9

2.3
2.3

1.9
1.9

1.7
1.7

The function:

[1]
[2]
[3]
[4]
[5]

’ Z„Linq32;Linq;APL
© Ordering Operators - OrderByDescending - Simple 1
Linq„Œwi 'Linq32'
APL„1.7 2.3 1.9 4.1 2.9
APL„APL[”APL]
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.6.6 OrderByDescending - Simple 2
This query returns a list of products sorted by the number of units of each product that are in stock.

Page 33 of 69

The APL+Win function:
’ Z„Linq33;Linq;APL
© Ordering Operators - OrderByDescending - Simple 2
Linq„œŒwi 'Linq33'
APL„Œwi 'GetProductListEx'
APL„œAPL[”(Œio+4)œ¨Œwi 'GetProductListEx'] © ProductID ProductName
Category UnitPrice UnitsInStock
[5]
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

[1]
[2]
[3]
[4]

1.6.7 OrderByDescending - Comparer
This query returns a vector of strings sorted alphabetically in descending order, using a case insensitive
comparison.
Linq34
LINQ
ClOvEr cHeRry bRaNcH BlUeBeRrY aPPLE
APL
ClOvEr cHeRry bRaNcH BlUeBeRrY aPPLE
Match?
1

AbAcUs
AbAcUs

The APL+Win function:
[1]
[2]
[3]
[4]
[5]

’ Z„Linq34;Linq;APL
© Ordering Operators - OrderByDescending - Comparer
Linq„Œwi 'Linq34'
APL„"aPPLE" "AbAcUs" "bRaNcH" "BlUeBeRrY" "ClOvEr"
APL„APL[”Œav¼LCase œAPL]
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

"cHeRry"

1.6.8 ThenBy - Simple
This sample uses a compound ‘orderby’ to sort a list of digits first by length of their name, and then
alphabetically.
Linq35
LINQ
one six
APL
one six
Match?
1

two
two

five
five

four
four

nine
nine

zero
zero

eight
eight

seven
seven

’ Z„Linq35;Linq;APL
© Ordering Operators - ThenBy - Simple
Linq„Œwi 'Linq35'
APL„"zero" "one" "two" "three" "four" "five" "six"
"eight" "nine"
[4]
APL„APL[“Œav¼LCase œAPL]
[5]
APL„APL[“¹½¨APL]
[6]
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

[1]
[2]
[3]

Page 34 of 69

three
three

"seven"

1.6.9 ThenBy - Comparer
This sample prints an array of string values sorted first by length, then sorted alphabetically, using a caseinsensitive comparison.

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]

’ Z„Linq36 words;Linq;APL
© Ordering Operators - ThenBy - Comparer
Linq„Œwi 'Linq36' words
words„words[“Œav¼LCaseœwords]
APL„words[“¹½¨words]
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)
…0
Linq36 "aPPLE" "AbAcUs" "bRaNcH" "BlUeBeRrY" "ClOvEr" "cHeRry"
Linq36 "zero" "oNe" "TWo" "tHRee" "foUR" "FIVE" "sIx" "SEVEn"
"eigHt" "NIne" "TEn" "HundRED"

1.6.10 ThenByDescending - Simple
This sample uses a compound ‘orderby’ to sort a list of products, first by category in ascending order, and
then by unit price in descending order.


[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]

Z„Linq37;Linq;APL
© Ordering Operators - ThenByDescending - Simple
Linq„œŒwi 'Linq37'
APL„Œwi 'GetProductListEx'
APL„œAPL[(”(Œio+3)œ¨APL)[“Œav¼œ ((Œio+2)œ¨APL) [”(Œio+3)œ¨APL]]]
Z„(›¨'LINQ' 'APL' 'Match?'),[Œio-0.5]Linq APL (Linq−APL)
…0
© Take it a little slower!
APL„Œwi 'GetProductListEx'
© Get the Product List
UnitPrice„(Œio+3)œ¨APL
© UnitProce is the 4th element
Category„(Œio+2)œ¨APL
© Category is the 3rd element
APL„œAPL[(”UnitPrice)[“Œav¼œCategory[”UnitPrice]]]

1.6.11 ThenByDescending - Comparer
This sample uses an ‘OrderBy’ and a ‘ThenBy’ clause with a custom comparer to sort first by word length and
then by a case-insensitive descending sort of the words in an array.

Page 35 of 69

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]

’ Z„Linq38 words;Linq;APL
© Ordering Operators: ThenByDescending - Comparer
Linq„Œwi 'Linq38' words
words„words[“Œav¼LCase œwords]
APL„words[²”¹½¨words]
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)
…0
Linq38 "aPPLE" "AbAcUs" "bRaNcH" "BlUeBeRrY" "ClOvEr" "cHeRry"
Linq36 "zero" "oNe" "TWo" "tHRee" "foUR" "FIVE" "sIx" "SEVEn"
"eigHt" "NIne" "TEn" "HundRED"

1.6.12 Reverse
This query returns a list strings from an input string vector where each element has the second letter 'i'. The
output list the elements in reverse order. The sample uses a query operator to gather the elements that have
'i' as the second letter and then reverses the sequence using the Reverse method.
Linq39
LINQ
nine eight
APL
nine eight
Match?
1

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]

six
six

five
five

’ Z„Linq39;Linq;APL
© Ordering Operators: ThenByDescending - Comparer
words„"zero" "one" "two" "three" "four" "five" "six" "seven"
"eight" "nine"
Linq„Œwi 'Linq39' words
APL„²(¹'i'=1†¨1‡¨words)/words
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)
…0
APL„²(¹'i'Ÿ.¹¨words)/words © to pick up words with at least one 'i'
anywhere
APL„²(¹'io'Ÿ.º¨words)/words © to pick up words with at least one
substring 'io' anywhere

1.7 Grouping Operators
This group of operators collates elements contiguously depending on applicable criteria.

1.7.1 GroupBy - Simple 1
This query uses group by to partition a list of numbers by their remainder when divided by 5.
LINQ
5
0
4
9
1
6

0
0
4
4
1
1

Linq40
APL
5
0
4
9
1
6

Page 36 of 69

0
0
4
4
1
1

Match?
1

3
8
7
2

3
3
2
2

3
8
7
2

3
3
2
2

The following function produces the matching result:
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]

’ Z„Linq40;L;R;Linq;APL
© Grouping Operators: GroupBy - Simple 1
Linq„³Œwi 'Linq40'
APL„5 4 1 3 9 8 6 7 2 0
L„5|APL
R„((L¼L)=¼½L)/L
(APL L)„œ¨(›“R¼L)Þ¨¨›¨L APL
APL„³œL APL
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.7.2 GroupBy - Simple 2
This sample partitions a vector of words into groups according to the first letter of each word.

b
b
c
c
a
a

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]

Linq41
LINQ
blueberry
banana
chimpanzee
cheese
abacus
apple

b
b
c
c
a
a

APL Match?
blueberry
1
banana
chimpanzee
cheese
abacus
apple

’ Z„Linq41;L;R;Linq;APL
© Grouping Operators: GroupBy - Simple 2
Linq„³Œwi 'Linq41'
APL„"blueberry" "chimpanzee" "abacus" "banana" "apple"
L„1†¨APL
R„((L¼L)=¼½L)/L
(L APL)„ œ¨(›“R¼L)Þ¨¨›¨L APL
APL„³œL APL
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

"cheese"

1.7.3 GroupBy - Simple 3
This sample uses group by to partition a list of products by category. Coding the C# method to return the
result to APL+Win was nothing short of an unpleasant ordeal, given the simplicity of the query.
The APL function:

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]

Z„Linq42;Linq;APL;Category;SelectByCategory;Select
© Grouping Operators - Simple 3
Linq„Œwi 'Linq42' © œœ¨¨Œwi 'Linq42'
APL„Œwi 'GetProductListEx'
0 0½Œdef '’ Z„L Select R',Œtcnl,'[1]
Z„L/R ’'
Category„(Œio+2)œ¨APL
DistinctCategory„((Category¼Category)=¼½Category)/Category
SelectByCategory„›[Œio+1](DistinctCategory)°.−Category
APL„((›¨DistinctCategory),¨›¨SelectByCategory Select ¨›APL)
Z„(œœ¨¨Linq) (œœ¨¨APL)
Z„(›¨'APL' 'Linq' 'Match?'),[Œio-0.5]Z ,›(Linq−APL)

Page 37 of 69

You will need to run the query in order to see the detail of the result. As an aside, this illustrates the
possibilities of created nested variables in C# in ways that make them compatible with ones in APL+Win.

1.7.4 GroupBy - Nested
1.7.5 GroupBy – Comparer
This query and the next one delivers the same functionality as the next one except in one respect: it does not
convert the result to uppercase. It also suffers from the same deficiencies
Linq44
LINQ
from
salt
last
earn near
FORM

APL
from FORM
salt last
earn near

Match?
0

The comparison fails since I have corrected the anomalies of the C# solution when coding the APL+Win
function:

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]

Z„Linq44;Linq;APL
© Grouping Operators - GroupBy - Comparer
Linq„œŒwi 'Linq44'
APL„" from " " salt " " earn " "last" "near" "FORM"
APL„(+/¨^\¨ ' '=¨APL)‡¨APL
APL„(-+/¨^\¨²¨' '=¨APL)‡¨APL
a„((a¼a)=¼½a)/a„¹LCase ¨APL
b„(›¨“¨(›a)¼¨LCase ¨APL)Þ¨LCase ¨APL
c„b¼b
APL„œ(›[Œio+1]((c=¼½c)/c)°.=c)Select ¨›APL
Z„(›¨'LINQ' 'APL' 'Match?'),[Œio-0.5]Linq APL (Linq−APL)

1.7.6 GroupBy – Comparer, Mapped
This sample displays which elements from an input string array have the same letters. The sample used
GroupBy and an AnagramEqualityComparer class to group the elements into those with the same letters.
Note the conversion to uppercase.
The C# sample solution is deficient! In order to illustrate this I have added two more pairs of phrases to
the arguments used in the sample.
Linq45
LINQ
FROM

Page 38 of 69

FORM

APL
FROM

FORM

Match?
0

SALT
LAST
EARN
NEAR
AJAY
ASKOOLUM
DEBIT CARD
BAD CREDIT
SLOW
OWLS

SALT
EARN
AJAY
ASKOOLUM
DEBIT CARD
OWLS

LAST
NEAR
BAD CREDIT
SLOW

The C# solution fails to allow for embedded spaces, although it does trim leading and trailing spaces,
which should be inconsequential in finding anagrams.
The C# solution also fails to cope with the case of the phrases, although it converts the result—not the
arguments—to uppercase.
The APL+Win solution, which corrects the C# deficiencies, is:
’ Z„Linq45;Linq;APL
© Grouping Operators - GroupBy - Comparer, Mapped
Linq„œŒwi 'Linq45'
APL„UCase¨ " from " " salt " " earn " "last" "near" "FORM" "ajay"
"askoolum" "Debit Card" "Bad Credit" "OWLS" "slow"
[4]
APL„(+/¨^\¨ ' '=¨APL)‡¨APL
[5]
APL„(-+/¨^\¨²¨' '=¨APL)‡¨APL
[6]
a„((a¼a)=¼½a)/a„¹APL
[7]
b„ (›¨“¨(›a)¼¨APL)Þ¨APL
[8]
c„b¼b
[9]
APL„œ(›[Œio+1]((c=¼½c)/c)°.=c)Select ¨›APL
[10] Z„(›¨'LINQ' 'APL' 'Match?'),[Œio-0.5]Linq APL (Linq−APL)

[1]
[2]
[3]

1.8 Set Operators
As the name implies, this group of operators apply to vectors of numbers or literals. APL+Win is naturally
disposed to excel in this context.

1.8.1 Distinct - 1
The sample creates a sequence of unique factors by using the Distinct method on the integer vector of factors
to remove duplicates.
Linq46
LINQ
2
APL
2
Match? 1

3
3

5
5

’ Z„Linq46;Linq;APL
© Set Operators: Distinct - 1
Linq„Œwi 'Linq46' (R„2 2 3 5 5)
APL„((R¼R)=¼½R)/R
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)
…0
Linq46 2 2 3 5 5 © Original example
Linq46 9 8 7 6 2 1 2 3 9 8 7 6 5 2 3 4 5 9 0 8 71

I have coded t he C# method to accept any arbitrary but compatible argument .

[1]
[2]
[3]
[4]
[5]
[6]
[7]

Linq46 9 8 7 6 2 1 2 3 9 8 7 6 5 2 3 4 5 9 0 8 71
LINQ
2
3
5
9 8 7 6 2 1 2 3 9 8 7 6 5 2 3 4 5 9 0 8 71
APL
2
3
5
Match? 1

1.8.2 Distinct - 2
This query returns the unique Category names by first selecting all of the category names from the product
list, then using Distinct to remove duplicate names.

Page 39 of 69

Linq47
LINQ

APL

Match?

Beverages
Condiments
Produce
Meat/Poultry
Seafood
Dairy Products
Confections
Grains/Cereals

Beverages
Condiments
Produce
Meat/Poultry
Seafood
Dairy Products
Confections
Grains/Cereals

1

The APL+Win function:
’ Z„Linq47;Linq;APL
© Set Operators: Distinct - 2
Linq„œŒwi 'Linq47'
APL„(Œio+2)œ¨Œwi 'GetProductListEx' © ProductID ProductName Category
UnitPrice UnitsInStock
[4]
APL„œ((APL¼APL)=¼½APL)/APL
[5]
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

[1]
[2]
[3]

1.8.3 Union - 1
This query returns the unique elements of two integer arrays. The sample uses the Union method to create a
sequence that is a union of the two integer arrays with duplicate elements removed.

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]

’ Z„Linq48;L;R;Linq;APL
© Set Operators: Union - 1
L„¹(2?10)?¨15 © With possible replication
R„¹(2?10)?¨23 © With possible replication
Linq„Œwi 'Linq48' L R
L„((L¼L)=¼½L)/L
R„((R¼R)=¼½R)/R
APL„L,R~L
Z„•œ(›¨'L' 'R' 'LINQ' 'APL' 'Match?'),¨L R Linq APL (Linq−APL)

This function is coded to use random arguments; therefore, it will return a different result simply because
the argument is different.

1.8.4 Union - 2
This query returns unique letters from Product and Customer names. The sample creates a sequence of
product first characters, a sequence of customer first characters, and then joins the two sets by using Union to
merge them and remove duplicates.
Linq49
LINQ APL Match?
C
A
G

C
A
G

Page 40 of 69

1

[1]
[2]
[3]

’ Z„Linq49;L;R;Linq;APL
© Set Operators: Union - 2
Linq„œŒwi 'Linq49'
© (ProductID ProductName Category UnitPrice

U
N
M
I
Q
K
T
P
S
R
B
J
Z
V
F
E
W
L
O
D
H

U
N
M
I
Q
K
T
P
S
R
B
J
Z
V
F
E
W
L
O
D
H

UnitsInStock) (© CustomerID CompanyName
Address City Region PostalCode Country Phone
Fax Orders[])
L„1†¨(Œio+1)œ¨Œwi 'GetProductListEx'
R„1†¨(Œio+1)œ¨Œwi 'GetCustomerListEx'
L„((L¼L)=¼½L)/L
R„((R¼R)=¼½R)/R
APL„œL,R~L
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL
(Linq−APL)

[4]
[5]
[6]
[7]
[8]
[9]

1.8.5 Intersect - 1
This query returns a list of numbers that are common to two integer vectors. The sample uses Intersect to
create one sequence that contains the common values shared by vectors.
Linq50
L
3
R
12
LINQ
13
APL
13
Match? 1

5
2
4
4

13
2
8
8

0
22

10
9

6
4

4
13

8
21

8

The APL+Win function :
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]

’ Z„Linq50;L;R;Linq;APL
© Set Operators: Intersect - 1
L„¹(2?10)?¨15 © With possible replication
R„¹(2?10)?¨23 © With possible replication
Linq„Œwi 'Linq50' L R
APL„(L¹R)/L
APL„((APL¼APL)=¼½APL)/APL
Z„•œ(›¨'L' 'R' 'LINQ' 'APL' 'Match?'),¨L R Linq APL (Linq−APL)
…0
(L R)„(0 2 4 5 6 8 9) (1 3 5 7 8)

This function generates its arguments randonmly and uses each such argument and passes the same
argument to the C# method. The original arguments uses in the sample are shown in line [9].

1.8.6 Intersect - 2
This query returns the letters that are both the first letter of a Product name and the first letter of a Customer
name. The sample uses query statements to create two sequence - the first letters of Product names and the
first letter of Customer names, then uses Intersect to create a sequence of letters common to both.
Linq51
LINQ APL Match?
C
A
G
N

C
A
G
N

1

[1]
[2]
[3]

’ Z„Linq51;L;R;Linq;APL
© Set Operators: Intersect - 2
Linq„œŒwi 'Linq51'
© (ProductID ProductName Category UnitPrice
UnitsInStock) (© CustomerID CompanyName
Address City Region PostalCode Country Phone

Page 41 of 69

M
I
Q
K
T
P
S
R
B
V
F
E
W
L
O

M
I
Q
K
T
P
S
R
B
V
F
E
W
L
O

Fax Orders[])
L„1†¨(Œio+1)œ¨Œwi 'GetProductListEx'
R„1†¨(Œio+1)œ¨Œwi 'GetCustomerListEx'
APL„(L¹R)/L
APL„œ((APL¼APL)=¼½APL)/APL
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL
(Linq−APL)

[4]
[5]
[6]
[7]
[8]

1.8.7 Except - 1
This query returns numbers that are in one integer array, but not another. The sample uses Except to create a
sequence that contains the values from numbersA that are not also in numbersB.
Linq52
L
9
R
12
LINQ
9
APL
9
Match? 1

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]

11
2
11
11

7
18
7
7

0
22
3
3

3
0
8
8

12
19

8
5

’ Z„Linq52;L;R;Linq;APL
© Set Operators: Except 1
L„¹(2?10)?¨15 © With possible replication
R„¹(2?10)?¨23 © With possible replication
Linq„Œwi 'Linq52' L R
L„((L¼L)=¼½L)/L
APL„(~L¹R)/L
Z„•œ(›¨'L' 'R' 'LINQ' 'APL' 'Match?'),¨L R Linq APL (Linq−APL)
…0
(L R)„(0 2 4 5 6 8 9)(1 3 5 7 8)

1.8.8 Except - 2
This query returns the first character of product names that are not also the first character of customer names.
After getting the first characters of product and customer names using query expressions, the sample uses
Except to create a sequence of product characters that doesn't include first characters of customer names.
LINQ
U
J
Z

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]

Linq53
APL Match?
U
1
J
Z

’ Z„Linq53;L;R;Linq;APL
© Set Operators: Except - 2
Linq„œŒwi 'Linq53'
© (ProductID ProductName Category UnitPrice UnitsInStock) (© CustomerID
CompanyName Address City Region PostalCode Country Phone Fax Orders[])
L„1†¨(Œio+1)œ¨Œwi 'GetProductListEx'
R„1†¨(Œio+1)œ¨Œwi 'GetCustomerListEx'
L„((L¼L)=¼½L)/L
APL„œ(~L¹R)/L
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

Page 42 of 69

1.9 Conversion Operators
From an APL+Win point of view, this group of operators contain trivial examples. However, in a C# context,
the examples illustrate the ease with which several operations can be carries out seamlessly using LINQ
techniques. The other purpose of these examples is too illustrate the conversion from one complex type to
another: APL+Win does not support complex types but can reproduce the results quite easily.

1.9.1 To Array
This sample generates a vector of double values by first using a query expression with orderby to create a
sorted sequence of doubles, then ToArray to generate an array from the sequence.
Linq54
LINQ
4.1
APL
4.1
Match? 1.0

2.3
2.3

1.7
1.7

The APL+Win function:
’ Z„Linq54;Linq;APL
© Conversion Operators: To Array
APL„1.7 2.3 1.9 4.1 2.9
Linq„Œwi 'Linq54' APL
APL„(2|+\(½APL)/1)/APL[”APL] © Œio independent
Z„œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Note that the value for ‘Match’ is shown as a double: this is a consequence of line [5], that is of formatting
the result. It is Boolean as you would expect.
[1]
[2]
[3]
[4]
[5]

1.9.2 To List
This sample generates an array of string values by first using a query expression with orderby to create a
sorted sequence of strings from a string array, then toList to generate a List from the sequence. The
conversion operation is from an array to a list.
Linq55
LINQ
apple blueberry cherry
APL
apple blueberry cherry
Match?
1
The APL+Win function:
[1]
[2]
[3]
[4]
[5]

’ Z„Linq55;Linq;APL
© Conversion Operators: To List
Linq„Œwi 'Linq55'
APL„"cherry" "apple" "blueberry"
APL„APL[“Œav¼œAPL]
Z„œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.9.3 To Dictionary
This sample uses anonymous types to create a data structure of people and their scores. It then uses
ToDictionary to generate a dictionary from the sequence and its key expression.
A Dictionary is a complex data type in C#. Essentially, it is a name/value pair array with a notable
difference: the array is indexed by the name and not an ordinal value that would represent an index. For
example:
Console.WriteLine("Bob's score: {0}", scoreRecordsDict["Bob"]);
This yields the following result:
Bob's score: {Name=Bob, Score=40}

Page 43 of 69

This is not available in APL+Win; however, it is quite straightforward to simulate the same effect.
Linq56
LINQ
Bob
APL
Bob
Match?
1

[1]
[2]
[3]
[4]
[5]

40
40

’ Z„Linq56;Linq;APL
© Conversion Operators: To Dictionary
Linq„Œwi 'Linq56'
APL„("Alice" 50) ("Bob" 40) ("Cathy" 45)
APL„,œ((›"Bob") −¨†¨APL)/APL
Z„œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.9.4 OfType
This sample uses OfType to return only the elements of the array that are of type double.
This sample is rather tricky for APL+Win: C# recognises a double number when a decimal point is
present, even when the decimal part is zero. In many situations, APL+Win silently converts any number with a
decimal part of zero to an integer. The objective is to identify elements of different types: for convenience, I
have altered the decimal part of doubles in the argument of this operator to a non-zero value.
Linq57
LINQ
1.1
APL
1.1
Match? 1.0

[1]
[2]
[3]
[4]
[5]

7.1
7.1

’ Z„Linq57;Linq;APL
© Conversion Operators: OfType
Linq„Œwi 'Linq57'
APL„(0/0) 1.1 "two" 3 "four" 5 "six" 7.1
APL„(645=Œdr ¨APL)/APL
Z„œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.10 Element Operators
This group of operators illustrate techniques for working with elements of vectors. This group contains trivial
examples, which are valuable nonetheless in one vital respect: when studying the examples, consider the
comparative readability of the C# and APL+Win code.

1.10.1 First - Simple
This sample uses ‘First’ to return the first matching element as a Product, instead of as a sequence containing
a Product; a Product is a complex data type containing named data items, in this case, ProductID,
ProductName, Category, UnitPrice, and UnitsInStock

Linq58
LINQ
12 Queso Manchego La Pastora Dairy Products
APL
12 Queso Manchego La Pastora Dairy Products
Match? 1
This function produces the matching result:

Page 44 of 69

38
38

86
86

’ Z„Linq58;Linq;APL
© Element Operators: First - Simple
Linq„Œwi 'Linq58'
APL „Œwi 'GetProductListEx'
APL„,œ(<\12=Œioœ¨APL)/APL
Z„œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Note that this query returns only the first matching element.

[1]
[2]
[3]
[4]
[5]

1.10.2 First – Condition (missing at URL)
This sample uses ‘First’ to find the first element in a vector that starts with 'o'.
Linq59
LINQ one
APL one

Match? 1

The function:
’ Z„Linq59;Linq;APL
© Element Operators: First - Condition
Linq„Œwi 'Linq59'
APL„ "zero" "one" "two" "three" "four" "five" "six"
"nine"
[4]
APL„†(¹'o'=†¨APL)/APL
[5]
Z„(›¨'LINQ' 'APL' 'Match?'),¨,¨Linq APL (Linq−APL)

[1]
[2]
[3]

"seven"

"eight"

1.10.3 First - Indexed
This example returns the first element of an integer vector that is both even and exists at an even index within
the vector. The code sample, as given at the URL, does not work! The culprit line is:
int evenNum = numbers.First((num, index) => (num % 2 == 0) && (index % 2 == 0));
It generates the following error:
Delegate 'System.Func<int,bool>' does not take '2' arguments
I can only surmise that Microsoft published the code sample prior to the release of C# 3.0. The corrected
line that produces the expected result is:
int evenNum = numbers.Where((num, index) => (num % 2 == 0) && (index % 2 == 0)).First();
The APL function is:
’ Z„Linq60;Linq;APL
© Element Operators: First - Indexed
Linq„œŒwi 'Linq60'
APL„5 4 1 3 9 8 6 7 2 0
APL„†((~2|APL)^~2|(-Œio)+APL¼APL)/APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

The matching result is:

[1]
[2]
[3]
[4]
[5]

Linq60
LINQ
6
APL
6
Match? 1

1.10.4 FirstOrDefault - Simple
This query returns the default value of a data type; the example uses the integer data type whose default
value is 0. This is possible only in a declarative language such as C#; APL is not a declarative language.
’ Z„Linq61;Linq;APL

Page 45 of 69

[1]
[2]
[3]

© Element Operators: FirstOrDefault - Simple
Linq„œŒwi 'Linq61'
© Create a variable from which the data type integer (323) can be
inferred
APL„9 10
© Create an empty value from the variable - this emulates declaration
APL„0/APL
© Take the first or default value
APL„†APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

[4]
[5]
[6]
[7]
[8]
[9]

However, it is possible to demonstrate the concept using APL+Win, using inference.

1.10.5 FirstOrDefault - Condition
This query uses ‘FirstOrDefault’ to return the first product whose ProductID is 789; if there is no match,
null is returned. Given the lack of support for the null data type in APL+Win, it is straightforward to modify
the method to return true or false.
Linq62
LINQ
0
APL
0
Match? 1
The function:
[1]
[2]
[3]
[4]

’ Z„Linq62;Linq;APL
© Element Operators: FirstOrDefault - Condition
Linq„œŒwi 'Linq62'
APL„789¹Œioœ¨Œwi 'GetProductListEx' © First element is ProductID
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.10.6 FirstOrDefault - Indexed
This query uses ‘FirstOrDefault’ to find the first number in a vector that is within 0.5 of its position in the vector
and returns either the first match or the default value of data type double, which is null.
This one is tricky! Not least because the sample code at the URL does not work. Even with correction, it
still presents two problems for APL+Win:

It does not recognise a method in a COM DLL that returns null.

A vector cannot contain a null value that is recognisable as null by the COM Server.

I propose a workaround: I will modify the query to return either the first number from its argument that is
between -0.5 and 0.5 of its position—remember the position is in index origin zero—or return the smallest
number if none is found.

Page 46 of 69

Let us explore the deliverable without null values, without recourse to the COM Server, and with the
following APL+Win function:

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]

Z„Linq63Ex APL
© Element Operators: FirstOrDefault - Indexed
© Linq„œŒwi 'Linq63'
© APL„1.7 2.3 4.1 1.9 2.9
APL„(0=+/×(³¯0.5 0.5°.+(-Œio)+¼½APL)-[Œio]APL)/APL
:if 0=½APL
Z„˜/Ð
:else
Z„нAPL
:endif
©Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

The APL+Win that delivers the same solution as the COM method—subject to the limitations discussed—
is as follows:
’ Z„Linq63;Linq;APL
[1]
© Element Operators: FirstOrDefault - Indexed
[2]
Linq„œŒwi 'Linq63'
[3]
APL„1.7 2.3 4.1 1.9 2.9
[4]
APL„(0=+/×(³¯0.5 0.5°.+(-Œio)+¼½APL)-[Œio]APL)/APL
[5]
:if 0=½APL
[6]
APL„˜/0
[7]
:else
[8]
APL„нAPL
[9]
:endif
[10] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

The result:
Linq63
Actual Result 1
LINQ
1.797693135E308
APL
1.797693135E308
Match? 1.000000000E0
There are no elements within ±0.5 of its position in the vector.

1.10.7 ElementAt
This query returns the fourth number less that 5 in an integer vector. The sample first uses a query expression
then uses ‘ElementAt’ to retrieve the fourth number from this sequence. Since ElementAt uses 0-based
indexing, 3 is passed to the method to retrieve the fourth element.
Linq64
LINQ
2
APL
2
Match? 1

Page 47 of 69

The matching result is produced by the following APL+Win function:
[1]
[2]
[3]
[4]
[5]

’ Z„Linq64;Linq;APL
© Element Operators: ElementAt
Linq„œŒwi 'Linq64'
APL„5 4 1 3 9 8 6 7 2 0
APL„н3‡(APL<5)/APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.11 Generation Operators
This group of queries or operators generate sequences: the samples provided generate sequences of
integers. What is striking is the simplicity of the APL+Win code to generate identical results produced by the
C# code.

1.11.1 Range
This query uses ‘Range’ to generate a sequence of numbers from 100 to 149 and then classifies each
element of the resulting vector as either odd or even.
[1]
[2]
[3]
[4]
[5]

’ Z„Linq65;Linq;APL
© Generation Operators: Range
Linq„³œŒwi 'Linq65'
APL„100+(-Œio)+¼50
APL„³œAPL (('even' 'odd')[Œio+2|APL])
Z„•(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

For APL+Win, this group of operators are trivial or routine.

1.11.2 Repeat
In the context of APL+Win, this is another trivial example. Moreover, the APL+Win technique of replication
applies equally to other types of data.
Linq66
LINQ
7
APL
7
Match? 1

7
7

7
7

7
7

7
7

7
7

7
7

The APL+Win function:
[1]
[2]
[3]
[4]

’ Linq66;Linq;APL
© Generation Operators: Repeat
Linq„Œwi 'Linq66'
APL„10/7
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Page 48 of 69

7
7

7
7

7
7

1.12 Quantifiers
This group of operators apply criteria within the elements of vectors. APL+Win does this type of processing
quite naturally using standard primitives.

1.12.1 Any - Simple
This query determines whether any words in a string array contain the character sequence 'ei'; it returns
1 or true if at least one element contains the designated substring.
Linq67
LINQ
1
APL
1
Match? 1

[1]
[2]
[3]
[4]
[5]

’ Z„Linq67;Linq;APL
© Quantifiers: Any - Simple
Linq„œŒwi 'Linq67'
APL„"believe" "relief" "receipt" "field"
APL„1¹(›'ie')Ÿ.º¨APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.12.2 Any - Indexed
The code sample given at the URL is plainly wrong! This sample determines whether any numbers in an
integer array are the negative of their position in the vector.
The culprit line is:
bool negativeMatch = numbers.Any((n, index) => n == -index);
The correction is:
bool negativeMatch = numbers.Select((n, index) => new { n, index }).Any(x => x.n == -x.index);
This is an appropriate point to pause and consider the readability of APL+Win code, comparing it to the
corresponding C# code.
Linq68
LINQ
0
APL
0
Match? 1
This function produces the matching result:
[1]
[2]
[3]
[4]
[5]

’ Z„Linq68;Linq;APL
© Quantifiers: Any - Indexed
APL„¯9 4 ¯8 3 ¯5 2 ¯1 6 ¯7
Linq„Œwi 'Linq68' APL
APL„×½(APL=-(-Œio)+¼½APL)/APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.12.3 Any - Grouped
1.12.4 All - Simple
This query uses All to determine whether an array contains only odd numbers. This type of query
represents very basic APL+Win skills of novices!
LINQ
APL

Linq70
1
1

Page 49 of 69

Match? 1
This function produces the matching result:
[1]
[2]
[3]
[4]
[5]

’ Z„Linq70;Linq;APL
© Quantifiers: All - Simple
Linq„œŒwi 'Linq70'
APL„1 11 3 19 41 65 19
APL„1^.=2|APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.12.5 All - Indexed
The sample code given for this example is also incorrect. The following line is incorrect:
bool allLower = lowNumbers.All((num, index) => num < highNumbers[index]);
It creates an error that prevents the code from compiling:
System.Func<int,bool>' does not take '2' arguments
The correct line is:
bool allLower = lowNumbers.Select((n, index) => new { n, index }).All(x => x.n < highNumbers[x.index]);
This sample determines whether every element of an integer vector is lower than the corresponding
element in another integer vector; the two vectors are conformable.
Linq71
LINQ
1
APL
1
Match? 1

[1]
[2]
[3]
[4]
[5]
[6]

’ Z„Linq71;Linq;APL;lowNumbers;highNumbers
© Quantifiers: All - Indexed
lowNumbers„ 1 11 3 19 41 65 19
highNumbers„ 7 19 42 22 45 79 24
Linq„Œwi 'Linq71' lowNumbers highNumbers
APL„lowNumbers^.<highNumbers
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.12.6 All - Grouped
This query returns all of the products in categories where none of the products in that category is out of stock.
The sample first uses group by to group the products into categories. It then uses Any to find those groups
that have no products out of stock. Finally, the select clause creates elements of the return sequence
consisting of the Category as the key and the associated group of products.

1.13 Aggregate Operators
There is a way of working with C# with the reassurance of the APL+Win comfort zone: this group of operators
illustrate this admirably.

1.13.1 Count - Simple
This query returns the count of distinct elements of an integer vector.
Linq73
LINQ
3
APL
3
Match? 1

Page 50 of 69

The APL+Win function that produces the matching result:
’ Z„Linq73;Linq;APL
© Aggregate Operators - Count - Simple
Linq„Œwi 'Linq73'
APL„2 2 3 5 5
APL„н½((APL¼APL)=¼½APL)/APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Note that the APL+Win solution needs to match the C# result: scalar for scalar, see line [4].

[1]
[2]
[3]
[4]
[5]

1.13.2 Count - Conditional
This sample finds the number of odd elements of an integer array. This describes the C# inner workings:
“The sample passes a lambda expression to Count that it uses to perform the test, while Count handles the
iteration and maintains the running total.”
I am finding the documentation of the solution difficult to understand, let alone the solution itself! In
contrast, the APL+Win solution could not be simpler.
Linq74
LINQ
5
APL
5
Match? 1

[1]
[2]
[3]
[4]
[5]

’ Z„Linq74;Linq;APL
© Aggregate Operators - Count - Conditional
Linq„œŒwi 'Linq74'
APL„5 4 1 3 9 8 6 7 2 0
APL„2+.|APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.3 Count - Indexed
The code sample provided at the URL does not compile. The culprit line is:
int oddEvenMatches = numbers.Count((n, index) => n % 2 == index % 2);
It causes the following error:
Delegate 'System.Func<int,bool>' does not take '2' arguments
The correct line is:
int oddEvenMatches = numbers.Where((n, index) => n % 2 == index % 2).Count();
Linq75
LINQ
4
APL
4
Match? 1
’ Z„Linq75;Linq;APL
© Aggregate Operators - Count - Indexed
Linq„œŒwi 'Linq75'
numbers„5 4 1 3 9 8 6 7 2 0
APL„(2|numbers)+.^2|(-Œio)+¼½numbers
© odd numbers at odd
positions (index origin zero)
[5]
APL„APL+(~2|numbers)+.^~2|(-Œio)+¼½numbers © even numbers at even
positions (index origin zero)
[6]
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

[1]
[2]
[3]
[4]

Page 51 of 69

1.13.4 Count - Nested
This sample uses Count to return a list of customers and how many orders each has; it includes customers
with nil orders.
[1]
[2]
[3]
[4]
[5]
[6]
[7]

’ Z„Linq76;Linq;APL
© Aggregate Operators - Count - Nested
Linq„œŒwi 'Linq76'
APL„Œwi 'GetCustomerListEx'
CustomerID„Œioœ¨APL
OrderCount„¹½¨(Œio+9)œ¨APL
APL„³œCustomerID OrderCount
Z„•(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.5 Count - Grouped
This sample prints each category and the number of products in that category. The sample first uses group by
to group the products according to their categories. Then it uses select to create an anonymous for each
category that contains the category and number of products.
Linq77
LINQ
Beverages
12
Condiments
12
Produce
5
Meat/Poultry
6
Seafood
12
Dairy Products 10
Confections
13
Grains/Cereals 7

[1]
[2]
[3]
[4]
[5]
[6]
[7]

APL
Beverages
12
Condiments
12
Produce
5
Meat/Poultry
6
Seafood
12
Dairy Products 10
Confections
13
Grains/Cereals 7

Match?
1

’ Z„Linq77;Linq;APL;a
© Aggregate Operators - Count - Grouped
Linq„œŒwi 'Linq77'
APL„Œwi 'GetProductListEx'
Category„(Œio+2)œ¨APL
a„((Category¼Category)=¼½Category)/Category
APL„³œa (+š(a¼Category)°.=¼½a)
Z„•(›¨'LINQ' 'APL' 'Match?'),[Œio-0.5]Linq APL (Linq−APL)

1.13.6 Sum - Simple
This query uses Sum to find the total of all of the numbers in an integer array. Sounds familiar?
LINQ
45
APL
45
Match? 1

Page 52 of 69

[1]
[2]
[3]
[4]
[5]

’ Z„Linq78;Linq;APL
© Aggregate Operators - Sum - Simple
Linq„Œwi 'Linq78'
APL„5 4 1 3 9 8 6 7 2 0
APL„+/APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.7 Sum - Projection
This sample uses Sum to find the total number of characters in all of the words in a string array.
Linq79
LINQ
20
APL
20
Match? 1

[1]
[2]
[3]
[4]
[5]

’ Z„Linq79;Linq;APL
© Aggregate Operators - Sum - Projection
Linq„,Œwi 'Linq79'
APL„"cherry" "apple" "blueberry"
APL„,¹+/½¨APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.8 Sum - Grouped
This query returns, for each category, the total number of units in stock for all products in that category.
Linq80
Beverages
Condiments
Produce
Meat/Poultry
Seafood
Dairy Products
Confections
Grains/Cereals

[1]
[2]
[3]
[4]
[5]
[6]

LINQ
559
507
100
165
701
393
386
308

Beverages
Condiments
Produce
Meat/Poultry
Seafood
Dairy Products
Confections
Grains/Cereals

APL Match?
559
1
507
100
165
701
393
386
308

’ Z„Linq80;Linq;APL
© Aggregate Operators - Sum - Grouped
Linq„œŒwi 'Linq80'
APL„Œwi 'GetProductListEx'
(Category UnitsInStock)„(Œio+2 4)œ¨¨›APL
DistinctCategory„((Category¼Category)=¼½Category)/Category
UnitsInStock„((DistinctCategory¼Category)°.=¼½DistinctCategory)×[Œio]Un
itsInStock © Separate UnitsInStock by Category (into columns)
APL„³œDistinctCategory (+šUnitsInStock)
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

[7]
[8]

1.13.9 Min - Simple
This sample uses Min to get the lowest number in an integer array.
Linq81
LINQ
0
APL
0
Match? 1

[1]
[2]
[3]
[4]

’ Z„Linq81;Linq;APL
© Aggregate Operators - Min - Simple
Linq„œŒwi 'Linq81'
APL„5 4 1 3 9 8 6 7 2 0
APL„˜/APL

Page 53 of 69

[5]

Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.10 Min - Projection
This sample uses Min to get the length of the shortest word in a string array.
Linq82
LINQ
5
APL
5
Match? 1

[1]
[2]
[3]
[4]
[5]

’ Z„Linq82;Linq;APL
© Aggregate Operators - Min - Projection
Linq„œŒwi 'Linq82'
APL„"cherry" "apple" "blueberry"
APL„н¹˜/½¨APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.11 Min - Grouped
This query finds, for each category, the lowest of all product prices.
Linq83
LINQ
Beverages
4.50
Condiments
10.00
Produce
10.00
Meat/Poultry
7.45
Seafood
6.00
Dairy Products 2.50
Confections
9.20
Grains/Cereals 7.00
[1]
[2]
[3]
[4]
[5]
[6]

APL Match?
Beverages
4.50
1
Condiments
10.00
Produce
10.00
Meat/Poultry
7.45
Seafood
6.00
Dairy Products 2.50
Confections
9.20
Grains/Cereals 7.00

’ Z„Linq83;Linq;APL;UnitPrice;Category;DistinctCategory
© Aggregate Operators - Min - Grouped
Linq„œŒwi 'Linq83'
APL„Œwi 'GetProductListEx'
(Category UnitPrice)„(Œio+2 3)œ¨¨›APL
DistinctCategory„((Category¼Category)=¼½Category)/Category
UnitPrice„((DistinctCategory¼Category)°.=¼½DistinctCategory)×[Œio]UnitP
rice © Separate prices by Category (into columns)
((0=,UnitPrice)/,UnitPrice)„˜/Ð
APL„³œDistinctCategory (˜šUnitPrice)
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

[7]
[8]
[9]

Pause a moment and consider this question: what is the purpose of line [7]?

1.13.12 Min - Elements
This query returns the cheapest price in each category

Page 54 of 69

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]

’ Z„Linq84;Linq;APL;Category;UnitPrice;DistinctCategory
© Aggregate Operators - Max - Grouped
Linq„œŒwi 'Linq84'
APL„Œwi 'GetProductListEx'
(Category UnitPrice)„(Œio+2 3)œ¨¨›APL
DistinctCategory„((Category¼Category)=¼½Category)/Category
UnitPrice„((DistinctCategory¼Category)°.=¼½DistinctCategory)×[Œio]UnitP
rice © Separate prices by Category (into columns)
((0=,UnitPrice)/,UnitPrice)„˜/Ð
APL„›[Œio+1](¹Ÿ/(›[Œio]UnitPrice)¹¨˜šUnitPrice)šœAPL
APL„œAPL[“DistinctCategory¼(Œio+2)œ¨APL] © Reorder in original category
order
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

[10]

The APL+Win solution has an interesting twist: refer to line [7].

1.13.13 Max - Simple
This sample uses Max to get the highest number in an integer array.
Linq85
LINQ
9
APL
9
Match? 1

[1]
[2]
[3]
[4]
[5]

’ Z„Linq85;Linq;APL
© Aggregate Operators - Max - Simple
Linq„œŒwi 'Linq85'
APL„5 4 1 3 9 8 6 7 2 0
APL„—/APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.14 Max - Projection
This sample uses Max to get the length of the longest word in a string array.
Linq86
LINQ
9
APL
9
Match? 1

[1]
[2]
[3]
[4]
[5]

’ Z„Linq86;Linq;APL
© Aggregate Operators - Max - Projection
Linq„Œwi 'Linq86'
APL„"cherry" "apple" "blueberry"
APL„н¹—/½¨APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.15 Max - Grouped
This query returns the most expensive price in each category.
Linq87
LINQ
Beverages
263.50
Condiments
43.90
Produce
53.00
Meat/Poultry
123.79
Seafood
62.50
Dairy Products 55.00
Confections
81.00
Grains/Cereals 38.00

APL Match?
Beverages
263.50
1
Condiments
43.90
Produce
53.00
Meat/Poultry
123.79
Seafood
62.50
Dairy Products 55.00
Confections
81.00
Grains/Cereals 38.00

Page 55 of 69

[1]
[2]
[3]
[4]
[5]
[6]

’ Z„Linq87;Linq;APL;Category;UnitPrice;DistinctCategory
© Aggregate Operators - Max - Grouped
Linq„œŒwi 'Linq87'
APL„Œwi 'GetProductListEx'
(Category UnitPrice)„(Œio+2 3)œ¨¨›APL
DistinctCategory„((Category¼Category)=¼½Category)/Category
UnitPrice„((DistinctCategory¼Category)°.=¼½DistinctCategory)×[Œio]UnitP
rice © Separate prices by Category (into columns)
APL„³œDistinctCategory (—šUnitPrice)
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

[7]
[8]

1.13.16 Max - Elements
This sample finds the most expensive products in each category.

[1]
[2]
[3]
[4]
[5]
[6]

’ Z„Linq88;Linq;APL;Category;UnitPrice;DistinctCategory
© Aggregate Operators - Max - Elements
Linq„œŒwi 'Linq88'
APL„Œwi 'GetProductListEx'
(Category UnitPrice)„(Œio+2 3)œ¨¨›APL
DistinctCategory„((Category¼Category)=¼½Category)/Category
UnitPrice„((DistinctCategory¼Category)°.=¼½DistinctCategory)×[Œio]UnitP
rice © Separate prices by Category (into columns)
APL„›[Œio+1](¹Ÿ/(›[Œio]UnitPrice)¹¨—šUnitPrice)šœAPL
APL„œAPL[“DistinctCategory¼(Œio+2)œ¨APL] © Reorder in original category
order
Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

[7]
[8]
[9]

1.13.17 Average - Simple
This sample uses Average to get the average of all values of an integer array.
Linq89
LINQ
4.5
APL
4.5
Match? 1.0
’ Z„Linq89;Linq;APL
© Aggregate Operators - Average - Simple
Linq„œŒwi 'Linq89'
APL„5 4 1 3 9 8 6 7 2 0
APL„н(+/APL)÷½APL © Result is scalar: would not match if a single
element
vector
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

[1]
[2]
[3]
[4]
[5]

1.13.18 Average - Projection
This sample uses Average to get the average length of the words in the string array.

Page 56 of 69

Linq90
LINQ
6.666666667
APL
6.666666667
Match? 1.000000000

[1]
[2]
[3]
[4]
[5]

’ Z„Linq90;Linq;APL
© Aggregate Operators - Average - Projection
Linq„Œwi 'Linq90'
APL„ "cherry" "apple" "blueberry"
APL„н(¹+/½¨APL)÷½APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.19 Average - Grouped
1.13.20 Fold - Simple
This sample finds the product of all elements of an array of doubles. The samples uses Fold to create a
running product on the array, passing each element in turn to the lambda expression that performs the
multiplication.
Linq92
LINQ
88.33081
APL
88.33081
Match? 1.00000

[1]
[2]
[3]
[4]

’ Z„Linq92;Linq;APL
© Aggregate Operators: Fold - Simple
Linq„Œwi 'Linq92'
APL„×/1.7 2.3 1.9 4.1 2.9
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.21 Fold - Seed
This sample subtracts a sequence of integers from a starting value, simulating withdrawals from an account.
While there is still cash left in the account, the withdrawal succeeds. The sample uses Fold to pass each
withdrawal value in turn to the lambda expression that performs the subtraction.
Linq93
startBalance
attemptedWithdrawals
LINQ
APL
Match?

[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]

37
29
17
17

140

60

4

Z„Linq93;Linq;APL;startBalance;attemptedWithdrawals;i;R
© Aggregate Operators - Fold - Seed
startBalance„?1000
attemptedWithdrawals„¹(2?5)?¨150 © With possible replication
Linq„œŒwi 'Linq93' startBalance attemptedWithdrawals
i„startBalance‰+\attemptedWithdrawals
APL„startBalance-+/i/attemptedWithdrawals
attemptedWithdrawals„(~i)/attemptedWithdrawals
:for R :in attemptedWithdrawals
:if APL>R
APL„APL-R
:endif
:endfor
Z„•œ(›¨'startBalance' 'attemptedWithdrawals' 'LINQ' 'APL'
'Match?'),¨startBalance attemptedWithdrawals Linq APL (Linq−APL)


The APL+Win function generates the arguments it uses and passes to C# randomly. This function is
coded such that it allows for easy step-wise debugging.

Page 57 of 69

1.14 Miscellaneous Operators
1.14.1 Concat - 1
This query merges two integer vectors into a single sequence. The sample uses ‘Concat’ to create the
sequence with each array's values, one after the other.
This is quite simply ‘Catenate’ in APL+Win without the sophistication of optional axes specifications.
Linq94
LINQ 0 2 4 5 6 8 9 1 3 5 7 8

APL 0 2 4 5 6 8 9 1 3 5 7 8

Match? 1

The APL+Win function:
[1]
[2]
[3]
[4]

’ Z„Linq94;Linq;APL
© Miscellaneous Operators - Concat - 1
Linq„Œwi 'Linq94'
APL„0 2 4 5 6 8 9,1 3 5 7 8
Z„•(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.14.2 Concat - 2
This query returns all customer names followed by all product names. The LINQ solution is elaborate because
the language operates at scalar level and needs to iterate to cope with arrays.

The APL+Win function:
’ Z„Linq95;Linq;APL
© Miscellaneous Operators - Concat - 2
Linq„œŒwi 'Linq95'
© (ProductID ProductName Category UnitPrice UnitsInStock) (© CustomerID
CompanyName Address City Region PostalCode Country Phone Fax Orders[])
[4]
APL„(œ(Œio+1)œ¨Œwi 'GetCustomerListEx') (œ(Œio+1)œ¨Œwi
'GetProductListEx')
[5]
APL„œ,[Œio]/((›¹0,—/¯1†¨½¨APL)—½¨APL)†¨APL
[6]
Z„•(›¨'LINQ' 'APL' 'Match?'),[Œio-0.5]Linq APL (Linq−APL)

This operation is simply catenation on the x axis.
[1]
[2]
[3]

1.14.3 EqualAll - 1
This query determines if two string vectors have the same elements in the same order. It uses ‘EqualAll’ to
compare the two vectors, element by element. For string vectors in C#, the APL+Win equivalent is a nested
vector. The corresponding structure for nested vector in C# is a jagged vector. However, the argument to this
query is not a jagged (or nested) vector but simply a single dimensional array.
LINQ
APL

Linq96
1
1

Page 58 of 69

Match? 1
The APL+Win function:
[1]
[2]
[3]
[4]
[5]

’ Z„Linq96;Linq;APL
© Miscellaneous Operators - EqualAll - 1
Linq„œŒwi 'Linq96'
APL„"cherry" "apple" "blueberry"
APL„^/¹APL−¨APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.14.4 EqualAll - 2
This repeats of the previous query using arguments that will create the opposite result, that is, false. APL+Win
returns a matching result.
Linq97
LINQ
0
APL
0
Match? 1
The function is:
[1]
[2]
[3]
[4]
[5]

’ Z„Linq97;Linq;APL
© Miscellaneous Operators - EqualAll - 2
Linq„œŒwi 'Linq97'
APL„"cherry" "apple" "blueberry"
APL„^/¹APL−¨"apple" "blueberry" "cherry"
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

The function Linq96Ex using the arguments used in this function will return a result that is true.

1.14.4.1 EqualAll - 2 : Extension
This query is looking to match not just the elements but also the order in which they occur. A useful extension
is to be able to determine if two vectors match simply by content irrespective of the order of the individual
elements. The APL+Win solution is:
[1]
[2]
[3]
[4]

’ Z„Linq97Ex;APL;APL2
© Miscellaneous Operators - Element All
APL„"cherry" "apple" "blueberry"
APL2„"apple" "cherry" "blueberry"
Z„APL^.¹APL2

Linq97Ex

1

1.15 Custom Sequence Operators
Some C# LINQ operators, like this one, are as old as APL itself. The C# solution is18:

18

Copied from the Visual Studio 2008 project, hence the line numbers.

Page 59 of 69

This appears deceptively simple; however, ‘Combine’ is defined as follows19:

It is clear that C# not only uses a different jargon to describe standard, even routine, APL+Win
functionality but also offers comparative solutions that are comparatively much more complicated. Does this
shed a new light on the arguments relating to APL+Win readability?

1.15.1 Combine
This query returns the dot product of two integer vectors. It uses a user-created sequence operator, Combine,
to calculate the dot product, passing it a lambda function to multiply two vectors, element by element, and
finally returns the sum as the result.
Linq98
LINQ
109
APL
109
Match?
1
This functionality in APL+Win is basic, going back to first generation APL; the function is defined as
follows:
[1]
[2]
[3]
[4]
[5]

’ Z„Linq98;Linq;APL
© Custom Sequence Operators - Combine
Linq„Œwi 'Linq98'
APL„0 2 4 5 6+.×1 3 5 7 8
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)
© Error in public static class CustomSequenceOperators

1.15.1.1 C#:APL+Win comparison
Consider another example, returning iota 10 in index origin 1 and 0.
The C# solution using LINQ:
public int[] Linq98Ex_IOTA(int io, int arg)
{
return Enumerable.Range(io, arg).ToArray();
}

19

This is defined incorrectly at the URL.

Page 60 of 69

The APL+Win solution:
[1]
[2]
[3]
[4]
[5]

’ Z„Linq98Ex_IOTA;Linq;APL;Match
© Ajay Askoolum
Linq„(Œwi 'Linq98Ex_IOTA' 1 10)(Œwi 'Linq98Ex_IOTA' 0 10)
APL„(+\10/1) ((-Œio)+¼10)
Match„Linq−APL
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Neither solution incorporates any argument validation or error trapping in order to demonstrate the bare
minimum code to deliver a simple result. The result:
Linq98Ex_IOTA
LINQ 1 2 3 4 5 6 7 8 9 10
APL
1 2 3 4 5 6 7 8 9 10
Match?
1

0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9

What does this say about APL+Win versus C# code maintenance and/or the rapid application
development credentials of the two languages?

1.16 Query Execution
This group of queries attempt to illustrate how a query is defined for deferred, immediate, and re-use
execution.

1.16.1 Deferred
A query is coded such that its execution is deferred until data is requested from it. I am not sure that I
understand the subtleties; however, it is possible to emulate the same behaviour.
[1]
[2]
[3]
[4]
[5]

’ Z„Linq99;Linq;APL
© Query Execution - Deferred
Linq„Œwi 'Linq99'
APL„5 4 1 3 9 8 6 7 2 0
APL„–¨('+\(½APL)/1') ('(~Œio)+¼½APL')
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Reading from right-to-left, line [4] defines a query whose execution is deferred until it is executed.
Linq99
LINQ 1 2 3 4 5 6 7 8 9 10
APL
1 2 3 4 5 6 7 8 9 10
Match?
1

1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10

1.16.2 Immediate
The query executes immediately.
Linq100
LINQ 1 2 3 4 5 6 7 8 9 10
APL
1 2 3 4 5 6 7 8 9 10
Match?
1

10 10 10 10 10 10 10 10 10 10
10 10 10 10 10 10 10 10 10 10

The APL+Win function:
[1]
[2]
[3]
[4]
[5]

’ Z„Linq100;Linq;APL
© Query Execution - Immediate
Linq„Œwi 'Linq100'
APL„5 4 1 3 9 8 6 7 2 0
APL„(+\(½APL)/1) ((½APL)/½APL)
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Page 61 of 69

1.16.3 Query Reuse
The query is defined such that it can be re-used subsequently—within the same scope—with different
arguments.
[1]
[2]
[3]
[4]
[5]
[6]

’ Z„Linq101;Linq;APL;Reuse
© Query Execution - Query Reuse
Linq„Œwi 'Linq101'
0 0½Œdef '’ R„Reuse R',Œtcnl,'[1] R„(Rˆ3)/R’'
APL„5 4 1 3 9 8 6 7 2 0
APL„(Reuse APL) (Reuse -APL)
Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

The key concept is that the query is accessible for re-use within the same scope. The APL+Win solution
emulates this by defining the query as a local function; the results match.
Linq101
LINQ 1 3 2 0
APL
1 3 2 0
Match?
1

¯5 ¯4 ¯1 ¯3 ¯9 ¯8 ¯6 ¯7 ¯2 0
¯5 ¯4 ¯1 ¯3 ¯9 ¯8 ¯6 ¯7 ¯2 0

1.17 Time for 101 APL+Win samples?
The intrinsic value of the 101 samples is not that they provide re-usable code or that they teach the C #
language; the value lies in the fact that they provide worked examples. As such, the samples provide a basis
on which to promote familiarity with key concepts and to enhance developer confidence. Sadly, (and wrongly,
in my opinion) APL has shirked this strategy for gaining wider acceptance.
One hundred and one LINQ samples delivered side by side in C# (the contemporary flagship language)
and APL+Win: I hope this bears witness to the credentials of APL+Win as a contemporary (where the Dot Net
Framework dominates) development tool.

1.17.1 Some eligible topics
1.17.1.1 Example 1 - Return the sign of all elements of a numeric vector/array
Negative
Zero
Positive
Null or Empty

-1
0
1
Null

1.17.1.2 Example 2 - Return the magnitude indicator of all elements of a numeric vector/array
, given a range
-2
-1
0
1
2

Is greater than Maximum
Is equal to Maximum
Within minimum and maximum
Is equal to Minimum
Is less than Minimum

1.17.1.3 Example 3 – Apply the percentage of increase
Salary
Band

12,345
0

67,654
8

9,876
0

67,542
9

78,345
3

32,456
1

Next consider the percentage increase applicable to salary bands, as follows:
Increase (%)

Page 62 of 69

4.5

3.45

5.12

1.78

Band

3

1

0

9

Note the complication: the percentage increase for band 8 is not specified/is missing. What is the amount
of increase in each Salary?

1.17.1.4 Example 4 – Re-calculate the diagonal elements of a numeric array
1.17.1.5 Example 5 – Return the powers of 2 of each element of a numeric vector/array

Ajay Askoolum
April 2009

References
2.
3.
4.

101 LINQ Samples: http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx
System Building with APL+Win, Ajay Askoolum, WileyBlackwell (7 Jul 2006), ISBN-10: 047003020

Page 63 of 69

101 LINQ to Objects Samples: From C# to APL+Win
Ajay Askoolum
Appendix A – Product & Customer Lists
The sample queries use the product and customer lists extensively; the way C# accesses these lists is shown
in Appendix B, the code listing. For APL+Win, C# returns the same lists as nested arrays.
The Product List
© ProductID ProductName Category UnitPrice UnitsInStock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

œŒwi 'GetProductListEx'
Chai
Chang
Aniseed Syrup
Chef Anton's Cajun Seasoning
Chef Anton's Gumbo Mix
Grandma's Boysenberry Spread
Uncle Bob's Organic Dried Pears
Northwoods Cranberry Sauce
Mishi Kobe Niku
Ikura
Queso Cabrales
Queso Manchego La Pastora
Konbu
Tofu
Genen Shouyu
Pavlova
Alice Mutton
Carnarvon Tigers
Teatime Chocolate Biscuits
Sir Rodney's Marmalade
Sir Rodney's Scones
Gustaf's Knãckebr÷d
Tunnbr÷d
Guaranß Fantßstica
NuNuCa Nuÿ-Nougat-Creme
Gumbãr Gummibãrchen
Schoggi Schokolade
R÷ssle Sauerkraut
Thžringer Rostbratwurst
Nord-Ost Matjeshering
Gorgonzola Telino
Mascarpone Fabioli
Geitost
Sasquatch Ale
Steeleye Stout
Inlagd Sill
Gravad lax
C•te de Blaye
Chartreuse verte
Boston Crab Meat
Jack's New England Clam Chowder
Singaporean Hokkien Fried Mee
Ipoh Coffee
Gula Malacca
Rogede sild
Spegesild
Zaanse koeken
Chocolade
Maxilaku
Valkoinen suklaa
Manjimup Dried Apples

© Ajay Askoolum

Beverages
18.00 39
Beverages
19.00 17
Condiments
10.00 13
Condiments
22.00 53
Condiments
21.35
0
Condiments
25.00 120
Produce
30.00 15
Condiments
40.00
6
Meat/Poultry
97.00 29
Seafood
31.00 31
Dairy Products 21.00 22
Dairy Products 38.00 86
Seafood
6.00 24
Produce
23.25 35
Condiments
15.50 39
Confections
17.45 29
Meat/Poultry
39.00
0
Seafood
62.50 42
Confections
9.20 25
Confections
81.00 40
Confections
10.00
3
Grains/Cereals 21.00 104
Grains/Cereals
9.00 61
Beverages
4.50 20
Confections
14.00 76
Confections
31.23 15
Confections
43.90 49
Produce
45.60 26
Meat/Poultry
123.79
0
Seafood
25.89 10
Dairy Products 12.50
0
Dairy Products 32.00
9
Dairy Products
2.50 112
Beverages
14.00 111
Beverages
18.00 20
Seafood
19.00 112
Seafood
26.00 11
Beverages
263.50 17
Beverages
18.00 69
Seafood
18.40 123
Seafood
9.65 85
Grains/Cereals 14.00 26
Beverages
46.00 17
Condiments
19.45 27
Seafood
9.50
5
Seafood
12.00 95
Confections
9.50 36
Confections
12.75 15
Confections
20.00 10
Confections
16.25 65
Produce
53.00 20

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

Filo Mix
Grains/Cereals
Perth Pasties
Meat/Poultry
Tourti²re
Meat/Poultry
P¼t´ chinois
Meat/Poultry
Gnocchi di nonna Alice
Grains/Cereals
Ravioli Angelo
Grains/Cereals
Escargots de Bourgogne
Seafood
Raclette Courdavault
Dairy Products
Camembert Pierrot
Dairy Products
Sirop d'´rable
Condiments
Tarte au sucre
Confections
Vegie-spread
Condiments
Wimmers gute Semmelkn÷del
Grains/Cereals
Louisiana Fiery Hot Pepper Sauce Condiments
Louisiana Hot Spiced Okra
Condiments
Laughing Lumberjack Lager
Beverages
Scottish Longbreads
Confections
Gudbrandsdalsost
Dairy Products
Outback Lager
Beverages
Flotemysost
Dairy Products
Mozzarella di Giovanni
Dairy Products
R÷d Kaviar
Seafood
Longlife Tofu
Produce
Rh÷nbrãu Klosterbier
Beverages
Lakkalik÷÷ri
Beverages
Original Frankfurter gržne Soÿe Condiments

7.00
32.80
7.45
24.00
38.00
19.50
13.25
55.00
34.00
28.50
49.30
43.90
33.25
21.05
17.00
14.00
12.50
36.00
15.00
21.50
34.80
15.00
10.00
7.75
18.00
13.00

38
0
21
115
21
36
62
79
19
113
17
24
22
76
4
52
6
26
15
26
14
101
4
125
57
32

The Customer List
The customer list is not tabular—ORDERS, its last element contains the orders for each customer and is
nested. I recommend that you use XML NOTEPAD to examine its structure and content; CUSTOMERS.XML,
an XML file, holds the structure.

Page 66 of 69

101 LINQ to Objects Samples: From C# to APL+Win
Ajay Askoolum
Index
Aggregate Operators
Linq73............................................................. 51
Linq74............................................................. 51
Linq75............................................................. 51
Linq77............................................................. 52
Linq78............................................................. 53
Linq79............................................................. 53
Linq80............................................................. 53
Linq81............................................................. 53
Linq82............................................................. 54
Linq83............................................................. 54
Linq84............................................................. 55
Linq85............................................................. 55
Linq86............................................................. 55
Linq87............................................................. 56
Linq88............................................................. 56
Linq89............................................................. 56
Linq90............................................................. 57
Linq92............................................................. 57
Linq93............................................................. 57
APL Functions
LCase ............................................................. 21
LINQ ................................................................. 9
Linq1 ............................................................... 12
Linq10............................................................. 22
Linq100........................................................... 61
Linq101........................................................... 62
Linq11............................................................. 22
Linq12............................................................. 23
Linq13............................................................. 23
Linq14............................................................. 23
Linq15............................................................. 24
Linq16............................................................. 25
Linq17............................................................. 26
Linq18............................................................. 26
Linq19............................................................. 28
Linq2 ............................................................... 16
Linq20............................................................. 28
Linq21............................................................. 28
Linq22............................................................. 29
Linq23............................................................. 29
Linq24............................................................. 30
Linq25............................................................. 31
Linq26............................................................. 31
Linq27............................................................. 31
Linq28............................................................. 31
Linq29............................................................. 32
Linq3 ............................................................... 17
Linq30............................................................. 32
Linq31............................................................. 33
Linq32............................................................. 33
Linq33............................................................. 34
Linq34............................................................. 34
Linq35............................................................. 34
Linq36............................................................. 35
Linq37............................................................. 35
Linq38............................................................. 36

© Ajay Askoolum

Linq39 .............................................................36
Linq4 ...............................................................18
Linq40 .............................................................37
Linq41 .............................................................37
Linq42 .............................................................37
Linq44 .............................................................38
Linq45 .............................................................39
Linq46 .............................................................39
Linq47 .............................................................40
Linq48 .............................................................40
Linq49 .............................................................40
Linq5 ...............................................................18
Linq50 .............................................................41
Linq51 .............................................................41
Linq52 .............................................................42
Linq53 .............................................................42
Linq54 .............................................................43
Linq55 .............................................................43
Linq56 .............................................................44
Linq57 .............................................................44
Linq58 .............................................................45
Linq59 .............................................................45
Linq6 ...............................................................19
Linq60 .............................................................45
Linq61 .............................................................45
Linq62 .............................................................46
Linq63 .............................................................47
Linq63Ex .........................................................47
Linq64 .............................................................48
Linq65 .............................................................48
Linq66 .............................................................48
Linq67 .............................................................49
Linq68 .............................................................49
Linq7 ...............................................................20
Linq70 .............................................................50
Linq71 .............................................................50
Linq73 .............................................................51
Linq74 .............................................................51
Linq75 .............................................................51
Linq76 .............................................................52
Linq77 .............................................................52
Linq78 .............................................................53
Linq79 .............................................................53
Linq8 ...............................................................21
Linq80 .............................................................53
Linq81 .............................................................53
Linq82 .............................................................54
Linq83 .............................................................54
Linq84 .............................................................55
Linq85 .............................................................55
Linq86 .............................................................55
Linq87 .............................................................56
Linq88 .............................................................56
Linq89 .............................................................56
Linq9 ...............................................................21
Linq90 .............................................................57
Linq92 .............................................................57

Linq93............................................................. 57
Linq94............................................................. 58
Linq95............................................................. 58
Linq96............................................................. 59
Linq97............................................................. 59
Linq97Ex......................................................... 59
Linq98............................................................. 60
Linq98Ex_IOTA .............................................. 61
Linq99............................................................. 61
UCase............................................................. 21
Comparable Nested Array................................ 38
Conversion Operators
Linq54............................................................. 43
Linq55............................................................. 43
Linq56............................................................. 44
Linq57............................................................. 44
Custom Sequence Operators
Linq94............................................................. 58
Linq95............................................................. 58
Linq96............................................................. 59
Linq97............................................................. 59
Linq98............................................................. 60
CUSTOMERS.XML ........................................ 7, 66
Element Operators
Linq58............................................................. 45
Linq59............................................................. 45
Linq60............................................................. 45
Linq61............................................................. 45
Linq62............................................................. 46
Linq63............................................................. 47
Linq64............................................................. 48
Generation Operators
Linq65............................................................. 48
Linq66............................................................. 48
Operators
Aggregate ....................................................... 50
Conversion ..................................................... 43
CustomSequence ........................................... 59
Element .......................................................... 44
Generation...................................................... 48
Grouping......................................................... 36
Miscellaneous................................................. 58
Partitioning...................................................... 28
Projection........................................................ 19
Quantifiers ...................................................... 49
Restriction....................................................... 11
Set .................................................................. 39
Ordering Operators
Linq28............................................................. 31
Linq29............................................................. 32
Linq30............................................................. 32
Linq31............................................................. 33
Linq32............................................................. 33
Linq33............................................................. 34
Linq34............................................................. 34
Linq35............................................................. 34
Linq36............................................................. 35
Linq37............................................................. 35

Page 68 of 69

Linq38 .............................................................36
Linq39 .............................................................36
Linq40 .............................................................37
Linq41 .............................................................37
Linq42 .............................................................37
Linq44 .............................................................38
Linq45 .............................................................39
Partitioning Operators
Linq20 .............................................................28
Linq21 .............................................................28
Linq22 .............................................................29
Linq23 .............................................................29
Linq24 .............................................................30
Linq25 .............................................................31
Linq26 .............................................................31
Linq27 .............................................................31
Projection Operators
Linq10 .............................................................22
Linq11 .............................................................22
Linq12 .............................................................23
Linq13 .............................................................23
Linq14 .............................................................23
Linq15 .............................................................24
Linq16 .............................................................25
Linq17 .............................................................26
Linq18 .............................................................26
Linq19 .............................................................28
Linq6 ...............................................................19
Linq7 ...............................................................20
Linq8 ...............................................................21
Linq9 ...............................................................21
Quantifiers
Linq67 .............................................................49
Linq68 .............................................................49
Linq70 .............................................................50
Linq71 .............................................................50
Linq76 .............................................................52
Query Execution
Linq100 ...........................................................61
Linq101 ...........................................................62
Linq99 .............................................................61
Restriction Operators
Linq1 ...............................................................12
Linq2 ...............................................................16
Linq3 ...............................................................17
Linq4 ...............................................................18
Linq5 ...............................................................18
Set Operators
Linq46 .............................................................39
Linq47 .............................................................40
Linq48 .............................................................40
Linq49 .............................................................40
Linq50 .............................................................41
Linq51 .............................................................41
Linq52 .............................................................42
Linq53 .............................................................42
Signature ............................................................13
XML NOTEPAD ..............................................7, 66

Code Listings
The remaining pages contains listing of EXPLORE.CS and DATA.CS from the Visual Studio 2008 project that
builds the COM DLL, LINQ.DLL, used throughout this article.

Page 69 of 69