You are on page 1of 95

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

Ajay Askoolum
1

Language Integrated Query (LINQ) is an extension to C#, available from version 3.0 , 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 Objectsthe other implementations
are LINQ to SQL, LINQ to Entities, LINQ to DataSet, and LINQ to XMLthat deals with in-memory data or,
more generally, collections.
APL+Win, A Programming Language (APL) has been about language integrated data manipulation
2
arrays, vectors, and nested or jagged arraysthroughout its history . 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............................................................................................9
1.1.4 Why LINQ to Objects?................................................................................................................9

1.2 Getting Started ....................................................................................................................9


1.2.1 Understanding the code ...........................................................................................................10
1.2.1.1 Language symbols............................................................................................................................ 11
1.2.1.2 What is the question? ....................................................................................................................... 11

1.3 Restriction Operators ........................................................................................................12


1.3.1 Where - Simple 1 ......................................................................................................................12
1.3.1.1 C# code ............................................................................................................................................ 12
1.3.1.2 APL+Win code................................................................................................................................. 13
1.3.1.3 Understanding the standard result .................................................................................................... 13
1.3.1.4 Method Signature ............................................................................................................................. 14
1.3.1.5 Further tests...................................................................................................................................... 14
1.3.2 Where - Simple 2 ......................................................................................................................15
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 .......................................................................................................................19

1.4 Projection Operators .........................................................................................................19


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

1.5 Partitioning Operators .......................................................................................................28


1.5.1 Take - Simple ...........................................................................................................................29
1.5.2 Take - Nested ...........................................................................................................................29
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.................................................................. 30
1.5.3 Skip - Simple.............................................................................................................................30
1.5.4 Skip - Nested ............................................................................................................................30
1.5.5 TakeWhile - Simple ..................................................................................................................31
1.5.6 TakeWhile - Indexed.................................................................................................................31
1.5.7 SkipWhile - Simple ...................................................................................................................31
1.5.8 SkipWhile - Indexed..................................................................................................................32

1.6 Ordering Operators ...........................................................................................................32


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

1.7 Grouping Operators...........................................................................................................37


1.7.1 GroupBy - Simple 1 ..................................................................................................................37
1.7.2 GroupBy - Simple 2 ..................................................................................................................37
1.7.3 GroupBy - Simple 3 ..................................................................................................................38
1.7.4 GroupBy - Nested.....................................................................................................................38
1.7.5 GroupBy Comparer ...............................................................................................................40
1.7.6 GroupBy Comparer, Mapped ................................................................................................40

1.8 Set Operators ....................................................................................................................41


1.8.1 Distinct - 1.................................................................................................................................41
1.8.2 Distinct - 2.................................................................................................................................41
1.8.3 Union - 1 ...................................................................................................................................42
1.8.4 Union - 2 ...................................................................................................................................42
1.8.5 Intersect - 1...............................................................................................................................43
1.8.6 Intersect - 2...............................................................................................................................43
1.8.7 Except - 1..................................................................................................................................44
1.8.8 Except - 2..................................................................................................................................44
Page 2 of 71

1.9 Conversion Operators .......................................................................................................44


1.9.1 To Array ....................................................................................................................................44
1.9.2 To List .......................................................................................................................................45
1.9.3 To Dictionary.............................................................................................................................45
1.9.4 OfType ......................................................................................................................................46

1.10 Element Operators ..........................................................................................................46


1.10.1 First - Simple ..........................................................................................................................46
1.10.2 First Condition (missing at URL) .........................................................................................47
1.10.3 First - Indexed.........................................................................................................................47
1.10.4 FirstOrDefault - Simple ...........................................................................................................47
1.10.5 FirstOrDefault - Condition.......................................................................................................48
1.10.6 FirstOrDefault - Indexed .........................................................................................................48
1.10.7 ElementAt ...............................................................................................................................49

1.11 Generation Operators......................................................................................................50


1.11.1 Range .....................................................................................................................................50
1.11.2 Repeat ....................................................................................................................................50

1.12 Quantifiers .......................................................................................................................50


1.12.1 Any - Simple ...........................................................................................................................50
1.12.2 Any - Indexed .........................................................................................................................51
1.12.3 Any - Grouped ........................................................................................................................51
1.12.4 All - Simple..............................................................................................................................52
1.12.5 All - Indexed............................................................................................................................52
1.12.6 All - Grouped ..........................................................................................................................52

1.13 Aggregate Operators.......................................................................................................53


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

1.14 Miscellaneous Operators ................................................................................................61


1.14.1 Concat - 1 ...............................................................................................................................61
1.14.2 Concat - 2 ...............................................................................................................................61
1.14.3 EqualAll - 1 .............................................................................................................................62
1.14.4 EqualAll - 2 .............................................................................................................................62
1.14.4.1 EqualAll - 2 : Extension ................................................................................................................. 63

1.15 Custom Sequence Operators..........................................................................................63


1.15.1 Combine .................................................................................................................................63
1.15.1.1 C#:APL+Win comparison.............................................................................................................. 64

1.16 Query Execution ..............................................................................................................64


1.16.1 Deferred..................................................................................................................................64
1.16.2 Immediate ...............................................................................................................................65
1.16.3 Query Reuse ..........................................................................................................................65
Page 3 of 71

1.17 Time for 101 APL+Win samples? ...................................................................................65


References ....................................................................................................................................66
Appendix A Product & Customer Lists ..................................................................................67
Index ..............................................................................................................................................69
Code Listings................................................................................................................................71

Page 4 of 71

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 incorrect3. 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 themsuch as Linq60do not work. This is not only misleading but also highly
unsatisfactory, not least because of the pedigree of its originMicrosoftand 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+Wins 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 configurationan 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 71

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.
If you do not have Visual Studio 20086, you will need to do the following:

Download and install Dot Net Framework 3.5

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

The critical steps:


1.

Copy the file LINQ.DLL to a desired location on the target computer, say, C:\APLWIN\C#COM\LINQ\

2.

Locate the file RegAsm.exe. Although we have been using Framework 3.5, the most recent version of the
registration tool came with Framework 2.0.
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe

3.

Run the following line from the command prompt:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe /codebase "C:\APLWIN\C#COM\LINQ\LINQ.DLL"


If you use other automated installation software such as InstallShield to deploy your application, it needs
to execute the above line.
4.

If you need to remove the DLL from the computer, run the following line from the command prompt:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe /U "C:\APLWIN\C#COM\LINQ\LINQ.DLL"
The paths may be different on your computer; change the commands as appropriate. Refer to
http://msdn.microsoft.com/enus/library/tzat5yw6(VS.71).aspx for more details on REGASM.EXE.
On successful registration, you should be able to recreate the following:

This presents the same scenario as that when deploying your own Dot Net DLL.

Page 7 of 71

Depending on how you use the supplied code, you may receive one of the following (or similar) errors:
WI ERROR: mscorlib exception 80070015 The device is not ready.
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.

The project needs re-compilation after any changes to the code.

Page 8 of 71

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:

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 functionalitya
Dot Net expert can do it independently of APLand second, each environment can be debugged
separately or together. Play the media file APLWINDEBUGSC#.WMV for an illustration.

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?

5.

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. Please refer to the end of
this document for a complete listing9 of the C# codeEXPLORE.CS and DATA.CSthat underlies the COM
DLL.

8
9

Remember that LINQ fits within a Dot NET language, such as C# or Visual Basic.NET.
I printed the code from Visual Studio 2008 and appended to this PDF.

Page 9 of 71

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:
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.
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
10
scope of that name to that function only . 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 quotesin pairsinterchangeably, 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.

10

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

Page 10 of 71

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

<

>

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

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+Winit also has a null value.

Page 11 of 71

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
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

Page 12 of 71

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]

ZLinq1;L;R;Linq;APL
RESTRICTION OPERATOR: Where - Simple1
L(2?10)?15 With possible replication
R?10
Linqwi 'Linq1' L R
APL(L<R)/L
Z('L' 'R' 'LINQ' 'APL' 'Match?'),L R Linq APL (LinqAPL)

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 sample shows the results as follows:

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 13 of 71

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 methodhow it is codedbefore attempting a
call.
The syntax of a method may be established by a visual examination of the coderefer to Appendix A
either for code listingsor as follows:
wi '?Linq1'
XLinq1 method:
Result@Array_Long WI 'XLinq1' numbers@Array_Long threshold@Long
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 argumentssee lines [2]-[3]and permits tests to be run
quickly. The following illustrates the results of three further tests.

12

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

Page 14 of 71

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.
13

This concludes the basic guide to reading each of the remaining examples/samples .

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.

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.

13
14

This is LINQ! an advancement to C#. .APL+Win has done this from inception, has it not?
This avoids transcription errors and facilitates comparisons.

Page 15 of 71

Finally, let us see the results of the LINQ Query in APL+Win:


ZLinq2;L;R;Linq;APL;products
RESTRICTION OPERATOR: Where - Simple 2
Linqwi 'Linq2'
productswi 'GetProductListEx' ProductID ProductName Category
UnitPrice UnitsInStock
[4]
L1products
Last element is UnitsInStock
[5]
APL(io+1)(L=0)/products
[6]
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

[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 71

The results match; they are produced by the following function:


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

ZLinq3;L;R;Linq;APL;products
RESTRICTION OPERATOR: Where - Simple 3
Linqwi 'Linq3'
productswi 'GetProductListEx' ProductID ProductName Category
UnitPrice UnitsInStock
L(io+3)products
Fourth element is UnitPrice
R1products
Last (fifth) element is UnitsInStock
APL(io+1)((R>0)^L>3)/products
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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 lengththey 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.

Page 17 of 71

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

Match?

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
10861
10904
11032
11066

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
30/01/1998
24/02/1998
17/04/1998
01/05/1998
1

The function is defined as follows:


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

ZLinq4;Linq;APL
RESTRICTION OPERATOR: Where - DrillDown
Linqwi 'Linq4'
APLwi '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
APL(io+0 1 9)APL
<CustomerID>, <CustomerName> &
<Orders> i.e. First, Second & Tenth element
((io+2)APL)1 1 0/(io+2)APL <OrderId> & <OrderDate> i.e First
& Second element of <Orders>, now the third element
APLAPL
Z('LINQ' 'APL' 'Match?'),[io+0.5] Linq APL (LinqAPL)

Page 18 of 71

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.
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
15
using the UK regional format, that is, dd/mm/yyyy . 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:


ZLinq5;Linq;APL
RESTRICTION OPERATOR: Where - Indexed
Linqwi 'Linq5'
APL"zero" "one" "two" "three" "four" "five" "six" "seven" "eight"
"nine"
[4]
APL((APL)<(-io)+APL)/APL
[5]
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

[1]
[2]
[3]

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();
}

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 19 of 71

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]

ZLinq6;Linq;APL
Projection Operators - Select Simple 1
Linqwi 'Linq6'
APL1+ 5 4 1 3 9 8 6 7 2 0
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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.
16

Of course, the APL solution takes such a restriction in its stride :


1+(0=2|APL)/APL5 4 1 3 9 8 6 7 2 0
5 9 7 3 1

1.4.2 Select - Simple 2

16

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

Page 20 of 71

This query returns the name of every product in the product list. The matching APL+Win result comes from
the following function:
ZLinq7;Linq;APL
RESTRICTION OPERATOR: Where - Indexed
Linqwi '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 (LinqAPL)

[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.
io1 Linq8
LINQ
five four
APL
five four
Match?
1

one
one

three
three

nine
nine

eight
eight

six
six

seven
seven

two
two

zero
zero

io0 Linq8
LINQ
five four
APL
five four
Match?
1

one
one

three
three

nine
nine

eight
eight

six
six

seven
seven

two
two

zero
zero

The APL+Win function is defined as follows:


ZLinq8;Linq;APL
Projection Operators: Select - Transformation
Linqwi 'Linq8'
APL5 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 (LinqAPL)

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

Page 21 of 71

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]

RL LCase R
Return R in uppercase
Lio
:for i :in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
((i=,R)/,R)'abcdefghijklmnopqrstuvwxyz'[L]
LL+1
:endfor

RL UCase R
Return R in uppercase
Lio
:for i :in 'abcdefghijklmnopqrstuvwxyz'
((i=,R)/,R)'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[L]
LL+1
:endfor

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

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

ZLinq9;Linq;APL
Projection Operators - Select - Anonymous Type 1
Linqwi 'Linq9'
APL,/(UCase "aPPLE" "BlUeBeRrY" "cHeRry") (LCase "aPPLE"
"BlUeBeRrY" "cHeRry")
[4]
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

[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 elements 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 zerothe index origin in C#.

The APL+Win functionindex origin independentis:


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

ZLinq10;Linq;APL
Projection Operators: Select - Anonymous Types 2
Linqwi 'Linq10'
APL5 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]

Page 22 of 71

[5]

Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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:


ZLinq11;Linq;APL
Projections Operators: Select - Anonymous Types 3
Linqwi 'Linq11'
APL0 1 1 1 0/wi 'GetProductListEx' ProductID ProductName Category
UnitPrice UnitsInStock
[4]
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)
[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 zeroin 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
6
7
2
0

0
0
0
1
0
0
1
1
0
0

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

0
0
0
1
0
0
1
1
0
0

Match?
1

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]

ZLinq12;Linq;APL
Projections Operators: Select Indexed
Linqwi 'Linq12'
APLAPL (APL=(-io)+APL5 4 1 3 9 8 6 7 2 0)
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

Page 23 of 71

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
four
one
three
two
zero

Linq13
APL

Match?

four
one
three
two
zero

The function is:


