Professional Documents
Culture Documents
Linq To Objects101Samples C To AplWin
Linq To Objects101Samples C To AplWin
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
Ajay Askoolum
Page 4 of 71
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.
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.
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.
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.
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.
Page 8 of 71
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.
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.
5.
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.
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.
<
>
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
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.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.
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)
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.
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 .
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
[1]
[2]
[3]
Page 16 of 71
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.
Page 17 of 71
LINQ
Linq4
LAZYK Lazy K Kountry Store
TRAIH Trail's Head Gourmet Provisioners
WHITC White Clover Markets
APL
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
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.
seven
seven
eight
eight
nine
nine
[1]
[2]
[3]
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
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)
16
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]
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
[1]
[2]
[3]
[4]
Page 21 of 71
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
[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]
BLUEBERRY blueberry
BLUEBERRY blueberry
CHERRY cherry
CHERRY cherry
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]
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]
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
Linq13
APL
Match?
four
one
three
two
zero
[1]
[2]
[3]
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
Match?
1
3
5
7
8
3
5
7
8
5
7
8
7
8
7
8
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]
[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?
Page 25 of 71
[1]
[2]
[3]
[4]
[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.
Page 26 of 71
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
[1]
[2]
[3]
[4]
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.
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]
Page 28 of 71
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)
Linq20
APL Match?
5 4 1
1
APL Match?
LAZYK 10482 21/03/1997
1
LAZYK 10545 22/05/1997
TRAIH 10574 19/06/1997
[1]
[2]
[3]
[4]
The APL+Win can compute other variations in this query that may also hide complications.
Page 29 of 71
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]
[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]
[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]
[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]
[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)
Page 31 of 71
[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)
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)
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)
Page 32 of 71
[1]
[2]
[3]
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.
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.
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]
[2]
[3]
[4]
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"
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]
[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"
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]
[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
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
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)
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)
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.
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
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
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)
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.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
[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
[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
[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.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
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)
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
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]
[2]
[3]
[4]
[5]
Linq60
LINQ
6
APL
6
Match? 1
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]
ZLinq62;Linq;APL
Element Operators: FirstOrDefault - Condition
Linqwi 'Linq62'
APL789iowi 'GetProductListEx' First element is ProductID
Z('LINQ' 'APL' 'Match?'),Linq APL (LinqAPL)
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.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)
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
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.
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)
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]
[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]
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]
[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)
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)
Note that the APL+Win solution needs to match the C# result: scalar for scalar, see line [4].
[1]
[2]
[3]
[4]
[5]
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]
[2]
[3]
[4]
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]
[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]
[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)
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]
[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]
[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)
Page 56 of 71
[1]
[2]
[3]
[4]
[5]
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]
[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]
[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]
[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]
[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]
[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]
[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]
[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]
[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]
[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)
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.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
[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
[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]
The function Linq96Ex using the arguments used in this function will return a result that is true.
ZLinq97Ex;APL;APL2
Miscellaneous Operators - Element All
APL"cherry" "apple" "blueberry"
APL2"apple" "cherry" "blueberry"
ZAPL^.APL2
Linq97Ex
19
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
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.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
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)
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
Page 65 of 71
April 2009
References
6.
7.
Page 66 of 71
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
Page 68 of 71
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
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
D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs
14
D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Explore.cs
15
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
D:\AJAY\C#\VS2008\LINQ to Objects\LINQ\LINQ\Data.cs
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);
}
}
}
}