One of the new features in C# 4 and VB 10 (.NET 4.0) is the Zip extension method on IEnumerable. This method allows you to merge two lists such as two arrays or two generic List<T>.
The Zip extension method merges the two sequences by matching up each item in one sequence with another item in another sequence.
Starting with a simple example, say you have an array of numbers 0-5. And you also have an array of strings that contain the number names zero-five. You want to merge these into a single array in the form: number (name), e.g. 0 (zero), 1 (one), and so on.
In C#:
int[] numberArray = {0, 1, 2, 3, 4, 5};
string[] nameArray = {"zero", "one", "two",
"three", "four", "five"};
var combinedArray = numberArray.Zip(nameArray,
(number, name) => number.ToString() + " (" +
name + ")").ToArray();
Array.ForEach(combinedArray, s=> Console.WriteLine(s));
In VB:
Dim numberArray() As Integer = {0, 1, 2, 3, 4, 5}
Dim nameArray() As String = {"zero", "one", "two",
"three", "four", "five"}
Dim combinedArray = numberArray.Zip(nameArray,
Function(number, name) number.ToString & " (" &
name & ")").ToArray
Array.ForEach(combinedArray, Sub(s) Console.WriteLine(s))
The first two lines of code declare the two arrays: one with a set of numbers and the other with a set of names.
The Zip extension method combines the two arrays. Use the Zip method on either of the arrays and set the first parameter of the Zip method to the other array. The second parameter of the Zip method is a Lambda expression that details the desired result. In this case, the merged array contains a string concatenation of the number and name.
The parameters to the Lambda expression, number and name in this example, represent a value from each of the two arrays. The first parameter maps to the array defined to the left of the Zip method and the second parameter maps to the array defined as the first parameter to the Zip method.
(For more technical detail on how the Zip method works, see Bart De Smet’s blog post.)
The last line of code uses the ForEach extension method to display each value in the merged array to the console.
The result is:
0 (zero)
1 (one)
2 (two)
3 (three)
4 (four)
5 (five)
Instead of returning a simple array, you can return an array of anonymous types. An anonymous type is like a class that you create dynamically that has no name, hence the reason it is called an anonymous type.
Using the same arrays as in the prior example, this next example returns an array of anonymous types where each item has a number and name.
In C#:
var nums = numberArray.Zip(nameArray,
(number, name) => new { number, name});
foreach (var item in nums)
Console.WriteLine(item);
In VB:
Dim nums = numberArray.Zip(nameArray,
Function(number, name) New With {number, name})
For Each item In nums
Console.WriteLine(item)
Next
This code starts with the same two arrays, but this time it creates an anonymous type. The new keyword defines the anonymous type and the list of items in the curly braces sets the properties of the anonymous type.
The result is:
{ number = 0, name = zero }
{ number = 1, name = one }
{ number = 2, name = two }
{ number = 3, name = three }
{ number = 4, name = four }
{ number = 5, name = five }
Now consider a more business-oriented example. Say you have a list of customers (like the one in this prior post) and a list of invoices (like the one in this prior post). You want to merge the lists into a single list so you can bind it to a grid.
In C#:
var listToBind = custList.Zip(invoiceList,
(cust, inv) => new {
LastName = cust.LastName,
FirstName = cust.FirstName,
InvoiceAmount = inv.InvoiceAmount,
InvoiceDate = inv.InvoiceDate});
dataGridView1.DataSource = listToBind.ToList();
In VB:
Dim listToBind = custList.Zip(invoiceList,
Function(cust, inv) New With {
.LastName = cust.LastName,
.FirstName = cust.FirstName,
.InvoiceAmount = inv.InvoiceAmount,
.InvoiceDate = inv.InvoiceDate})
DataGridView1.DataSource = listToBind.ToList
This code uses the Zip extension method to combine the customer list and the invoice list. It uses the new keyword to create an anonymous type with properties from both lists. The result can be bound to a grid, such as the DataGridView.
The result is as follows:
You can use filtering and sorting to change the contents of the resulting list. For example, the following code only displays invoices with amounts > 100.
In C#:
var listToBind = custList.Zip(invoiceList,
(cust, inv) => new {
LastName = cust.LastName,
FirstName = cust.FirstName,
InvoiceAmount = inv.InvoiceAmount,
InvoiceDate = inv.InvoiceDate}).
Where(c => c.InvoiceAmount>100);
dataGridView1.DataSource = listToBind.ToList();
In VB:
Dim listToBind = custList.Zip(invoiceList,
Function(cust, inv) New With {
.LastName = cust.LastName,
.FirstName = cust.FirstName,
.InvoiceAmount = inv.InvoiceAmount,
.InvoiceDate = inv.InvoiceDate}).
Where(Function(c) c.InvoiceAmount > 100)
DataGridView1.DataSource = listToBind.ToList
Use the Zip extension method any time you need to combine lists or build a new list from existing lists. This is especially useful for binding to grids.
Enjoy!