ZLinq13;Linq;APL
Projection Operators: Select - Filtered
Linqwi 'Linq13'
APL"zero" "one" "two" "three" "four" "five" "six" "seven"
"eight" "nine"
[4]
APLAPL[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 (LinqAPL)

[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.
ZLinq14;Linq;APL;numbersA;numbersB;Select
[1]
Projection Operators: SelectMany - Compound from 1
[2]
Linqwi 'Linq14'
[3]
numbersA0 2 4 5 6 8 9
[4]
numbersB1 3 5 7 8
[5]
0 0def ' ZL Select R',tcnl,'[1]
ZL/R '
[6]
APL([io+1]numbersA.<numbersB) Select numbersB
[7]
APLnumbersA,APL
[8]
APL,/(APL)/APL
[9]
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

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

APL
1
3
5
7
8
3
5
7
8
5
7
8
7
8
7
8

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

Page 24 of 71

The results match:

Match?
1
3
5
7
8
3
5
7
8
5
7
8
7
8
7
8

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

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


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:
ZLinq15;Linq;APL;Select;CustomerID;Orders
Projection Operators: SelectMany - Compound from 2
Linqwi 'Linq15'
0 0def ' ZL Select R',tcnl,'[1]
ZL/R '
APLwi 'GetCustomerListEx'
CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5]
CustomerIDioAPL
CustomerID
[6]
Orders(1 0 1) Select (io+9)APL
Orders(OrderID 1,
OrderDate 2, OrderValue 3) without OrderDate 2
[7]
APL(500>1Orders) 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 (LinqAPL)

[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, didnt you?

1.4.11 SelectMany - Compound from 3


This query returns all orders made in 1998 or later.

The APL+Win function that produces the matching result is:


ZLinq16;Linq;APL;Select;CustomerID;Orders

Page 25 of 71

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

Projection Operators: SelectMany - Compound from 3


Linqwi 'Linq16'
0 0def ' ZL Select R',tcnl,'[1]
ZL/R '
APLwi 'GetCustomerListEx'
CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
CustomerIDioAPL
CustomerID
Orders(1 1 0) Select (io+9)APL
Orders(OrderID 1,
OrderDate 2, OrderValue 3) without OrderValue 3
APL(1Orders)
Extract the Order
Date UK Short date format DD/MM/YYYY
APL100,(100 100 10000)fiAPL~'/' Convert to
ISO format YYYYMMDD
APLAPL19980101
Selection vector
APLAPL Select Orders
Orders on or
after 1 January 1998
(CustomerID APL)(APL) Select CustomerID APL Remove those
without qualifying OrderValue
APL(CustomerID),APL
Add CustomerID
APL,[io]/APL
Return as a 3
column array
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)
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

[5]
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
[14]
[15]
[16]
[17]

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 genericit 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.

The APL+Win function that returns the result is:


ZLinq17;Linq;APL;Select;CustomerID;Orders
Projection Operators: SelectMany - from Assignment
Linqwi 'Linq17'
0 0def ' ZL Select R',tcnl,'[1]
ZL/R '
APLwi 'GetCustomerListEx'
CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5]
CustomerIDioAPL
CustomerID
[1]
[2]
[3]
[4]

Page 26 of 71

[6]

Orders(1 0 1) Select (io+9)APL


Orders(OrderID 1,
OrderDate 2, OrderValue 3) without OrderDate 2
APL(2000<1Orders) Select Orders
Order Values
below 500
(CustomerID APL)(APL) Select CustomerID APL Remove those
without qualifying OrderValue
APL(CustomerID),APL
Add CustomerID
APL,[io]/APL
Return as a 3
column array
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)
0
How is the query paradigm different from Linq15? I've included it here
for completeness.

[7]
[8]
[9]
[10]
[11]
[12]
[13]

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:
ZLinq18;Linq;APL;Select;CustomerID;Orders
Projection Operators: SelectMany - Multiple from
Linqwi 'Linq18'
0 0def ' ZL Select R',tcnl,'[1]
ZL/R '
APLwi 'GetCustomerListEx'
CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5]
APL(((io+4)APL)'WA')/APL
Just those in the
WA Region - <Region> is the fifth element
[6]
CustomerIDioAPL
CustomerID
[7]
Orders(1 1 0) Select (io+9)APL
Orders(OrderID 1,
OrderDate 2, OrderValue 3) without OrderValue 3
[8]
APL(1Orders)
Extract the Order
Date UK Short date format DD/MM/YYYY
[9]
APL100,(100 100 10000)fiAPL~'/' Convert to
ISO format YYYYMMDD
[10] APLAPL19970101
Selection vector
[11] APLAPL Select Orders
Orders on or
after 1 January 1998
[12] (CustomerID APL)(APL) Select CustomerID APL Remove those
without qualifying OrderValue
[13] APL(1 0) Select APL
Drop OrderValue
[14] APL(CustomerID),APL
Add CustomerID
[15] APL,[io]/APL
Return as a 2
column array
[16] Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)
[17] 0
[18] This involves selection by date, a data type that APL+Win does not
support inherently
[19] 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 results match:


Linq18
LINQ
LAZYK 10482
LAZYK 10545

APL Match?
LAZYK 10482
1
LAZYK 10545

Page 27 of 71

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

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

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

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 screenshotyou 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 databasethe Customer list. This query
is interesting in that it returns the results as formatted text. What if the raw results are required?

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:
ZLinq19;Linq;APL;Select;CustomerID;Orders
Projection Operators: SelectMany - Indexed
Linqwi 'Linq19'
0 0def ' ZL Select R',tcnl,'[1]
ZL/R '
APLwi 'GetCustomerListEx'
CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5]
CustomerIDioAPL
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)(0Orders) 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 (LinqAPL)

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

1.5 Partitioning Operators


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

Page 28 of 71

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]

ZLinq20;Linq;APL
Partitioning Operators: Take - Simple
Linqwi 'Linq20'
APL35 4 1 3 9 8 6 7 2 0
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

Of course, the results match!


LINQ
5 4 1

Linq20
APL Match?
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:


ZLinq21;Linq;APL;Select;CustomerID;Orders
Projection Operators: Take - Nested
Linqwi 'Linq21'
0 0def ' ZL Select R',tcnl,'[1]
ZL/R '
APLwi 'GetCustomerListEx'
CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5]
APL(((io+4)APL)'WA')/APL
Just those in the
WA Region - <Region> is the fifth element
[6]
CustomerIDioAPL
CustomerID
[7]
Orders(1 1 0) Select (io+9)APL
Orders(OrderID 1,
OrderDate 2, OrderValue 3) Keep OrderID, OrderDate only
[8]
(CustomerID Orders)(0Orders) Select CustomerID Orders Remove
those without any Orders
[9]
APL(CustomerID),Orders
Add CustomerID
[10] APL,[io]/APL
Return as a 3
column array
[11] APL3[io+1]APL
[12] Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

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

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]
APL2APL
[11] APL3[io+1]APL

Page 29 of 71

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
APL3[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]

ZLinq22;Linq;APL
Partitioning Operators: Skip - Simple
Linqwi 'Linq22'
APL45 4 1 3 9 8 6 7 2 0
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

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.
ZLinq23;Linq;APL;Select;CustomerID;Orders
Projection Operators: Skip - Nested
Linqwi 'Linq23'
0 0def ' ZL Select R',tcnl,'[1]
ZL/R '
APLwi 'GetCustomerListEx'
CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5]
APL(((io+4)APL)'WA')/APL
Just those in the
WA Region - <Region> is the fifth element
[6]
CustomerIDioAPL
CustomerID
[7]
Orders(1 1 0) Select (io+9)APL
Orders(OrderID 1,
OrderDate 2, OrderValue 3) Keep OrderID, OrderDate only
[8]
(CustomerID Orders)(0Orders) Select CustomerID Orders Remove
those without any Orders
[9]
APL(CustomerID),Orders
Add CustomerID
[10] APL2 0,[io]/APL
Return as a 3
column array
[11] Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

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

Linq23
TRAIH
TRAIH
TRAIH
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC

10574
10577
10822
10269
10344
10469
10483
10504
10596

Page 30 of 71

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

TRAIH
TRAIH
TRAIH
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC

10574
10577
10822
10269
10344
10469
10483
10504
10596

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

WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC

10693
10696
10723
10740
10861
10904
11032
11066

06/10/1997
08/10/1997
30/10/1997
13/11/1997
30/01/1998
24/02/1998
17/04/1998
01/05/1998

WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC
WHITC

10693
10696
10723
10740
10861
10904
11032
11066

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]

ZLinq24;Linq;APL
Partitioning Operators: TakeWhile - Simple
Linqwi 'Linq24'
APL5 4 1 3 9 8 6 7 2 0
APL(^\APL<6)/APL
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

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.
LINQ APL Match?
5 4 5 4
1

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

ZLinq25;Linq;APL
Partitioning Operators: TakeWhile - Indexed
Linqwi 'Linq25'
APL(^\APL>(APLAPL)-io)/APL5 4 1 3 9 8 6 7 2 0
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

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]

17

APL Match?
3 9 8 6 7 2 0
1

ZLinq26;Linq;APL
Partitioning Operators: SkipWhile - Simple
Linqwi 'Linq26'
APL5 4 1 3 9 8 6 7 2 0
APL(\0=3|APL)/APL
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

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

Page 31 of 71

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

ZLinq27;Linq;APL
Partitioning Operators: SkipWhile - Indexed
Linqwi 'Linq27'
APL(\APL<(-io)+APL)/APL5 4 1 3 9 8 6 7 2 0
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

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.
Linq28
LINQ

APL

apple
ASKOOLUM
blueberry
cherry

apple
ASKOOLUM
blueberry
cherry

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

Match?
1

ZLinq28;Linq;APL
Ordering Operators: OrderBy - Simple 1
Linqwi 'Linq28'
APL"cherry" "apple" "blueberry" "ASKOOLUM" Case insensitive
APLAPL[avLCase APL]
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

1.6.2 OrderBy - Simple 2


This query returns its vector argument sorted in ascending order of the length of each element.
Linq29
LINQ

APL

apple
cherry
blueberry

apple
cherry
blueberry

Match?
1

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

ZLinq29;Linq;APL
Ordering Operators: OrderBy - Simple 2
Linqwi 'Linq29'
APL"cherry" "apple" "blueberry"
APLAPL[' '+.=APL]
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

1.6.3 OrderBy - Simple 3


This query returns all of the products sorted alphabetically by the product name.

Page 32 of 71

This result is interesting: it does not match!


ZLinq30;Linq;APL
Ordering Operators: OrderBy - Simple 3
Linqwi 'Linq30'
APLwi 'GetProductListEx' ProductID ProductName Category UnitPrice
UnitsInStock
[4]
APL(APL)[av(io+1)APL;]
[5]
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

[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:

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# resultnot the datathat 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]

ZLinq31;Linq;APL
Ordering Operators - OrderBy - Comparer
Linqwi 'Linq31'
APL"aPPLE" "AbAcUs" "bRaNcH" "BlUeBeRrY" "ClOvEr"
APLAPL[avUCase APL]
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

"cHeRry"

Page 33 of 71


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]

ZLinq32;Linq;APL
Ordering Operators - OrderByDescending - Simple 1
Linqwi 'Linq32'
APL1.7 2.3 1.9 4.1 2.9
APLAPL[APL]
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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.

The APL+Win function:


ZLinq33;Linq;APL
Ordering Operators - OrderByDescending - Simple 2
Linqwi 'Linq33'
APLwi 'GetProductListEx'
APLAPL[(io+4)wi 'GetProductListEx'] ProductID ProductName
Category UnitPrice UnitsInStock
[5]
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

[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
The APL+Win function:

Page 34 of 71

AbAcUs
AbAcUs

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

ZLinq34;Linq;APL
Ordering Operators - OrderByDescending - Comparer
Linqwi 'Linq34'
APL"aPPLE" "AbAcUs" "bRaNcH" "BlUeBeRrY" "ClOvEr"
APLAPL[avLCase APL]
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

"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

ZLinq35;Linq;APL
Ordering Operators - ThenBy - Simple
Linqwi 'Linq35'
APL"zero" "one" "two" "three" "four" "five" "six"
"eight" "nine"
[4]
APLAPL[avLCase APL]
[5]
APLAPL[APL]
[6]
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

[1]
[2]
[3]

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]

ZLinq36 words;Linq;APL
Ordering Operators - ThenBy - Comparer
Linqwi 'Linq36' words
wordswords[avLCasewords]
APLwords[words]
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)
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.

Page 35 of 71

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

ZLinq37;Linq;APL
Ordering Operators - ThenByDescending - Simple
Linqwi 'Linq37'
APLwi 'GetProductListEx'
APLAPL[((io+3)APL)[av ((io+2)APL) [(io+3)APL]]]
Z('LINQ' 'APL' 'Match?'),[io-0.5]Linq APL (LinqAPL)
0
Take it a little slower!
APLwi 'GetProductListEx'
Get the Product List
UnitPrice(io+3)APL
UnitProce is the 4th element
Category(io+2)APL
Category is the 3rd element
APLAPL[(UnitPrice)[avCategory[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.

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

ZLinq38 words;Linq;APL
Ordering Operators: ThenByDescending - Comparer
Linqwi 'Linq38' words
wordswords[avLCase words]
APLwords[words]
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)
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

Page 36 of 71

six
six

five
five

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

ZLinq39;Linq;APL
Ordering Operators: ThenByDescending - Comparer
words"zero" "one" "two" "three" "four" "five" "six" "seven"
"eight" "nine"
Linqwi 'Linq39' words
APL('i'=11words)/words
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)
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
3
8
7
2

0
0
4
4
1
1
3
3
2
2

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

Match?

0
0
4
4
1
1
3
3
2
2

The following function produces the matching result:


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

ZLinq40;L;R;Linq;APL
Grouping Operators: GroupBy - Simple 1
Linqwi 'Linq40'
APL5 4 1 3 9 8 6 7 2 0
L5|APL
R((LL)=L)/L
(APL L)(RL)L APL
APLL APL
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

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]

Linq41
LINQ
blueberry
banana
chimpanzee
cheese
abacus
apple

b
b
c
c
a
a

APL Match?
blueberry
1
banana
chimpanzee
cheese
abacus
apple

ZLinq41;L;R;Linq;APL
Grouping Operators: GroupBy - Simple 2
Linqwi 'Linq41'
APL"blueberry" "chimpanzee" "abacus" "banana"
L1APL

"apple"

"cheese"

Page 37 of 71

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

R((LL)=L)/L
(L APL) (RL)L APL
APLL APL
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

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]

ZLinq42;Linq;APL;Category;SelectByCategory;Select
Grouping Operators - Simple 3
Linqwi 'Linq42' wi 'Linq42'
APLwi 'GetProductListEx'
0 0def ' ZL Select R',tcnl,'[1]
ZL/R '
Category(io+2)APL
DistinctCategory((CategoryCategory)=Category)/Category
SelectByCategory[io+1](DistinctCategory).Category
APL((DistinctCategory),SelectByCategory Select APL)
Z(Linq) (APL)
Z('APL' 'Linq' 'Match?'),[io-0.5]Z ,(LinqAPL)

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


This query returns a list of each customer's orders, first by year, and then by month. It reconstructs a
hierarchical data source:
:for customer :in
:for order :in
:for year
:for

customers
orders
in orderdate
month in orderdate
. process
:endfor
:endfor
:endfor
:endfor

Unique
Recurring
Recurring
Recurring

This query is a complicated mess. I am not going to present the result in tabular format since this is
probably impossible given the structure of the sample code. The sample reads as follows:

Page 38 of 71

public void Linq43() {


List<Customer> customers = GetCustomerList();
var customerOrderGroups =
from c in customers
select
new {c.CompanyName,
YearGroups =
from o in c.Orders
group o by o.OrderDate.Year into yg
select
new {Year = yg.Key,
MonthGroups =
from o in yg
group o by o.OrderDate.Month into mg
select new { Month = mg.Key, Orders = mg }
}
};
ObjectDumper.Write(customerOrderGroups, 3);
}
Note that ObjectDumper is a class that writes the result to the console; I have not used this anywhere
within this project. The result it produces looks as follows:

APL+Win presents the result of the query for the first customer much more meaningfully:

Page 39 of 71

This customer placed orders in June and October 1997, one (value of 8145) and two (values of 878 and
330) respectively in each. The orders for 1998 must be self-evident from the above.
This function simple brings the results and raw data from C#: feel free to render the data in tabular format.
[1]
[2]
[3]
[4]
[5]

ZLinq43;Linq;APL
Grouping Operators - GroupBy - Nested
Linqwi 'Linq43'
APLiowi 'GetCustomerListEx'
LinqLinq[1;]
See the writeup

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
earn
FORM

last
near

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]

ZLinq44;Linq;APL
Grouping Operators - GroupBy - Comparer
Linqwi 'Linq44'
APL" from " " salt " " earn " "last" "near" "FORM"
APL(+/^\ ' '=APL)APL
APL(-+/^\' '=APL)APL
a((aa)=a)/aLCase APL
b((a)LCase APL)LCase APL
cbb
APL([io+1]((c=c)/c).=c)Select APL
Z('LINQ' 'APL' 'Match?'),[io-0.5]Linq APL (LinqAPL)

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
FORM
SALT
LAST
EARN
NEAR
AJAY
ASKOOLUM
DEBIT CARD
BAD CREDIT
SLOW
OWLS

APL
FROM
SALT
EARN
AJAY
ASKOOLUM
DEBIT CARD
OWLS

FORM
LAST
NEAR

Match?
0

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.

Page 40 of 71

The C# solution also fails to cope with the case of the phrases, although it converts the resultnot the
argumentsto uppercase.
The APL+Win solution, which corrects the C# deficiencies, is:
ZLinq45;Linq;APL
Grouping Operators - GroupBy - Comparer, Mapped
Linqwi 'Linq45'
APLUCase " from " " salt " " earn " "last" "near" "FORM" "ajay"
"askoolum" "Debit Card" "Bad Credit" "OWLS" "slow"
[4]
APL(+/^\ ' '=APL)APL
[5]
APL(-+/^\' '=APL)APL
[6]
a((aa)=a)/aAPL
[7]
b ((a)APL)APL
[8]
cbb
[9]
APL([io+1]((c=c)/c).=c)Select APL
[10] Z('LINQ' 'APL' 'Match?'),[io-0.5]Linq APL (LinqAPL)

[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

ZLinq46;Linq;APL
Set Operators: Distinct - 1
Linqwi 'Linq46' (R2 2 3 5 5)
APL((RR)=R)/R
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)
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.
Linq47
LINQ

APL

Beverages
Condiments
Produce
Meat/Poultry
Seafood
Dairy Products
Confections

Beverages
Condiments
Produce
Meat/Poultry
Seafood
Dairy Products
Confections

Match?
1

Page 41 of 71

Grains/Cereals

Grains/Cereals

The APL+Win function:


ZLinq47;Linq;APL
Set Operators: Distinct - 2
Linqwi 'Linq47'
APL(io+2)wi 'GetProductListEx' ProductID ProductName Category
UnitPrice UnitsInStock
[4]
APL((APLAPL)=APL)/APL
[5]
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

[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]

ZLinq48;L;R;Linq;APL
Set Operators: Union - 1
L(2?10)?15 With possible replication
R(2?10)?23 With possible replication
Linqwi 'Linq48' L R
L((LL)=L)/L
R((RR)=R)/R
APLL,R~L
Z('L' 'R' 'LINQ' 'APL' 'Match?'),L R Linq APL (LinqAPL)

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
U
N
M
I
Q
K
T
P
S
R

C
A
G
U
N
M
I
Q
K
T
P
S
R

Page 42 of 71

[1]
[2]
[3]

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

ZLinq49;L;R;Linq;APL
Set Operators: Union - 2
Linqwi 'Linq49'
(ProductID ProductName Category UnitPrice
UnitsInStock) ( CustomerID CompanyName
Address City Region PostalCode Country Phone
Fax Orders[])
L1(io+1)wi 'GetProductListEx'
R1(io+1)wi 'GetCustomerListEx'
L((LL)=L)/L
R((RR)=R)/R
APLL,R~L
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL
(LinqAPL)

B
J
Z
V
F
E
W
L
O
D
H

B
J
Z
V
F
E
W
L
O
D
H

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

The APL+Win function :


ZLinq50;L;R;Linq;APL
Set Operators: Intersect - 1
L(2?10)?15 With possible replication
R(2?10)?23 With possible replication
Linqwi 'Linq50' L R
APL(LR)/L
APL((APLAPL)=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)

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

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
M
I
Q
K
T
P
S
R

C
A
G
N
M
I
Q
K
T
P
S
R

[1]
[2]
[3]

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

ZLinq51;L;R;Linq;APL
Set Operators: Intersect - 2
Linqwi 'Linq51'
(ProductID ProductName Category UnitPrice
UnitsInStock) ( CustomerID CompanyName
Address City Region PostalCode Country Phone
Fax Orders[])
L1(io+1)wi 'GetProductListEx'
R1(io+1)wi 'GetCustomerListEx'
APL(LR)/L
APL((APLAPL)=APL)/APL
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL
(LinqAPL)

Page 43 of 71

B
V
F
E
W
L
O

B
V
F
E
W
L
O

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

ZLinq52;L;R;Linq;APL
Set Operators: Except 1
L(2?10)?15 With possible replication
R(2?10)?23 With possible replication
Linqwi 'Linq52' L R
L((LL)=L)/L
APL(~LR)/L
Z('L' 'R' 'LINQ' 'APL' 'Match?'),L R Linq APL (LinqAPL)
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

ZLinq53;L;R;Linq;APL
Set Operators: Except - 2
Linqwi 'Linq53'
(ProductID ProductName Category UnitPrice UnitsInStock) ( CustomerID
CompanyName Address City Region PostalCode Country Phone Fax Orders[])
L1(io+1)wi 'GetProductListEx'
R1(io+1)wi 'GetCustomerListEx'
L((LL)=L)/L
APL(~LR)/L
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

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.

Page 44 of 71

Linq54
LINQ
4.1
APL
4.1
Match? 1.0

2.3
2.3

1.7
1.7

The APL+Win function:


ZLinq54;Linq;APL
Conversion Operators: To Array
APL1.7 2.3 1.9 4.1 2.9
Linqwi 'Linq54' APL
APL(2|+\(APL)/1)/APL[APL] io independent
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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]

ZLinq55;Linq;APL
Conversion Operators: To List
Linqwi 'Linq55'
APL"cherry" "apple" "blueberry"
APLAPL[avAPL]
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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}
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]

40
40

ZLinq56;Linq;APL
Conversion Operators: To Dictionary

Page 45 of 71

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

Linqwi 'Linq56'
APL("Alice" 50) ("Bob" 40) ("Cathy" 45)
APL,(("Bob") APL)/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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

ZLinq57;Linq;APL
Conversion Operators: OfType
Linqwi 'Linq57'
APL(0/0) 1.1 "two" 3 "four" 5 "six" 7.1
APL(645=dr APL)/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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:
ZLinq58;Linq;APL
Element Operators: First - Simple
Linqwi 'Linq58'
APL wi 'GetProductListEx'
APL,(<\12=ioAPL)/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

Note that this query returns only the first matching element.

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

Page 46 of 71

38
38

86
86

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:
ZLinq59;Linq;APL
Element Operators: First - Condition
Linqwi 'Linq59'
APL "zero" "one" "two" "three" "four" "five" "six"
"nine"
[4]
APL('o'=APL)/APL
[5]
Z('LINQ' 'APL' 'Match?'),,Linq APL (LinqAPL)

[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:
ZLinq60;Linq;APL
Element Operators: First - Indexed
Linqwi 'Linq60'
APL5 4 1 3 9 8 6 7 2 0
APL((~2|APL)^~2|(-io)+APLAPL)/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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.
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]

ZLinq61;Linq;APL
Element Operators: FirstOrDefault - Simple
Linqwi 'Linq61'
Create a variable from which the data type integer (323) can be
inferred
APL9 10
Create an empty value from the variable - this emulates declaration
APL0/APL
Take the first or default value
APLAPL

Page 47 of 71

[9]

Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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]

ZLinq62;Linq;APL
Element Operators: FirstOrDefault - Condition
Linqwi 'Linq62'
APL789iowi 'GetProductListEx' First element is ProductID
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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 positionremember the position is in index origin zeroor return the smallest
number if none is found.
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]

ZLinq63Ex APL
Element Operators: FirstOrDefault - Indexed
Linqwi 'Linq63'
APL1.7 2.3 4.1 1.9 2.9
APL(0=+/(0.5 0.5.+(-io)+APL)-[io]APL)/APL
:if 0=APL
Z/

Page 48 of 71

[7]
[8]
[9]
[10]

:else
ZAPL
:endif
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

The APL+Win that delivers the same solution as the COM methodsubject to the limitations discussed
is as follows:
ZLinq63;Linq;APL
Element Operators: FirstOrDefault - Indexed
Linqwi 'Linq63'
APL1.7 2.3 4.1 1.9 2.9
APL(0=+/(0.5 0.5.+(-io)+APL)-[io]APL)/APL
:if 0=APL
APL/0
:else
APLAPL
:endif
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

The result:

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

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
The matching result is produced by the following APL+Win function:
[1]
[2]
[3]
[4]
[5]

ZLinq64;Linq;APL
Element Operators: ElementAt
Linqwi 'Linq64'
APL5 4 1 3 9 8 6 7 2 0
APL3(APL<5)/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

Page 49 of 71

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 match the 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]

ZLinq65;Linq;APL
Generation Operators: Range
Linqwi 'Linq65'
APL100+(-io)+50
APLAPL (('even' 'odd')[io+2|APL])
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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

7
7

7
7

7
7

The APL+Win function:


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

Linq66;Linq;APL
Generation Operators: Repeat
Linqwi 'Linq66'
APL10/7
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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

Page 50 of 71

LINQ
1
APL
1
Match? 1

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

ZLinq67;Linq;APL
Quantifiers: Any - Simple
Linqwi 'Linq67'
APL"believe" "relief" "receipt" "field"
APL1('ie').APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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]

ZLinq68;Linq;APL
Quantifiers: Any - Indexed
APL9 4 8 3 5 2 1 6 7
Linqwi 'Linq68' APL
APL(APL=-(-io)+APL)/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

1.12.3 Any - Grouped


This query returns all of the products in categories where at least one of the products in that category is out
of stock.

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

ZLinq69;Linq;APL;EligibleCategories;Category
Quantifiers: Any - Grouped
Linq,/wi 'Linq69'
APLwi 'GetProductListEx'
(Category UnitsInStock)(io+2 4)APL

Page 51 of 71

[5]
[6]

EligibleCategories(0=UnitsInStock)/Category
DistinctCategories((EligibleCategoriesEligibleCategories)=EligibleC
ategories)/EligibleCategories
APL(CategoryEligibleCategories)/APL
APLAPL[DistinctCategory(io+2)APL]
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

[7]
[8]
[9]

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!
Linq70
LINQ
1
APL
1
Match? 1
This function produces the matching result:
[1]
[2]
[3]
[4]
[5]

ZLinq70;Linq;APL
Quantifiers: All - Simple
Linqwi 'Linq70'
APL1 11 3 19 41 65 19
APL1^.=2|APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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]

ZLinq71;Linq;APL;lowNumbers;highNumbers
Quantifiers: All - Indexed
lowNumbers 1 11 3 19 41 65 19
highNumbers 7 19 42 22 45 79 24
Linqwi 'Linq71' lowNumbers highNumbers
APLlowNumbers^.<highNumbers
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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

Page 52 of 71

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]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
[14]

ZLinq72;Linq;APL;Category;UnitsInStock;a;b;EligibleCategories
Quantifiers: All - Grouped
LinqLinq,/wi 'Linq72'
APLwi 'GetProductListEx'
(Category UnitsInStock)(io+2 4)APL
a((CategoryCategory)=Category)/Category
b(0=UnitsInStock)/Category
EligibleCategoriesa~b
APL(CategoryEligibleCategories)/APL
APL(EligibleCategories3APL)APL
Category(io+2)APL
a(CategoryCategory)=Category
Categorya\a/Category
APLAPL[(EligibleCategories)2APL]
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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
The APL+Win function that produces the matching result:
ZLinq73;Linq;APL
Aggregate Operators - Count - Simple
Linqwi 'Linq73'
APL2 2 3 5 5
APL((APLAPL)=APL)/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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.

Page 53 of 71

Linq74
LINQ
5
APL
5
Match? 1

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

ZLinq74;Linq;APL
Aggregate Operators - Count - Conditional
Linqwi 'Linq74'
APL5 4 1 3 9 8 6 7 2 0
APL2+.|APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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
ZLinq75;Linq;APL
Aggregate Operators - Count - Indexed
Linqwi 'Linq75'
numbers5 4 1 3 9 8 6 7 2 0
APL(2|numbers)+.^2|(-io)+numbers
odd numbers at odd
positions (index origin zero)
[5]
APLAPL+(~2|numbers)+.^~2|(-io)+numbers even numbers at even
positions (index origin zero)
[6]
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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

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]

ZLinq76;Linq;APL
Aggregate Operators - Count - Nested
Linqwi 'Linq76'
APLwi 'GetCustomerListEx'
CustomerIDioAPL
OrderCount(io+9)APL
APLCustomerID OrderCount
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

Page 54 of 71

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]

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

APL
12
12
5
6
12
10
13
7

Match?
1

ZLinq77;Linq;APL;a
Aggregate Operators - Count - Grouped
Linqwi 'Linq77'
APLwi 'GetProductListEx'
Category(io+2)APL
a((CategoryCategory)=Category)/Category
APLa (+(aCategory).=a)
Z('LINQ' 'APL' 'Match?'),[io-0.5]Linq APL (LinqAPL)

1.13.6 Sum - Simple


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

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

ZLinq78;Linq;APL
Aggregate Operators - Sum - Simple
Linqwi 'Linq78'
APL5 4 1 3 9 8 6 7 2 0
APL+/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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.
LINQ

Linq79
20

Page 55 of 71

APL
20
Match? 1

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

ZLinq79;Linq;APL
Aggregate Operators - Sum - Projection
Linq,wi 'Linq79'
APL"cherry" "apple" "blueberry"
APL,+/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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

ZLinq80;Linq;APL
Aggregate Operators - Sum - Grouped
Linqwi 'Linq80'
APLwi 'GetProductListEx'
(Category UnitsInStock)(io+2 4)APL
DistinctCategory((CategoryCategory)=Category)/Category
UnitsInStock((DistinctCategoryCategory).=DistinctCategory)[io]Un
itsInStock Separate UnitsInStock by Category (into columns)
APLDistinctCategory (+UnitsInStock)
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

[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]
[5]

ZLinq81;Linq;APL
Aggregate Operators - Min - Simple
Linqwi 'Linq81'
APL5 4 1 3 9 8 6 7 2 0
APL/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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
ZLinq82;Linq;APL

Page 56 of 71

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

Aggregate Operators - Min - Projection


Linqwi 'Linq82'
APL"cherry" "apple" "blueberry"
APL/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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

ZLinq83;Linq;APL;UnitPrice;Category;DistinctCategory
Aggregate Operators - Min - Grouped
Linqwi 'Linq83'
APLwi 'GetProductListEx'
(Category UnitPrice)(io+2 3)APL
DistinctCategory((CategoryCategory)=Category)/Category
UnitPrice((DistinctCategoryCategory).=DistinctCategory)[io]UnitP
rice Separate prices by Category (into columns)
((0=,UnitPrice)/,UnitPrice)/
APLDistinctCategory (UnitPrice)
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

[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

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

ZLinq84;Linq;APL;Category;UnitPrice;DistinctCategory
Aggregate Operators - Max - Grouped
Linqwi 'Linq84'
APLwi 'GetProductListEx'
(Category UnitPrice)(io+2 3)APL
DistinctCategory((CategoryCategory)=Category)/Category
UnitPrice((DistinctCategoryCategory).=DistinctCategory)[io]UnitP
rice Separate prices by Category (into columns)
((0=,UnitPrice)/,UnitPrice)/
APL[io+1](/([io]UnitPrice)UnitPrice)APL

Page 57 of 71

[9]
[10]

APLAPL[DistinctCategory(io+2)APL] Reorder in original category


order
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

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]

ZLinq85;Linq;APL
Aggregate Operators - Max - Simple
Linqwi 'Linq85'
APL5 4 1 3 9 8 6 7 2 0
APL/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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]

ZLinq86;Linq;APL
Aggregate Operators - Max - Projection
Linqwi 'Linq86'
APL"cherry" "apple" "blueberry"
APL/APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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

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

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

ZLinq87;Linq;APL;Category;UnitPrice;DistinctCategory
Aggregate Operators - Max - Grouped
Linqwi 'Linq87'
APLwi 'GetProductListEx'
(Category UnitPrice)(io+2 3)APL
DistinctCategory((CategoryCategory)=Category)/Category
UnitPrice((DistinctCategoryCategory).=DistinctCategory)[io]UnitP
rice Separate prices by Category (into columns)
APLDistinctCategory (UnitPrice)
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

Page 58 of 71

1.13.16 Max - Elements


This sample finds the most expensive products in each category.

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

ZLinq88;Linq;APL;Category;UnitPrice;DistinctCategory
Aggregate Operators - Max - Elements
Linqwi 'Linq88'
APLwi 'GetProductListEx'
(Category UnitPrice)(io+2 3)APL
DistinctCategory((CategoryCategory)=Category)/Category
UnitPrice((DistinctCategoryCategory).=DistinctCategory)[io]UnitP
rice Separate prices by Category (into columns)
APL[io+1](/([io]UnitPrice)UnitPrice)APL
APLAPL[DistinctCategory(io+2)APL] Reorder in original category
order
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

[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

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

ZLinq98;Linq;APL
Custom Sequence Operators - Combine
Linqwi 'Linq98'
APL0 2 4 5 6+.1 3 5 7 8
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)
Error in public static class CustomSequenceOperators

1.13.18 Average - Projection


This sample uses Average to get the average length of the words in the string array.
Linq90
LINQ
6.666666667
APL
6.666666667
Match? 1.000000000

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

ZLinq90;Linq;APL
Aggregate Operators - Average - Projection
Linqwi 'Linq90'
APL "cherry" "apple" "blueberry"
APL(+/APL)APL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

Page 59 of 71

1.13.19 Average - Grouped


This query returns the average price of the products in each category.
Linq91
Beverages
Condiments
Produce
Meat/Poultry
Seafood
Dairy Products
Confections
Grains/Cereals

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

LINQ
37.97916667
23.06250000
32.37000000
54.00666667
20.68250000
28.73000000
25.16000000
20.25000000

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

APL Match?
37.97916667
1
23.06250000
32.37000000
54.00666667
20.68250000
28.73000000
25.16000000
20.25000000

ZLinq91;Linq;APL;NoItemsPerCategory
Aggregate Operators - Average - Grouped
Linqwi 'Linq91'
APLwi 'GetProductListEx'
(Category UnitPrice)(io+2 3)APL
DistinctCategory((CategoryCategory)=Category)/Category
UnitPrice((DistinctCategoryCategory).=DistinctCategory)[io]UnitP
rice Separate prices by Category (into columns)
NoItemsPerCategory+((DistinctCategoryCategory).=DistinctCategory)
APLDistinctCategory ((+UnitPrice)NoItemsPerCategory)
Z('LINQ' 'APL' 'Match?'),[io-0.5] Linq APL (LinqAPL)

In case you are tempted to use the following as the number of items in any category, beware:
+UnitPrice
12 12 5 6 12 10 13 7
It is pure coincidence that the above expression yields the same result as:
+((DistinctCategoryCategory).=DistinctCategory)
12 12 5 6 12 10 13 7
The result matches simply because there are no prices which are zero; had there been, the result will be
incorrect with the first expression.

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]

ZLinq92;Linq;APL
Aggregate Operators: Fold - Simple
Linqwi 'Linq92'
APL/1.7 2.3 1.9 4.1 2.9
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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.

Page 60 of 71

Linq93
startBalance
attemptedWithdrawals
LINQ
APL
Match?

37
29
17
17

140

60

ZLinq93;Linq;APL;startBalance;attemptedWithdrawals;i;R
Aggregate Operators - Fold - Seed
startBalance?1000
attemptedWithdrawals(2?5)?150 With possible replication
Linqwi 'Linq93' startBalance attemptedWithdrawals
istartBalance+\attemptedWithdrawals
APLstartBalance-+/i/attemptedWithdrawals
attemptedWithdrawals(~i)/attemptedWithdrawals
:for R :in attemptedWithdrawals
:if APL>R
APLAPL-R
:endif
:endfor
Z('startBalance' 'attemptedWithdrawals' 'LINQ' 'APL'
'Match?'),startBalance attemptedWithdrawals Linq APL (LinqAPL)

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.

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

1.14 Miscellaneous Operators


This group of operators are also of a trivial nature in an APL+Win context: they illustrate basic principles in
APL+Win that are within the grasp of APL beginners.

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:


ZLinq94;Linq;APL
Miscellaneous Operators - Concat - 1
Linqwi 'Linq94'
APL0 2 4 5 6 8 9,1 3 5 7 8
Z('LINQ' 'APL' 'Match?'),Linq APL (Linq-APL)

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

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.

Page 61 of 71

The APL+Win function:


ZLinq95;Linq;APL
Miscellaneous Operators - Concat - 2
Linqwi '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,/1APL)APL)APL
[6]
Z('LINQ' 'APL' 'Match?'),[io-0.5]Linq APL (LinqAPL)

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.
Linq96
LINQ
1
APL
1
Match? 1
The APL+Win function:
[1]
[2]
[3]
[4]
[5]

ZLinq96;Linq;APL
Miscellaneous Operators - EqualAll - 1
Linqwi 'Linq96'
APL"cherry" "apple" "blueberry"
APL^/APLAPL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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]

ZLinq97;Linq;APL
Miscellaneous Operators - EqualAll - 2
Linqwi 'Linq97'

Page 62 of 71

[3]
[4]
[5]

APL"cherry" "apple" "blueberry"


APL^/APL"apple" "blueberry" "cherry"
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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]

ZLinq97Ex;APL;APL2
Miscellaneous Operators - Element All
APL"cherry" "apple" "blueberry"
APL2"apple" "cherry" "blueberry"
ZAPL^.APL2

Linq97Ex

1.15 Custom Sequence Operators


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

19

This appears deceptively simple; however, Combine is defined as follows :

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

18
19

Copied from the Visual Studio 2008 project, hence the line numbers.
This is defined incorrectly at the URL.

Page 63 of 71

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]

ZLinq98;Linq;APL
Custom Sequence Operators - Combine
Linqwi 'Linq98'
APL0 2 4 5 6+.1 3 5 7 8
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)
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();
}
The APL+Win solution:
[1]
[2]
[3]
[4]
[5]

ZLinq98Ex_IOTA;Linq;APL;Match
Ajay Askoolum
Linq(wi 'Linq98Ex_IOTA' 1 10)(wi 'Linq98Ex_IOTA' 0 10)
APL(+\10/1) ((-io)+10)
MatchLinqAPL
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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]

ZLinq99;Linq;APL
Query Execution - Deferred
Linqwi 'Linq99'
APL5 4 1 3 9 8 6 7 2 0
APL('+\(APL)/1') ('(~io)+APL')
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

Page 64 of 71

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]

ZLinq100;Linq;APL
Query Execution - Immediate
Linqwi 'Linq100'
APL5 4 1 3 9 8 6 7 2 0
APL(+\(APL)/1) ((APL)/APL)
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

1.16.3 Query Reuse


The query is defined such that it can be re-used subsequentlywithin the same scopewith different
arguments.
[1]
[2]
[3]
[4]
[5]
[6]

ZLinq101;Linq;APL;Reuse
Query Execution - Query Reuse
Linqwi 'Linq101'
0 0def ' RReuse R',tcnl,'[1] R(R3)/R'
APL5 4 1 3 9 8 6 7 2 0
APL(Reuse APL) (Reuse -APL)
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)

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.
In theory, any of the standard idioms is eligible for inclusion in 101 APL+Win samples.
Ajay Askoolum

Page 65 of 71

April 2009

References
6.
7.

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 66 of 71

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 Knckebrd
Tunnbrd
Guaran Fantstica
NuNuCa Nu-Nougat-Creme
Gumbr Gummibrchen
Schoggi Schokolade
Rssle Sauerkraut
Thringer Rostbratwurst
Nord-Ost Matjeshering
Gorgonzola Telino
Mascarpone Fabioli
Geitost
Sasquatch Ale
Steeleye Stout
Inlagd Sill
Gravad lax
Cte 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
Tourtire
Meat/Poultry
Pt 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 Semmelkndel
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
Rd Kaviar
Seafood
Longlife Tofu
Produce
Rhnbru Klosterbier
Beverages
Lakkalikri
Beverages
Original Frankfurter grne Soe 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 tabularORDERS, 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 68 of 71

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


Ajay Askoolum
Index
Aggregate Operators
Linq73..............................................................53
Linq74..............................................................54
Linq75..............................................................54
Linq76..............................................................54
Linq77..............................................................55
Linq78..............................................................55
Linq79..............................................................56
Linq80..............................................................56
Linq81..............................................................56
Linq82..............................................................56
Linq83..............................................................57
Linq84..............................................................57
Linq85..............................................................58
Linq86..............................................................58
Linq87..............................................................58
Linq88..............................................................59
Linq89..............................................................59
Linq90..............................................................59
Linq91..............................................................60
Linq92..............................................................60
Linq93..............................................................61
APL Functions
LCase ..............................................................22
LINQ ................................................................10
Linq1................................................................13
Linq10..............................................................22
Linq100............................................................65
Linq101............................................................65
Linq11..............................................................23
Linq12..............................................................23
Linq13..............................................................24
Linq14..............................................................24
Linq15..............................................................25
Linq16..............................................................25
Linq17..............................................................26
Linq18..............................................................27
Linq19..............................................................28
Linq2................................................................16
Linq20..............................................................29
Linq21..............................................................29
Linq22..............................................................30
Linq23..............................................................30
Linq24..............................................................31
Linq25..............................................................31
Linq26..............................................................31
Linq27..............................................................32
Linq28..............................................................32
Linq29..............................................................32
Linq3................................................................17
Linq30..............................................................33
Linq31..............................................................33
Linq32..............................................................34
Linq33..............................................................34
Linq34..............................................................35
Linq35..............................................................35
Linq36..............................................................35

Ajay Askoolum

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

Linq88..............................................................59
Linq89..............................................................59
Linq9................................................................22
Linq90..............................................................59
Linq91..............................................................60
Linq92..............................................................60
Linq93..............................................................61
Linq94..............................................................61
Linq95..............................................................62
Linq96..............................................................62
Linq97..............................................................62
Linq97Ex .........................................................63
Linq98..............................................................64
Linq98Ex_IOTA ...............................................64
Linq99..............................................................64
UCase..............................................................22
Comparable Nested Array ................................38
Conversion Operators
Linq54..............................................................45
Linq55..............................................................45
Linq56..............................................................45
Linq57..............................................................46
Custom Sequence Operators
Linq94..............................................................61
Linq95..............................................................62
Linq96..............................................................62
Linq97..............................................................62
Linq98..............................................................64
CUSTOMERS.XML .........................................8, 68
Element Operators
Linq58..............................................................46
Linq59..............................................................47
Linq60..............................................................47
Linq61..............................................................47
Linq62..............................................................48
Linq63..............................................................49
Linq64..............................................................49
Generation Operators
Linq65..............................................................50
Linq66..............................................................50
Grouping Operators
Linq40..............................................................37
Linq41..............................................................37
Linq42..............................................................38
Linq43..............................................................40
Linq44..............................................................40
Linq45..............................................................41
Operators
Aggregate ........................................................53
Conversion ......................................................44
CustomSequence............................................63
Element ...........................................................46
Generation.......................................................50
Grouping..........................................................37
Miscellaneous..................................................61
Partitioning ......................................................28
Projection ........................................................19
Quantifiers .......................................................50
Restriction .......................................................12
Set ...................................................................41

Page 70 of 71

Ordering Operators
Linq28 ..............................................................32
Linq29 ..............................................................32
Linq30 ..............................................................33
Linq31 ..............................................................33
Linq32 ..............................................................34
Linq33 ..............................................................34
Linq34 ..............................................................35
Linq35 ..............................................................35
Linq36 ..............................................................35
Linq37 ..............................................................36
Linq38 ..............................................................36
Linq39 ..............................................................37
Partitioning Operators
Linq20 ..............................................................29
Linq21 ..............................................................29
Linq22 ..............................................................30
Linq23 ..............................................................30
Linq24 ..............................................................31
Linq25 ..............................................................31
Linq26 ..............................................................31
Linq27 ..............................................................32
Projection Operators
Linq10 ..............................................................22
Linq11 ..............................................................23
Linq12 ..............................................................23
Linq13 ..............................................................24
Linq14 ..............................................................24
Linq15 ..............................................................25
Linq16 ..............................................................25
Linq17 ..............................................................26
Linq18 ..............................................................27
Linq19 ..............................................................28
Linq6 ................................................................20
Linq7 ................................................................21
Linq8 ................................................................21
Linq9 ................................................................22
Quantifiers
Linq67 ..............................................................51
Linq68 ..............................................................51
Linq69 ..............................................................51
Linq70 ..............................................................52
Linq71 ..............................................................52
Linq72 ..............................................................53
Query Execution
Linq100 ............................................................65
Linq101 ............................................................65
Linq99 ..............................................................64
Restriction Operators
Linq1 ................................................................13
Linq2 ................................................................16
Linq3 ................................................................17
Linq4 ................................................................18
Linq5 ................................................................19
Set Operators
Linq46 ..............................................................41
Linq47 ..............................................................42
Linq48 ..............................................................42
Linq49 ..............................................................42
Linq50 ..............................................................43

Linq51..............................................................43
Linq52..............................................................44
Linq53..............................................................44

Signature ............................................................14
XML NOTEPAD...............................................8, 68

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 71 of 71

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs
// 101 LINQ to OBJECTS: C# (with numerous corrections for Framework 3.5) to APL+Win - Ajay
Askoolum
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace LINQ
{
public static class CustomSequenceOperators
{
public static IEnumerable<T> Combine<T>(this IEnumerable<T> first, IEnumerable<T>
second, Func<T, T, T> func)
{
using (IEnumerator<T> e1 = first.GetEnumerator(), e2 = second.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext())
{
yield return func(e1.Current, e2.Current);
}
}
}
}
public class CaseInsensitiveComparer : IComparer<string>
{
public int Compare(string x, string y)
{
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}
}
public class AnagramEqualityComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return getCanonicalString(x) == getCanonicalString(y);
}
public int GetHashCode(string obj)
{
return getCanonicalString(obj).GetHashCode();
}
private string getCanonicalString(string word)
{
char[] wordChars = word.ToCharArray();
Array.Sort<char>(wordChars);
return new string(wordChars);
}
}
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Explore
{
public Explore()
{
}
public int[] Linq1(int[] numbers, int threshold)
{
var lowNums = from n in numbers
where n < threshold
select n;
return lowNums.ToArray();
}
public string[] Linq2()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs

var soldOutProducts = from p in products


where p.UnitsInStock == 0
select p.ProductName;
return soldOutProducts.ToArray();
}
public string[] Linq3()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var expensiveInStockProducts = from p in products
where p.UnitsInStock > 0 && p.UnitPrice > 3.00M
select p.ProductName;
return expensiveInStockProducts.ToArray();
}
public object[] Linq4()
{
Data myObj = new Data();
List<Data.Customer> customers = myObj.GetCustomerList();
var waCustomers = from c in customers
where c.Region == "WA"
select c;
object[] res = new object[waCustomers.Count()];
int i = 0;
foreach (var customer in waCustomers)
{
object[] res2 = new object[customer.Orders.Length];
int j = 0;
foreach (var order in customer.Orders)
{
res2[j] = new object[] { order.OrderID, order.OrderDate.ToShortDateString
() };
j++;
}
res[i] = new object[] { customer.CustomerID, customer.CompanyName, res2 };
i++;
}
return res;
}
public string[] Linq5()
{
string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven"
, "eight", "nine" };
var shortDigits = digits.Where((digit, index) => digit.Length < index);
return shortDigits.ToArray();
}
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();
}
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;
}
public string[] Linq7()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var productNames = from p in products

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs

select p.ProductName;
return productNames.ToArray();
}
public string[] Linq8()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
string[] strings = { "zero", "one", "two", "three", "four", "five", "six", "seven
", "eight", "nine" };
var textNums = from n in numbers
select strings[n];
return textNums.ToArray();
}
public object[] Linq9()
{
string[] words = { "aPPLE", "BlUeBeRrY", "cHeRry" };
var upperLowerWords = from w in words
select new { Upper = w.ToUpper(), Lower = w.ToLower() };
object[] res = new object[upperLowerWords.Count()];
int i = 0;
foreach (var ul in upperLowerWords)
{
res[i] = new object[] { ul.Upper, ul.Lower };
i++;
}
return res;
}
public object[] Linq10()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
string[] strings = { "zero", "one", "two", "three", "four", "five", "six", "seven
", "eight", "nine" };
var digitOddEvens = from n in numbers
select new { Digit = strings[n], Even = (n % 2 == 0) };
object[] res = new object[digitOddEvens.Count()];
int i = 0;
foreach (var d in digitOddEvens)
{
res[i] = new object[] { d.Digit, d.Even ? "even" : "odd" };
i++;
}
return res;
}
public object[] Linq11()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var productInfos = from p in products
select new { p.ProductName, p.Category, Price = p.UnitPrice };
object[] res = new object[productInfos.Count()];
int i = 0;
foreach (var pdt in productInfos)
{
res[i] = new object[] { pdt.ProductName, pdt.Category, pdt.Price };
i++;
}
return res;
}
public object[] Linq12()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var numsInPlace = numbers.Select((num, index) => new { Num = num, InPlace = (num
== index) });
int[] res = new int[numsInPlace.Count()];
int[] inPlace = new int[numsInPlace.Count()];
int i = 0;
foreach (var n in numsInPlace)
{
res[i] = n.Num;
inPlace[i] = (i == res[i]) ? 1 : 0;

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs

i++;
}
return new object[] { res.ToArray(), inPlace };
}
public string[] Linq13()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven"
, "eight", "nine" };
var lowNums = from n in numbers
where n < 5
select digits[n];
return lowNums.ToArray();
}
public int[,] Linq14()
{
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };
var pairs = from a in numbersA
from b in numbersB
where a < b
select new { a, b };
int[,] res = new int[pairs.Count(), 2];
int i = 0;
foreach (var pair in pairs)
{
res[i, 0] = pair.a;
res[i, 1] = pair.b;
i++;
}
return res;
}
public object[] Linq15()
{
Data myObj = new Data();
List<Data.Customer> customers = myObj.GetCustomerList();
var orders = from c in customers
from o in c.Orders
where o.Total < 500.00M
select new { c.CustomerID, o.OrderID, o.Total };
object[] res = new object[orders.Count()];
int i = 0;
foreach (var order in orders)
{
res[i] = new object[] { order.CustomerID, order.OrderID, order.Total };
i++;
}
return res;
}
public object[] Linq16()
{
Data myObj = new Data();
List<Data.Customer> customers = myObj.GetCustomerList();
var orders = from c in customers
from o in c.Orders
where o.OrderDate >= new DateTime(1998, 1, 1)
select new { c.CustomerID, o.OrderID, o.OrderDate };
object[] res = new object[orders.Count()];
int i = 0;
foreach (var order in orders)
{
res[i] = new object[] { order.CustomerID, order.OrderID, order.OrderDate.
ToShortDateString() };
i++;
}
return res;
}
public object[] Linq17()
{

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs
Data myObj = new Data();
List<Data.Customer> customers = myObj.GetCustomerList();
var orders = from c in customers
from o in c.Orders
where o.Total >= 2000.0M
select new { c.CustomerID, o.OrderID, o.Total };
object[] res = new object[orders.Count()];
int i = 0;
foreach (var order in orders)
{
res[i] = new object[] { order.CustomerID, order.OrderID, order.Total };
i++;
}
return res;
}
public object[] Linq18()
{
Data myObj = new Data();
List<Data.Customer> customers = myObj.GetCustomerList();
DateTime cutoffDate = new DateTime(1997, 1, 1);
var orders =
from c in customers
where c.Region == "WA"
from o in c.Orders
where o.OrderDate >= cutoffDate
select new { c.CustomerID, o.OrderID };
object[] res = new object[orders.Count()];
int i = 0;
foreach (var order in orders)
{
res[i] = new object[] { order.CustomerID, order.OrderID };
i++;
}
return res;
}
public string[] Linq19()
{
Data myObj = new Data();
List<Data.Customer> customers = myObj.GetCustomerList();
var customerOrders = customers.SelectMany(
(cust, custIndex) =>
cust.Orders.Select(o => "Customer #" +
(custIndex + 1) +
" has an order with OrderID " + o.OrderID));
return customerOrders.ToArray();
}
public int[] Linq20()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var first3Numbers = numbers.Take(3);
return first3Numbers.ToArray();
}
public object[] Linq21()
{
Data myObj = new Data();
List<Data.Customer> customers = myObj.GetCustomerList();
var first3WAOrders = (from c in customers
from o in c.Orders
where c.Region == "WA"
select new { c.CustomerID, o.OrderID, o.OrderDate }).Take
(3);
object[] res = new object[first3WAOrders.Count()];
int i = 0;
foreach (var order in first3WAOrders)
{
res[i] = new object[] { order.CustomerID, order.OrderID, order.OrderDate.
ToShortDateString() };
i++;

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs
}
return res;
}
public int[] Linq22()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var allButFirst4Numbers = numbers.Skip(4);
return allButFirst4Numbers.ToArray();
}
public int[] Linq24()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);
return firstNumbersLessThan6.ToArray();
}
public object[] Linq23()
{
Data myObj = new Data();
List<Data.Customer> customers = myObj.GetCustomerList();
var waOrders =
from c in customers
from o in c.Orders
where c.Region == "WA"
select new { c.CustomerID, o.OrderID, o.OrderDate };
var allButFirst2Orders = waOrders.Skip(2);
object[] res = new object[allButFirst2Orders.Count()];
int i = 0;
foreach (var order in allButFirst2Orders)
{
res[i] = new object[] { order.CustomerID, order.OrderID, order.OrderDate.
ToShortDateString() };
i++;
}
return res;
}
public int[] Linq25()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);
return firstSmallNumbers.ToArray();
}
public int[] Linq26()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var allButFirst3Numbers = numbers.SkipWhile(n => n % 3 != 0);
return allButFirst3Numbers.ToArray();
}
public int[] Linq27()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var laterNumbers = numbers.SkipWhile((n, index) => n >= index);
return laterNumbers.ToArray();
}
public string[] Linq28()
{
string[] words = { "cherry", "apple", "blueberry", "ASKOOLUM" };
var sortedWords = from w in words
orderby w
select w;
return sortedWords.ToArray(); ;
}
public string[] Linq29()
{
string[] words = { "cherry", "apple", "blueberry" };
var sortedWords = from w in words
orderby w.Length
select w;
return sortedWords.ToArray();

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs

}
public object[] Linq30()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var sortedProducts = from p in products
orderby p.ProductName
select p;
object[] res = new object[sortedProducts.Count()];
int i = 0;
foreach (var pdt in sortedProducts)
{
res[i] = new object[] { pdt.ProductID, pdt.ProductName, pdt.Category, pdt.
UnitPrice, pdt.UnitsInStock };
i++;
}
return res;
}
public string[] Linq31()
{
string[] words = { "aPPLE", "AbAcUs", "bRaNcH", "BlUeBeRrY", "ClOvEr", "cHeRry" }
;
var sortedWords = words.OrderBy(a => a, new Data.CaseInsensitiveComparer());
return sortedWords.ToArray();
}
public double[] Linq32()
{
double[] doubles = { 1.7, 2.3, 1.9, 4.1, 2.9 };
var sortedDoubles = from d in doubles
orderby d descending
select d;
return sortedDoubles.ToArray();
}
public object[] Linq33()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var sortedProducts = from p in products
orderby p.UnitsInStock descending
select p;
object[] res = new object[sortedProducts.Count()];
int i = 0;
foreach (var pdt in sortedProducts)
{
res[i] = new object[] { pdt.ProductID, pdt.ProductName, pdt.Category, pdt.
UnitPrice, pdt.UnitsInStock };
i++;
}
return res;
}
public string[] Linq34()
{
string[] words = { "aPPLE", "AbAcUs", "bRaNcH", "BlUeBeRrY", "ClOvEr", "cHeRry" }
;
var sortedWords = words.OrderByDescending(a => a, new CaseInsensitiveComparer());
return sortedWords.ToArray();
}
public string[] Linq35()
{
string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven"
, "eight", "nine" };
var sortedDigits = from d in digits
orderby d.Length, d
select d;
return sortedDigits.ToArray();
}
public string[] Linq36(string[] words)
{

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs
var sortedWords =
words.OrderBy(a => a.Length)
.ThenBy(a => a, new CaseInsensitiveComparer());
return sortedWords.ToArray();
}
public object[] Linq37()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var sortedProducts =
from p in products
orderby p.Category, p.UnitPrice descending
select p;
object[] res = new object[sortedProducts.Count()];
int i = 0;
foreach (var pdt in sortedProducts)
{
res[i] = new object[] { pdt.ProductID, pdt.ProductName, pdt.Category, pdt.
UnitPrice, pdt.UnitsInStock };
i++;
}
return res;
}
public string[] Linq38(string[] words)
{
var sortedWords =
words.OrderBy(a => a.Length)
.ThenByDescending(a => a, new CaseInsensitiveComparer());
return sortedWords.ToArray();
}
public string[] Linq39(string[] digits)
{
//string[] digits = { "zero", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine" };
var reversedIDigits = (
from d in digits
where d[1] == 'i'
select d)
.Reverse();
return reversedIDigits.ToArray();
}
public int[,] Linq40()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var numberGroups = from n in numbers
group n by n % 5 into g
select new { Remainder = g.Key, Numbers = g };
int[,] res = new int[numbers.Length, 2];
int i = 0;
foreach (var g in numberGroups)
{
foreach (var n in g.Numbers)
{
res[i, 0] = n; ;
res[i, 1] = g.Remainder;
i++;
}
}
return res;
}
public string[,] Linq41()
{
string[] words = { "blueberry", "chimpanzee", "abacus", "banana", "apple",
"cheese" };
var wordGroups = from w in words
group w by w[0] into g
select new { FirstLetter = g.Key, Words = g };
string[,] res = new string[words.Length, 2];

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs

int i = 0;
foreach (var g in wordGroups)
{
foreach (var w in g.Words)
{
res[i, 0] = g.FirstLetter.ToString();
res[i, 1] = w;
i++;
}
}
return res;
}
public object Linq42()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var orderGroups = from p in products
group p by p.Category into g
select new { Category = g.Key, Products = g };
object[] res = new object[orderGroups.Count()];
int i = 0;
foreach (var pdt in orderGroups)
{
object[] res2 = new object[pdt.Products.Count()];
int j = 0;
foreach (var thisCat in pdt.Products)
{
res2[j] = new object[] { thisCat.ProductID, thisCat.ProductName, thisCat.
Category, thisCat.UnitPrice, thisCat.UnitsInStock };
j++;
}
res[i] = new object[] { pdt.Category, (object[])res2 };
i++;
}
return res;
}
public object Linq43()
{
Data myObj = new Data();
List<Data.Customer> customers = myObj.GetCustomerList();
var customerOrderGroups = from c in customers
select
new
{
c.CompanyName,
YearGroups =
from o in c.Orders
group o by o.OrderDate.Year into yg
select
new
{
Year = yg.Key,
MonthGroups =
from o in yg
group o by o.OrderDate.Month into mg
select new { Month = mg.Key, Orders = mg }
}
};
object[] res = new object[customerOrderGroups.Count()];
int i = 0;
foreach (var cust in customerOrderGroups)
{
object[] resYear = new object[cust.YearGroups.Count()];
for (int j = 0; j < cust.YearGroups.Count(); j++)
{
object[] resMonth = new object[cust.YearGroups.ElementAt(j).MonthGroups.
Count()];
for (int k = 0; k < cust.YearGroups.ElementAt(j).MonthGroups.Count(); k+
+)

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs

10

{
object[] resOrder = new object[cust.YearGroups.ElementAt(j).
MonthGroups.ElementAt(k).Orders.Count()];
for (int l = 0; l < cust.YearGroups.ElementAt(j).MonthGroups.
ElementAt(k).Orders.Count(); l++)
{
resOrder[l] = new object[] { cust.YearGroups.ElementAt(j).
MonthGroups.ElementAt(k).Orders.ElementAt(l).Total };
}
resMonth[k] = new object[] { cust.YearGroups.ElementAt(j).MonthGroups
.ElementAt(k).Month,
resOrder};
};
resYear[j] = new object[] { cust.YearGroups.ElementAt(j).Year, resMonth }
;
}
res[i] = new object[] { cust.CompanyName, resYear };
i++;
}
return res;
}
public object Linq44()
{
string[] anagrams = { "from ", " salt", " earn ", " last ", "near ", " FORM " };
var orderGroups = anagrams.GroupBy(w => w.Trim(), new AnagramEqualityComparer());
object[] res = new object[orderGroups.Count()];
int i = 0;
foreach (var g in orderGroups)
{
res[i] = g.ToArray();
i++;
}
return res;
}
public object Linq45()
{
string[] anagrams = { "from", "salt", "earn", "last", "near", "form", "ajay",
"askoolum", "Debit Card", "Bad Credit", "slow", "OWLS" };
var orderGroups = anagrams.GroupBy(w => w.Trim(), a => a.ToUpper(), new
AnagramEqualityComparer());
object[] res = new object[orderGroups.Count()];
int i = 0;
foreach (var g in orderGroups)
{
res[i] = g.ToArray();
i++;
}
return res;
}
public int[] Linq46(int[] factorsOf300)
{
//int[] factorsOf300 = { 2, 2, 3, 5, 5 };
var uniqueFactors = factorsOf300.Distinct();
return uniqueFactors.ToArray();
}
public string[] Linq47()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var categoryNames = (from p in products
select p.Category).Distinct();
return categoryNames.ToArray();
}
public decimal[] Linq48(decimal[] L, decimal[] R)
{
return L.Union(R).ToArray();
}
public string[] Linq49()
{

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
List<Data.Customer> customers = myObj.GetCustomerList();
var productFirstChars = from p in products
select p.ProductName[0];
var customerFirstChars = from c in customers
select c.CompanyName[0];
var uniqueFirstChars = productFirstChars.Union(customerFirstChars);
// APL+Win does not support data type char
string[] res = new string[uniqueFirstChars.Count()];
int i = 0;
foreach (var c in uniqueFirstChars)
{
res[i] = c.ToString();
i++;
}
return res;
}
public int[] Linq50(int[] numbersA, int[] numbersB)
{
/* int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };
*/
var commonNumbers = numbersA.Intersect(numbersB);
return commonNumbers.ToArray();
}
public string[] Linq51()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
List<Data.Customer> customers = myObj.GetCustomerList();
var productFirstChars = from p in products
select p.ProductName[0];
var customerFirstChars = from c in customers
select c.CompanyName[0];
var commonFirstChars = productFirstChars.Intersect(customerFirstChars);
// APL+Win does not support data type char
string[] res = new string[commonFirstChars.Count()];
int i = 0;
foreach (var c in commonFirstChars)
{
res[i] = c.ToString();
i++;
}
return res;
}
public int[] Linq52(int[] numbersA, int[] numbersB)
{
/* int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };
*/
IEnumerable<int> aOnlyNumbers = numbersA.Except(numbersB);
return aOnlyNumbers.ToArray();
}
public string[] Linq53()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
List<Data.Customer> customers = myObj.GetCustomerList();
var productFirstChars = from p in products
select p.ProductName[0];
var customerFirstChars = from c in customers
select c.CompanyName[0];
var productOnlyFirstChars = productFirstChars.Except(customerFirstChars);
string[] res = new string[productOnlyFirstChars.Count()];
int i = 0;
foreach (var c in productOnlyFirstChars)
{
res[i] = c.ToString();
i++;
}

11

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs

12

return res;
}
public double[] Linq54(double[] doubles)
{
/* double[] doubles = { 1.7, 2.3, 1.9, 4.1, 2.9 }; */
var sortedDoubles = from d in doubles
orderby d descending
select d;
var doublesArray = sortedDoubles.ToArray();
double[] res = new double[(int)(doublesArray.Length / 2.0 + 0.5)];
int i = 0;
for (int d = 0; d < doublesArray.Length; d += 2)
{
res[i] = doublesArray[d];
i++;
}
return res;
}
public string[] Linq55()
{
string[] words = { "cherry", "apple", "blueberry" };
var sortedWords = from w in words
orderby w
select w;
return sortedWords.ToArray(); // APL+Win does not support complex return types
such as List<string>
}
public object[] Linq56()
{
var scoreRecords = new[] { new {Name = "Alice", Score = 50},
new {Name = "Bob" , Score = 40},
new {Name = "Cathy", Score = 45}
};
var scoreRecordsDict = scoreRecords.ToDictionary(sr => sr.Name);
object[] res = new object[2];
res[0] = scoreRecordsDict["Bob"].Name;
res[1] = scoreRecordsDict["Bob"].Score;
return res;
}
public object[] Linq57()
{
object[] numbers = { null, 1.1, "two", 3, "four", 5, "six", 7.1 };
var doubles = numbers.OfType<double>();
object[] res = new object[doubles.Count()];
int i = 0;
foreach (var d in doubles)
{
res[i] = d;
i++;
}
return res;
}
public object[] Linq58()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
Data.Product product12 = (from p in products
where p.ProductID == 12
select p).First();
return new object[] { product12.ProductID, product12.ProductName, product12.
Category, product12.UnitPrice, product12.UnitsInStock };
}
public string Linq59()
{
string[] strings = { "zero", "one", "two", "three", "four", "five", "six", "seven
", "eight", "nine" };
string startsWithO = strings.First(s => s[0] == 'o');
return startsWithO;
}

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs

13

public int Linq60() // Sample code @ URL is faulty


{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
//int evenNum = numbers.First((num, index) => (num % 2 == 0) && (index % 2 == 0))
; // sample code ... does not work!
int evenNum = numbers.Where((num, index) => (num % 2 == 0) && (index % 2 == 0)).
First();
return evenNum;
}
public int Linq61()
{
int[] numbers = { };
int firstNumOrDefault = numbers.FirstOrDefault();
return firstNumOrDefault;
}
public bool Linq62()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
Data.Product product789 = products.FirstOrDefault(p => p.ProductID == 789);
return (product789 != null);
}
public double Linq63()
{
double?[] doubles = { 1.7, 2.3, 4.1, 1.9, 2.9 };
//double? num = doubles.FirstOrDefault((n, index) => (n >= index - 0.5 && n <=
index + 0.5));// faulty line
double? num = doubles.Where((n, index) => (n >= index - 0.5 && n <= index + 0.5))
.FirstOrDefault();
if (num != null)
{
return (double)num;
}
else
{
return double.MinValue;
}
}
public int Linq64()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int fourthLowNum = (from n in numbers
where n < 5
select n).ElementAt(3); // 3 because sequences use 0-based
indexing
return fourthLowNum;
}
public object Linq65()
{
var numbers = from n in Enumerable.Range(100, 50)
select new { Number = n, OddEven = n % 2 == 1 ? "odd" : "even" };
int[] num = new int[numbers.Count()];
string[] str = new string[numbers.Count()];
for (int i = 0; i < numbers.Count(); i++)
{
foreach (var n in numbers)
{
num[i] = n.Number;
str[i] = n.OddEven;
i++;
}
}
return new object[] { num, str };
}
public int[] Linq66()
{
var numbers = Enumerable.Repeat(7, 10);
return numbers.ToArray();
}

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs

14

public bool Linq67()


{
string[] words = { "believe", "relief", "receipt", "field" };
bool iAfterE = words.Any(w => w.Contains("ei"));
return iAfterE;
}
public bool Linq68(int[] numbers) // Sample code @ URL is faulty
{
//int[] numbers = { -9, -4, -8, -3, -5, -2, -1, -6, -7 };
//bool negativeMatch = numbers.Any((n, index) => n == -index); //misleading line
given in the sample
bool negativeMatch = numbers.Select((n, index) => new { n, index }).Any(x => x.n
== -x.index);
return negativeMatch;
}
public object[] Linq69()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var productGroups = from p in products
group p by p.Category into g
where g.Any(p => p.UnitsInStock == 0)
select new { Category = g.Key, Products = g };
object[] res = new object[productGroups.Count()];
int i = 0;
foreach (var pdt in productGroups)
{
object[] res2 = new object[pdt.Products.Count()];
int j = 0;
foreach (var thisCat in pdt.Products)
{
res2[j] = new object[] { thisCat.ProductID, thisCat.ProductName, thisCat.
Category, thisCat.UnitPrice, thisCat.UnitsInStock };
j++;
}
res[i] = res2;
i++;
}
return res;
}
public bool Linq70()
{
int[] numbers = { 1, 11, 3, 19, 41, 65, 19 };
bool onlyOdd = numbers.All(n => n % 2 == 1);
return onlyOdd;
}
public bool Linq71(int[] lowNumbers, int[] highNumbers) // Sample code @ URL is
faulty
{
//int[] lowNumbers = { 1, 11, 3, 19, 41, 65, 19 };
//int[] highNumbers = { 7, 19, 42, 22, 45, 79, 24 };
// Original line from the URL is faulty
//Error 1
Delegate 'System.Func<int,bool>' does not take '2' arguments
//bool allLower = lowNumbers.All((num, index) => num < highNumbers[index]); Error
:'System.Func<int,bool>' does not take '2' arguments
bool allLower = lowNumbers.Select((n, index) => new { n, index }).All(x => x.n <
highNumbers[x.index]);
return allLower;
}
public object[] Linq72()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var productGroups = from p in products
group p by p.Category into g
where g.All(p => p.UnitsInStock > 0)
select new { Category = g.Key, Products = g };
object[] res = new object[productGroups.Count()];
int i = 0;

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs

15

foreach (var pdt in productGroups)


{
object[] res2 = new object[pdt.Products.Count()];
int j = 0;
foreach (var thisCat in pdt.Products)
{
res2[j] = new object[] { thisCat.ProductID, thisCat.ProductName, thisCat.
Category, thisCat.UnitPrice, thisCat.UnitsInStock };
j++;
}
res[i] = res2;
i++;
}
return res;
}
public int Linq73()
{
int[] factorsOf300 = { 2, 2, 3, 5, 5 };
int uniqueFactors = factorsOf300.Distinct().Count();
return uniqueFactors;
}
public int Linq74()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);
return oddNumbers;
}
public int Linq75()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddEvenMatches = numbers.Where((n, index) => n % 2 == index % 2).Count();
return oddEvenMatches;
}
public object Linq76()
{
Data myObj = new Data();
List<Data.Customer> customers = myObj.GetCustomerList();
var orderCounts = from c in customers
select new
{
c.CustomerID,
OrderCount = c.Orders.Count()
};
object[] res = new object[orderCounts.Count()];
int i = 0;
foreach (var ord in orderCounts)
{
res[i] = new object[] { ord.CustomerID, ord.OrderCount };
i++;
}
return res;
}
public object Linq77()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var categoryCounts = from p in products
group p by p.Category into g
select new
{
Category = g.Key,
ProductCount = g.Count()
};
object[] res = new object[categoryCounts.Count()];
int i = 0;
foreach (var ord in categoryCounts)
{
res[i] = new object[] { ord.Category, ord.ProductCount };
i++;

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs
}
return res;
}
public double Linq78()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
double numSum = numbers.Sum();
return numSum;
}
public double Linq79()
{
string[] words = { "cherry", "apple", "blueberry" };
double totalChars = words.Sum(w => w.Length);
return totalChars;
}
public object Linq80()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var categories = from p in products
group p by p.Category into g
select new
{
Category = g.Key,
TotalUnitsInStock = g.Sum(p => p.UnitsInStock)
};
object[] res = new object[categories.Count()];
int i = 0;
foreach (var cat in categories)
{
res[i] = new object[] { cat.Category, cat.TotalUnitsInStock };
i++;
}
return res;
}
public int Linq81()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int minNum = numbers.Min();
return minNum;
}
public int Linq82()
{
string[] words = { "cherry", "apple", "blueberry" };
int shortestWord = words.Min(w => w.Length);
return shortestWord;
}
public object Linq83()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var categories = from p in products
group p by p.Category into g
select new
{
Category = g.Key,
CheapestPrice = g.Min(p => p.UnitPrice)
};
object[] res = new object[categories.Count()];
int i = 0;
foreach (var cat in categories)
{
res[i] = new object[] { cat.Category, cat.CheapestPrice };
i++;
}
return res;
}
public object Linq84()
{

16

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var categories = from p in products
group p by p.Category into g
let minPrice = g.Min(p => p.UnitPrice)
select new
{
Category = g.Key,
CheapestProducts = g.Where(p => p.UnitPrice == minPrice)
};
object[] res = new object[categories.Count()];
int i = 0;
foreach (var cat in categories)
{
res[i] = new object[] { cat.CheapestProducts.ElementAt(0).ProductID,
cat.CheapestProducts.ElementAt(0).ProductName,
cat.CheapestProducts.ElementAt(0).Category,
cat.CheapestProducts.ElementAt(0).UnitPrice,
cat.CheapestProducts.ElementAt(0).UnitsInStock
};
i++;
}
return res;
}
public int Linq85()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int maxNum = numbers.Max();
return maxNum;
}
public int Linq86()
{
string[] words = { "cherry", "apple", "blueberry" };
int longestLength = words.Max(w => w.Length);
return longestLength;
}
public object Linq87()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var categories = from p in products
group p by p.Category into g
select new
{
Category = g.Key,
MostExpensivePrice = g.Max(p => p.UnitPrice)
};
object[] res = new object[categories.Count()];
int i = 0;
foreach (var cat in categories)
{
res[i] = new object[] { cat.Category, cat.MostExpensivePrice };
i++;
}
return res;
}
public object Linq88()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var categories = from p in products
group p by p.Category into g
let maxPrice = g.Max(p => p.UnitPrice)
select new
{
Category = g.Key,
MostExpensiveProducts = g.Where(p => p.UnitPrice ==
maxPrice)

17

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs

18

};
object[] res = new object[categories.Count()];
int i = 0;
foreach (var cat in categories)
{
res[i] = new object[] { cat.MostExpensiveProducts.ElementAt(0).ProductID,
cat.MostExpensiveProducts.ElementAt(0).ProductName,
cat.MostExpensiveProducts.ElementAt(0).Category,
cat.MostExpensiveProducts.ElementAt(0).UnitPrice,
cat.MostExpensiveProducts.ElementAt(0).UnitsInStock
};
i++;
}
return res;
}
public double Linq89()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
double averageNum = numbers.Average();
return averageNum;
}
public double Linq90()
{
string[] words = { "cherry", "apple", "blueberry" };
double averageLength = words.Average(w => w.Length);
return averageLength;
}
public object Linq91()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
var categories = from p in products
group p by p.Category into g
select new
{
Category = g.Key,
AveragePrice = g.Average(p => p.UnitPrice)
};
object[] res = new object[categories.Count()];
int i = 0;
foreach (var cat in categories)
{
res[i] = new object[] { cat.Category, cat.AveragePrice };
i++;
}
return res;
}
public double Linq92()
{
double[] doubles = { 1.7, 2.3, 1.9, 4.1, 2.9 };
double product = doubles.Aggregate((runningProduct, nextFactor) => runningProduct
* nextFactor);
return product;
}
public double Linq93(double startBalance, int[] attemptedWithdrawals)
{
double endBalance = attemptedWithdrawals.Aggregate(startBalance, (balance,
nextWithdrawal) =>
((nextWithdrawal <= balance) ? (balance - nextWithdrawal) :
balance));
return endBalance;
}
public int[] Linq94()
{
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };
var allNumbers = numbersA.Concat(numbersB);
return allNumbers.ToArray();

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs
}
public string[] Linq95()
{
Data myObj = new Data();
List<Data.Customer> customers = myObj.GetCustomerList();
List<Data.Product> products = myObj.GetProductList();
var customerNames = from c in customers
select c.CompanyName;
var productNames = from p in products
select p.ProductName;
var allNames = customerNames.Concat(productNames);
return allNames.ToArray();
}
public bool Linq96()
{
var wordsA = new string[] { "cherry", "apple", "blueberry" };
var wordsB = new string[] { "cherry", "apple", "blueberry" };
bool match = wordsA.SequenceEqual(wordsB);
return match;
}
public bool Linq97()
{
var wordsA = new string[] { "cherry", "apple", "blueberry" };
var wordsB = new string[] { "apple", "blueberry", "cherry" };
bool match = wordsA.SequenceEqual(wordsB);
return match;
}
public int Linq98()
{
int[] vectorA = { 0, 2, 4, 5, 6 };
int[] vectorB = { 1, 3, 5, 7, 8 };
int dotProduct = vectorA.Combine(vectorB, (a, b) => a * b).Sum();
return dotProduct;
}
public int[] Linq98Ex_IOTA(int io, int arg)
{
return Enumerable.Range(io, arg).ToArray();
}
public object[] Linq99()
{
int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int i = 0;
var q = from n in numbers
select ++i;
object[] res1 = new object[numbers.Length];
object[] res2 = new object[numbers.Length];
int j = 0;
foreach (var v in q)
{
res1[j] = v;
res2[j] = i;
j++;
}
return new object[] { res1, res2 };
}
public object[] Linq100()
{
int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int i = 0;
var q = (from n in numbers
select ++i).ToList();
object[] res2 = new object[q.Count()];
int j = 0;
foreach (var v in q)
{
res2[j] = i;
j++;
}
return new object[] { q.ToArray(), res2 };

19

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs

20

}
public object[] Linq101()
{
object[] res = new object[2];
int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var lowNumbers = from n in numbers
where n <= 3
select n;
res[0] = lowNumbers.ToArray();
for (int i = 0; i < 10; i++)
{
numbers[i] = -numbers[i];
}
res[1] = numbers;
return res;
}
public object[] GetProductListEx() //List<Data.Product> GetData()
{
Data myObj = new Data();
List<Data.Product> products = myObj.GetProductList();
object[] res = new object[products.Count];
for (int i = 0; i < products.Count(); i++)
{
res[i] = new object[] { products[i].ProductID, products[i].ProductName,
products[i].Category, products[i].UnitPrice, products[i].UnitsInStock };
}
return res;
}
public object[] GetCustomerListEx() // List<Data.Customer> GetCustomerListEx()
{
Data myObj = new Data();
List<Data.Customer> customers = myObj.GetCustomerList();
object[] res = new object[customers.Count];
for (int i = 0; i < customers.Count(); i++)
{
object[] orders = new object[customers[i].Orders.Count()]; // Number of
Orders for this customer
object[] res2 = new object[customers[i].Orders.Count()];
for (int j = 0; j < customers[i].Orders.Count(); j++)
{
res2[j] = new object[] { customers[i].Orders[j].OrderID, customers[i].
Orders[j].OrderDate.ToShortDateString(), customers[i].Orders[j].Total };
}
res[i] = new object[] { customers[i].CustomerID, customers[i].CompanyName,
customers[i].Address, customers[i].City, customers[i].Region,
customers[i].PostalCode,customers[i].Country,customers[i].Phone,customers
[i].Fax,
res2};
}
return res;
}
public string Client()
{
return System.Reflection.Assembly.GetCallingAssembly().FullName; // and
StrongNameIdentityPermission.
}
}
}

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Data.cs
using
using
using
using
using
using

System;
System.IO;
System.Collections.Generic;
System.Linq;
System.Text;
System.Xml.Linq;

namespace LINQ
{
public class Data
{
public class Customer
{
public string CustomerID;
public string CompanyName;
public string Address;
public string City;
public string Region;
public string PostalCode;
public string Country;
public string Phone;
public string Fax;
public Order[] Orders;
}
public class Order
{
public int OrderID;
public DateTime OrderDate;
public decimal Total;
}
public class Product
{
public int ProductID;
public string ProductName;
public string Category;
public decimal UnitPrice;
public int UnitsInStock;
}
private List<Product> productList;
private List<Customer> customerList;
private void createLists()
{
// Product data created in-memory using collection initializer:
productList =
new List<Product> {
new Product { ProductID = 1, ProductName = "Chai", Category = "Beverages"
, UnitPrice = 18.0000M, UnitsInStock = 39 },
new Product { ProductID = 2, ProductName = "Chang", Category = "Beverages
", UnitPrice = 19.0000M, UnitsInStock = 17 },
new Product { ProductID = 3, ProductName = "Aniseed Syrup", Category =
"Condiments", UnitPrice = 10.0000M, UnitsInStock = 13 },
new Product { ProductID = 4, ProductName = "Chef Anton's Cajun Seasoning"
, Category = "Condiments", UnitPrice = 22.0000M, UnitsInStock = 53 },
new Product { ProductID = 5, ProductName = "Chef Anton's Gumbo Mix",
Category = "Condiments", UnitPrice = 21.3500M, UnitsInStock = 0 },
new Product { ProductID = 6, ProductName = "Grandma's Boysenberry Spread"
, Category = "Condiments", UnitPrice = 25.0000M, UnitsInStock = 120 },
new Product { ProductID = 7, ProductName = "Uncle Bob's Organic Dried
Pears", Category = "Produce", UnitPrice = 30.0000M, UnitsInStock = 15 },
new Product { ProductID = 8, ProductName = "Northwoods Cranberry Sauce",
Category = "Condiments", UnitPrice = 40.0000M, UnitsInStock = 6 },
new Product { ProductID = 9, ProductName = "Mishi Kobe Niku", Category =
"Meat/Poultry", UnitPrice = 97.0000M, UnitsInStock = 29 },
new Product { ProductID = 10, ProductName = "Ikura", Category = "Seafood"
, UnitPrice = 31.0000M, UnitsInStock = 31 },

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Data.cs

new Product { ProductID = 11, ProductName = "Queso Cabrales", Category =


"Dairy Products", UnitPrice = 21.0000M, UnitsInStock = 22 },
new Product { ProductID = 12, ProductName = "Queso Manchego La Pastora",
Category = "Dairy Products", UnitPrice = 38.0000M, UnitsInStock = 86 },
new Product { ProductID = 13, ProductName = "Konbu", Category = "Seafood"
, UnitPrice = 6.0000M, UnitsInStock = 24 },
new Product { ProductID = 14, ProductName = "Tofu", Category = "Produce",
UnitPrice = 23.2500M, UnitsInStock = 35 },
new Product { ProductID = 15, ProductName = "Genen Shouyu", Category =
"Condiments", UnitPrice = 15.5000M, UnitsInStock = 39 },
new Product { ProductID = 16, ProductName = "Pavlova", Category =
"Confections", UnitPrice = 17.4500M, UnitsInStock = 29 },
new Product { ProductID = 17, ProductName = "Alice Mutton", Category =
"Meat/Poultry", UnitPrice = 39.0000M, UnitsInStock = 0 },
new Product { ProductID = 18, ProductName = "Carnarvon Tigers", Category
= "Seafood", UnitPrice = 62.5000M, UnitsInStock = 42 },
new Product { ProductID = 19, ProductName = "Teatime Chocolate Biscuits",
Category = "Confections", UnitPrice = 9.2000M, UnitsInStock = 25 },
new Product { ProductID = 20, ProductName = "Sir Rodney's Marmalade",
Category = "Confections", UnitPrice = 81.0000M, UnitsInStock = 40 },
new Product { ProductID = 21, ProductName = "Sir Rodney's Scones",
Category = "Confections", UnitPrice = 10.0000M, UnitsInStock = 3 },
new Product { ProductID = 22, ProductName = "Gustaf's Knckebrd",
Category = "Grains/Cereals", UnitPrice = 21.0000M, UnitsInStock = 104 },
new Product { ProductID = 23, ProductName = "Tunnbrd", Category =
"Grains/Cereals", UnitPrice = 9.0000M, UnitsInStock = 61 },
new Product { ProductID = 24, ProductName = "Guaran Fantstica",
Category = "Beverages", UnitPrice = 4.5000M, UnitsInStock = 20 },
new Product { ProductID = 25, ProductName = "NuNuCa Nu-Nougat-Creme",
Category = "Confections", UnitPrice = 14.0000M, UnitsInStock = 76 },
new Product { ProductID = 26, ProductName = "Gumbr Gummibrchen",
Category = "Confections", UnitPrice = 31.2300M, UnitsInStock = 15 },
new Product { ProductID = 27, ProductName = "Schoggi Schokolade",
Category = "Confections", UnitPrice = 43.9000M, UnitsInStock = 49 },
new Product { ProductID = 28, ProductName = "Rssle Sauerkraut", Category
= "Produce", UnitPrice = 45.6000M, UnitsInStock = 26 },
new Product { ProductID = 29, ProductName = "Thringer Rostbratwurst",
Category = "Meat/Poultry", UnitPrice = 123.7900M, UnitsInStock = 0 },
new Product { ProductID = 30, ProductName = "Nord-Ost Matjeshering",
Category = "Seafood", UnitPrice = 25.8900M, UnitsInStock = 10 },
new Product { ProductID = 31, ProductName = "Gorgonzola Telino", Category
= "Dairy Products", UnitPrice = 12.5000M, UnitsInStock = 0 },
new Product { ProductID = 32, ProductName = "Mascarpone Fabioli",
Category = "Dairy Products", UnitPrice = 32.0000M, UnitsInStock = 9 },
new Product { ProductID = 33, ProductName = "Geitost", Category = "Dairy
Products", UnitPrice = 2.5000M, UnitsInStock = 112 },
new Product { ProductID = 34, ProductName = "Sasquatch Ale", Category =
"Beverages", UnitPrice = 14.0000M, UnitsInStock = 111 },
new Product { ProductID = 35, ProductName = "Steeleye Stout", Category =
"Beverages", UnitPrice = 18.0000M, UnitsInStock = 20 },
new Product { ProductID = 36, ProductName = "Inlagd Sill", Category =
"Seafood", UnitPrice = 19.0000M, UnitsInStock = 112 },
new Product { ProductID = 37, ProductName = "Gravad lax", Category =
"Seafood", UnitPrice = 26.0000M, UnitsInStock = 11 },
new Product { ProductID = 38, ProductName = "Cte de Blaye", Category =
"Beverages", UnitPrice = 263.5000M, UnitsInStock = 17 },
new Product { ProductID = 39, ProductName = "Chartreuse verte", Category
= "Beverages", UnitPrice = 18.0000M, UnitsInStock = 69 },
new Product { ProductID = 40, ProductName = "Boston Crab Meat", Category
= "Seafood", UnitPrice = 18.4000M, UnitsInStock = 123 },
new Product { ProductID = 41, ProductName = "Jack's New England Clam
Chowder", Category = "Seafood", UnitPrice = 9.6500M, UnitsInStock = 85 },
new Product { ProductID = 42, ProductName = "Singaporean Hokkien Fried
Mee", Category = "Grains/Cereals", UnitPrice = 14.0000M, UnitsInStock = 26 },
new Product { ProductID = 43, ProductName = "Ipoh Coffee", Category =
"Beverages", UnitPrice = 46.0000M, UnitsInStock = 17 },
new Product { ProductID = 44, ProductName = "Gula Malacca", Category =
"Condiments", UnitPrice = 19.4500M, UnitsInStock = 27 },
new Product { ProductID = 45, ProductName = "Rogede sild", Category =

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Data.cs

"Seafood", UnitPrice = 9.5000M, UnitsInStock = 5 },


new Product { ProductID = 46, ProductName = "Spegesild", Category =
"Seafood", UnitPrice = 12.0000M, UnitsInStock = 95 },
new Product { ProductID = 47, ProductName = "Zaanse koeken", Category =
"Confections", UnitPrice = 9.5000M, UnitsInStock = 36 },
new Product { ProductID = 48, ProductName = "Chocolade", Category =
"Confections", UnitPrice = 12.7500M, UnitsInStock = 15 },
new Product { ProductID = 49, ProductName = "Maxilaku", Category =
"Confections", UnitPrice = 20.0000M, UnitsInStock = 10 },
new Product { ProductID = 50, ProductName = "Valkoinen suklaa", Category
= "Confections", UnitPrice = 16.2500M, UnitsInStock = 65 },
new Product { ProductID = 51, ProductName = "Manjimup Dried Apples",
Category = "Produce", UnitPrice = 53.0000M, UnitsInStock = 20 },
new Product { ProductID = 52, ProductName = "Filo Mix", Category =
"Grains/Cereals", UnitPrice = 7.0000M, UnitsInStock = 38 },
new Product { ProductID = 53, ProductName = "Perth Pasties", Category =
"Meat/Poultry", UnitPrice = 32.8000M, UnitsInStock = 0 },
new Product { ProductID = 54, ProductName = "Tourtire", Category = "Meat
/Poultry", UnitPrice = 7.4500M, UnitsInStock = 21 },
new Product { ProductID = 55, ProductName = "Pt chinois", Category =
"Meat/Poultry", UnitPrice = 24.0000M, UnitsInStock = 115 },
new Product { ProductID = 56, ProductName = "Gnocchi di nonna Alice",
Category = "Grains/Cereals", UnitPrice = 38.0000M, UnitsInStock = 21 },
new Product { ProductID = 57, ProductName = "Ravioli Angelo", Category =
"Grains/Cereals", UnitPrice = 19.5000M, UnitsInStock = 36 },
new Product { ProductID = 58, ProductName = "Escargots de Bourgogne",
Category = "Seafood", UnitPrice = 13.2500M, UnitsInStock = 62 },
new Product { ProductID = 59, ProductName = "Raclette Courdavault",
Category = "Dairy Products", UnitPrice = 55.0000M, UnitsInStock = 79 },
new Product { ProductID = 60, ProductName = "Camembert Pierrot", Category
= "Dairy Products", UnitPrice = 34.0000M, UnitsInStock = 19 },
new Product { ProductID = 61, ProductName = "Sirop d'rable", Category =
"Condiments", UnitPrice = 28.5000M, UnitsInStock = 113 },
new Product { ProductID = 62, ProductName = "Tarte au sucre", Category =
"Confections", UnitPrice = 49.3000M, UnitsInStock = 17 },
new Product { ProductID = 63, ProductName = "Vegie-spread", Category =
"Condiments", UnitPrice = 43.9000M, UnitsInStock = 24 },
new Product { ProductID = 64, ProductName = "Wimmers gute Semmelkndel",
Category = "Grains/Cereals", UnitPrice = 33.2500M, UnitsInStock = 22 },
new Product { ProductID = 65, ProductName = "Louisiana Fiery Hot Pepper
Sauce", Category = "Condiments", UnitPrice = 21.0500M, UnitsInStock = 76 },
new Product { ProductID = 66, ProductName = "Louisiana Hot Spiced Okra",
Category = "Condiments", UnitPrice = 17.0000M, UnitsInStock = 4 },
new Product { ProductID = 67, ProductName = "Laughing Lumberjack Lager",
Category = "Beverages", UnitPrice = 14.0000M, UnitsInStock = 52 },
new Product { ProductID = 68, ProductName = "Scottish Longbreads",
Category = "Confections", UnitPrice = 12.5000M, UnitsInStock = 6 },
new Product { ProductID = 69, ProductName = "Gudbrandsdalsost", Category
= "Dairy Products", UnitPrice = 36.0000M, UnitsInStock = 26 },
new Product { ProductID = 70, ProductName = "Outback Lager", Category =
"Beverages", UnitPrice = 15.0000M, UnitsInStock = 15 },
new Product { ProductID = 71, ProductName = "Flotemysost", Category =
"Dairy Products", UnitPrice = 21.5000M, UnitsInStock = 26 },
new Product { ProductID = 72, ProductName = "Mozzarella di Giovanni",
Category = "Dairy Products", UnitPrice = 34.8000M, UnitsInStock = 14 },
new Product { ProductID = 73, ProductName = "Rd Kaviar", Category =
"Seafood", UnitPrice = 15.0000M, UnitsInStock = 101 },
new Product { ProductID = 74, ProductName = "Longlife Tofu", Category =
"Produce", UnitPrice = 10.0000M, UnitsInStock = 4 },
new Product { ProductID = 75, ProductName = "Rhnbru Klosterbier",
Category = "Beverages", UnitPrice = 7.7500M, UnitsInStock = 125 },
new Product { ProductID = 76, ProductName = "Lakkalikri", Category =
"Beverages", UnitPrice = 18.0000M, UnitsInStock = 57 },
new Product { ProductID = 77, ProductName = "Original Frankfurter grne
Soe", Category = "Condiments", UnitPrice = 13.0000M, UnitsInStock = 32 }
};
// Customer/order data read into memory from XML file using XLinq:
//dataPath
string dataPath = @"C:\aplwin\C#COM\LINQ";//Path.GetDirectoryName(System.

D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Data.cs

Reflection.Assembly.GetExecutingAssembly().FullName);
//string dataPath =
@"D:\AJAY\C#\VS2008\LINQ to Objects\LINQ";
string customerListPath = Path.GetFullPath(Path.Combine(dataPath, "customers.xml"
));
customerList = (
from e in XDocument.Load(customerListPath).
Root.Elements("customer")
select new Customer
{
CustomerID = (string)e.Element("id"),
CompanyName = (string)e.Element("name"),
Address = (string)e.Element("address"),
City = (string)e.Element("city"),
Region = (string)e.Element("region"),
PostalCode = (string)e.Element("postalcode"),
Country = (string)e.Element("country"),
Phone = (string)e.Element("phone"),
Fax = (string)e.Element("fax"),
Orders = (
from o in e.Elements("orders").Elements("order")
select new Order
{
OrderID = (int)o.Element("id"),
OrderDate = (DateTime)o.Element("orderdate"),
Total = (decimal)o.Element("total")
})
.ToArray()
})
.ToList();
}
public List<Product> GetProductList()
{
if (customerList == null)
createLists();
return productList;
}
public List<Customer> GetCustomerList()
{
if (customerList == null)
createLists();
return customerList;
}
public class CaseInsensitiveComparer : IComparer<string>
{
public int Compare(string x, string y)
{
return string.Compare(x, y, true);
}
}
}
}

You might also like