Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

Archive for LINQ

May 7, 2010

LINQ: Mean, Median, and Mode

Filed under: C#,Lambda Expressions,LINQ,VB.NET @ 2:23 am

If you are doing any type of statistical analysis, you probably need to calculate mean, median and mode. There are lots of places on the Web you can find the calculations. This post is different than most in that it uses LINQ and Lambda expressions.

Mean is the statistical average of a set of numbers. This one is easy with LINQ because of the Average function.

In C#:

int[] numbers = { 4, 4, 4, 4, 3, 2, 2, 2, 1 };

double mean = numbers.Average();
Debug.WriteLine(("Mean: " + mean));

In VB:

Dim numbers() As Integer = {4, 4, 4, 4, 3, 2, 2, 2, 1}

Dim mean As Double = numbers.Average()
Debug.WriteLine("Mean: " & mean)

The result is:

Mean: 2.88888888888889

This code uses the Average extension method on the IEnumerable class to calculate the mean, or average, of the numbers.

Median is the middle number of a set of numbers. If there is an even number of entries, it is the average of the two middle numbers.

In C#:

int[] numbers = { 4, 4, 4, 4, 3, 2, 2, 2, 1 };

int numberCount = numbers.Count();
int halfIndex = numbers.Count()/2;
var sortedNumbers = numbers.OrderBy(n=>n);
double median;
if ((numberCount % 2) == 0)
{
    median = ((sortedNumbers.ElementAt(halfIndex) +
        sortedNumbers.ElementAt((halfIndex – 1)))/ 2);
} else {
    median = sortedNumbers.ElementAt(halfIndex);
}
Debug.WriteLine(("Median is: " + median));

In VB:

Dim numbers() As Integer = {4, 4, 4, 4, 3, 2, 2, 2, 1}

Dim numberCount As Integer = numbers.Count
Dim halfIndex As Integer = numbers.Count \ 2
Dim sortedNumbers = numbers.OrderBy(Function(n) n)
Dim median As Double
If (numberCount Mod 2 = 0) Then
    median = (sortedNumbers.ElementAt(halfIndex) +
       sortedNumbers.ElementAt(halfIndex – 1)) / 2
Else
    median = sortedNumbers.ElementAt(halfIndex)
End If
Debug.WriteLine("Median is: " & median)

The result is:

Median is: 3

This code first counts the numbers and divides the count by 2 to find the middle of the list. Note that the VB code uses the backslash (\) to perform an integer division where the C# code uses a forward slash (/) for the division.

It then sorts the numbers in order using the OrderBy extension method and a Lambda expression that simply orders by the numbers.

The last step is to get the element at the middle (if odd) or the average of the two middle elements (if even). The result is the median.

Mode is the number that occurs the largest number of times.

In C#:

int[] numbers = { 4, 4, 4, 4, 3, 2, 2, 2, 1 };

var mode = numbers.GroupBy(n=> n).
    OrderByDescending(g=> g.Count()).
    Select(g => g.Key).FirstOrDefault();
Debug.WriteLine(("Mode is: " + mode));

In VB:

Dim numbers() As Integer = {4, 4, 4, 4, 3, 2, 2, 2, 1}

Dim mode = numbers.GroupBy(Function(n) n).
     OrderByDescending(Function(g) g.Count).
     Select(Function(g) g.Key).FirstOrDefault
Debug.WriteLine("Mode is: " & mode)

The result is:

Mode is: 4

This code uses the GroupBy extension method on IEnumerable to group the numbers by number. It then orders them by the count and selects the first one. This provides the number that occurs the most times.

Use these techniques whenever you need to calculate the mean, median, or mode.

Enjoy!

May 4, 2010

Grouping and Summing with Lambda Expressions

Filed under: C#,Lambda Expressions,LINQ,VB.NET @ 12:43 am

There are often cases where you need to group on multiple properties and potentially sum on others. This post details how to group and sum on multiple properties using lambda expressions.

This example uses an Invoice class, since that provides many opportunities for summing. First, here is a basic Invoice class.

In C#:

class Invoice
{
    public DateTime InvoiceDate { get; set; }
    public int InvoiceType { get; set; }
    public decimal InvoiceAmount { get; set; }
    public int NumberOfItems { get; set; }
}

In VB:

Public Class Invoice
    Public Property InvoiceDate As DateTime
    Public Property InvoiceType As Integer
    Public Property InvoiceAmount As Decimal
    Public Property NumberOfItems As Integer
End Class

Normally, you would populate the Invoice class from data in a database or other data store. But to keep this example simple, the values for the invoices are hard-coded.

In C#:

List<Invoice> invoiceList = new List<Invoice>();

invoiceList = new List<Invoice>
        {new Invoice()
              {
                InvoiceDate=new DateTime(2010,4,30),
                InvoiceType = 1,
                InvoiceAmount = 150,
                NumberOfItems = 8},
        new Invoice()
              {
                InvoiceDate=new DateTime(2010,4,29),
                InvoiceType = 2,
                InvoiceAmount = 215,
                NumberOfItems = 7},
        new Invoice()
              {
                InvoiceDate=new DateTime(2010,4,30),
                InvoiceType = 1,
                InvoiceAmount = 50,
                NumberOfItems = 2},
        new Invoice()
              {
                InvoiceDate=new DateTime(2010,4,29),
                InvoiceType = 2,
                InvoiceAmount = 550,
                NumberOfItems = 5}};

In VB:

Dim invoiceList As List(Of Invoice)

invoiceList = New List(Of Invoice) From
            {New Invoice With
                  {
                    .InvoiceDate = New DateTime(2010, 4, 30),
                    .InvoiceType = 1,
                    .InvoiceAmount = 150,
                    .NumberOfItems = 8},
            New Invoice With
                  {
                    .InvoiceDate = New DateTime(2010, 4, 29),
                    .InvoiceType = 2,
                    .InvoiceAmount = 215,
                    .NumberOfItems = 7},
            New Invoice With
                  {
                    .InvoiceDate = New DateTime(2010, 4, 30),
                    .InvoiceType = 1,
                    .InvoiceAmount = 50,
                    .NumberOfItems = 2},
            New Invoice With
                  {
                    .InvoiceDate = New DateTime(2010, 4, 29),
                    .InvoiceType = 2,
                    .InvoiceAmount = 550,
                    .NumberOfItems = 5}}

This code creates two invoices of type 1 that are dated 4/30/2010 and two invoices of type 2 that are dated 4/29/2010.

Now for the fun part. This example groups on both the InvoiceDate and the InvoiceType properties. The totals accumulate the invoice amount and the number of items. This allows the code to provide totals based both on the date and type.

In C#:

var query = invoiceList
            .GroupBy(g => new { g.InvoiceDate,
                                g.InvoiceType })
            .Select(group => new {
                       InvoiceDate = group.Key.InvoiceDate,
                       InvoiceType = group.Key.InvoiceType,
                       TotalAmount = group.Sum(a=>a.InvoiceAmount),
                       TotalCount = group.Sum(c=>c.NumberOfItems)});

In VB:

Dim query = invoiceList.
           GroupBy(Function(g) New With {Key g.InvoiceDate, 
                                         Key g.InvoiceType}).
           Select(Function(group) New With {
              .InvoiceDate = group.Key.InvoiceDate,
              .InvoiceType = group.Key.InvoiceType,
              .TotalAmount = group.Sum(Function(a) a.InvoiceAmount),
              .TotalCount = group.Sum(Function(c) c.NumberOfItems)})

This code starts with a GroupBy clause with a Lambda expression. The Lambda expression (indicated with the => syntax in C# and the Function keyword in VB) uses the new keyword to create a new anonymous type with two properties: InvoiceDate and InvoiceType. This technique allows the code to group on both properties. More properties can be included here if you need to group on more than two properties.

Notice the Key modifier on the two properties in the Lambda expression for the VB example. This ensures that the anonymous type is immutable, providing read-only values. This is not necessary in the C# code because C# only supports immutable anonymous types.

The next part of the above code is a Select clause with another Lambda expression. The Lambda expression uses the new keyword to create an anonymous type with four properties: the invoice date and type from the grouping and the two totals. Each total uses a Sum clause with a Lambda expression defining the property to sum.

The group.Key syntax provides the key values from the grouping, which in this case are the properties from our first anonymous type.

The group.Sum syntax provides a sum on a particular property in the original list.

To view the results of the query, the code can display the items.

In C#:

foreach (var item in query)
{
    Console.WriteLine("Invoice Date: {0} ({1}) TotalAmount: {2} TotalCount: {3}",
                        item.InvoiceDate.ToShortDateString(),
                        item.InvoiceType,
                        item.TotalAmount,
                        item.TotalCount);
}

In VB:

For Each item In query
    Console.WriteLine("Invoice Date: {0} ({1}) TotalAmount: {2} TotalCount: {3}",
                        item.InvoiceDate.ToShortDateString(),
                        item.InvoiceType,
                        item.TotalAmount,
                        item.TotalCount)
Next

The result is as follows:

Invoice Date: 4/30/2010 (1) TotalAmount: 200 TotalCount: 10
Invoice Date: 4/29/2010 (2) TotalAmount: 765 TotalCount: 12

Use this technique any time you need to group or sum information in your business objects.

Enjoy!

March 25, 2010

Finding a set of Nodes in an XML String

Filed under: C#,LINQ,VB.NET,XML @ 11:48 am

In this prior post, I demonstrated how to find a node in an XML string. In this post, I expand on that topic to find a set of nodes. You can then process those nodes as needed in your application. In this example, the set of nodes are displayed in a ComboBox.

If you are targeting the .NET Framework version 3.5 or later, you can use Linq to XML to retrieve a set of nodes. This example using Linq.

Here is the XML we will use in this example:

<States>
  <State name="California">
    <Regions>
      <Region name="San Luis Obispo">
      <Area name="Santa Maria" />
      <Area name="Seaside" />
      </Region>
      <Region name="Silicon Valley">
        <Area name="San Jose"/>
        <Area name="Sunnyvale"/>
      </Region>
      <Region name="Springfield">
        <Area name="Emeryville"/>
        <Area name="Hooterville"/>
      </Region>
    </Regions>
  </State>
  <State name="Wisconsin">
    <Regions>
      <Region name="Milwaukee">
        <Area name="Mukwanago"/>
        <Area name="Germantown"/>
      </Region>
      <Region name="Fox Valley">
        <Area name="Oshkosh" />
        <Area name="Appleton" />
      </Region>
      <Region name="Springfield">
        <Area name="Ogdenville"/>
        <Area name="Shelbyville"/>
      </Region>
    </Regions>
  </State>
</States>

Starting with a simple example, let’s find the set of all regions and display them in a combo box.

In C#:

// Build a list of region names
var regionElements = doc.Descendants("Region");
var regionEnumeration = regionElements.Select
                        (r => r.Attribute("name").Value);
List<string> regionList = regionEnumeration.ToList();
regionList.Sort();

// Bind to a ComboBox
comboBox1.DataSource = regionList;

// OR

List<string> regions = doc.Descendants("Region").Select
                       (r => r.Attribute("name").Value).ToList();
regions.Sort();
comboBox1.DataSource = regions;

In VB:

‘ Build a list of region names
Dim regionElements = doc…<Region>
Dim regionEnumeration = regionElements.Select(Function(r) r.@name)
Dim regionList As List(Of String) = regionEnumeration.ToList
regionList.Sort()

‘ Bind to a ComboBox
ComboBox1.DataSource = regionList

‘ OR

Dim regions As List(Of String) = doc…<Region>.Select _
                                 (Function(r) r.@name).ToList
regions.Sort()
ComboBox1.DataSource = regions

The code first finds the set of region elements. The C# code uses the XElement methods and the VB code uses XML literals.

Only the name of the regions are needed, so the next statement selects the name attribute. Again, the C# code uses the XElement methods and the VB code uses XML literals.

NOTE: You can also use the XElement methods in VB as well.

The enumeration is then converted to a list so it can be sorted and assigned as a DataSource.

The second set of code combines the set of Linq to XML statements into one for a more condensed version of the code.

The result is as follows:

image

Use this technique any time you need to retrieve a set of nodes from an XML file.

Enjoy.

February 27, 2010

Binding to a ComboBox using a DataTable and Linq

If you retrieve data into a DataTable, it is easy to bind it to a ComboBox. And before Linq, you would filter the DataTable using a DataView. But with Linq, you have some easy to use features for filtering the contents of your ComboBox.

First, here is the basic code for binding a ComboBox to a DataTable.

In C#:

private DataTable dt = Customers.Retrieve();

ComboBox1.DataSource = dt;
ComboBox1.DisplayMember = "FullName";
ComboBox1.ValueMember = "CustomerId";

In VB:

Private dt As DataTable = Customers.Retrieve

ComboBox1.DataSource = dt
ComboBox1.DisplayMember = "FullName"
ComboBox1.ValueMember = "CustomerId"

The Retrieve method on the Customers class retrieves a DataTable of customers. If you want to try this code, you can build a DataTable in code following the techniques covered here.

You can then set the DataSource to the DataTable, set the DisplayMember to the name of the field to display in the ComboBox, and set the ValueMember to the name of the field to use as the field value. This is most often the unique key.

The result looks like this:

image

With Linq you can add filtering criteria. So let’s add a second ComboBox that lists only the customers with a last name that starts with "B".

NOTE: Be sure to set a reference to System.Data.DataSetExtensions.

In C#:

var query = dt.AsEnumerable().Where(c=>
        c.Field<String>("LastName").StartsWith("B"));

ComboBox2.DataSource = query.AsDataView();
ComboBox2.DisplayMember = "FullName";
ComboBox2.ValueMember = "CustomerId";

In VB:

Dim query = dt.AsEnumerable.Where(Function(c) _
       c.Field(Of String)("LastName").StartsWith("B"))

ComboBox2.DataSource = query.AsDataView
ComboBox2.DisplayMember = "FullName"
ComboBox2.ValueMember = "CustomerId"

This code uses a Lambda expression to filter the DataTable to only those rows where the LastName starts with "B".

The AsEnumerable extension method is necessary to allow Linq/Lambda expressions to work with a DataTable. Any field in the DataTable is accessed using c.Field<T> Or c.Field(Of T) where T is the type of the field. In this example, the field is a string.

The second ComboBox is then bound to the query using the AsDataView extension method. This allows the binding to bind the result as a DataView.

The result looks like this.

image

But wait, there is more. Because of the way that binding works. adding rows to the DataTable will add rows to the first ComboBox that is bound to the DataTable. AND if the new row starts with the letter "B", it will add it to the second ComboBox as well.

In this example, code in the Add button does this:

In C#:

private void Button1_Click(object sender, EventArgs e)
{
    dt.Rows.Add(5, "Bond", "James", "Bond, James", DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, 3);
}

In VB:

Private Sub Button1_Click2(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles Button1.Click
    dt.Rows.Add(5, "Bond", "James", "Bond, James", DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, 3)
End Sub

Click the button and the result is as follows:

image

No refreshing or re-executing binding code required. It just works!!

Use this technique any time you want to bind a ComboBox to a filtered set of data from a DataTable.

Enjoy!

January 31, 2010

Finding a Node in an XML String

Filed under: C#,Lambda Expressions,LINQ,VB.NET,XML @ 4:21 pm

A common requirement with an XML file is to find a particular node. If you are targeting the .NET framework 3.5 or higher, you can find the node using Linq to XML or by using Lambda expressions.

As with many of my prior XML examples, the XML string is as follows:

<States>
  <State name="Wisconsin">
    <Regions>
      <Region name="Milwaukee">
        <Area name="Mukwanago"/>
        <Area name="Germantown"/>
      </Region>
      <Region name="Fox Valley">
        <Area name="Oshkosh" />
        <Area name="Appleton" />
      </Region>    
    </Regions>
  </State>
</States>

The code to find the node for the Milwaukee region is as follows:

In C#:

// Be sure to set a reference to System.Core and System.Xml.Linq
XElement states  = XElement.Load("testXML.xml");

// Using LINQ
XElement foundNode;
var query = from XElement r in states.Descendants("Region")
                   where r.Attribute("name").Value == "Milwaukee"
                   select r;
foundNode = query.FirstOrDefault();

// Using Lambda expressions
foundNode = states.Descendants("Region").
     Where(r => r.Attribute("name").Value ==
                         "Milwaukee").FirstOrDefault();

In VB:

‘ Be sure to set a reference to System.Core and System.Xml.Linq
Dim states As XElement = XElement.Load("testXML.xml")

‘ Using LINQ
Dim foundNode As XElement
Dim query = From r As XElement In states…<Region> _
                  Where r.@<name> = "Milwaukee"
foundNode = query.FirstOrDefault()

‘ Using Lambda expression
foundNode = states…<Region>.Where(Function(r) r.@<name> =  _ 
                                 "Milwaukee").FirstOrDefault

This code first loads the XML file containing the XML. The next set of code can be done using LINQ or using Lambda expressions. Use either one, but not both. :-)

The C# code uses the XElement properties and methods. The VB code uses XML literals.

NOTE: The XElement properties and methods work in VB as well.

Enjoy!

NOTE: This post was created based on a prior post that included both finding a node and adding new nodes. This post separates the first step to provide a more straightforward example.

October 20, 2009

Populating a DataGridView from Xml Data

Filed under: C#,Data Binding,LINQ,VB.NET,WinForms,XML @ 11:49 pm

If you are using XML in a WinForms application you may find the need to display the XML data in a DataGridView.

Let’s take this XML:

<states>
    <state name="California">
        <abbreviation>CA</abbreviation>
        <year>1850</year>
        <governor>Schwarzenegger</governor>
    </state>
    <state name="Wisconsin">
        <abbreviation>WI</abbreviation>
        <year>1848</year>
        <governor>Doyle</governor>
    </state>
</states>

Displaying XML in a DataGridView sounds easy, but if you just set the DataGridView DataSource to the XML data, you will get something like this:

image

Notice how it has lots of yuck in it: attribute details, node properties, and so on for every node in the XML. So how do you get something more like this:

image

The trick is to use anonymous types.

The code is provided here in VB and C# and then described in detail below.

NOTE: Be sure to set a reference to System.Core and System.Xml.Linq

In C#:

XElement statesXml = XElement.Parse("<states>" +
    "<state name=’California’>" +
        "<abbreviation>CA</abbreviation>" +
        "<year>1850</year>" +
        "<governor>Schwarzenegger</governor>" +
    "</state>" +
    "<state name=’Wisconsin’>" +
        "<abbreviation>WI</abbreviation>" +
        "<year>1848</year>" +
        "<governor>Doyle</governor>" +
    "</state>" +
   "</states>");

var query = from st in statesXml.Descendants("state")
            select new
            {
                Name = st.Attribute("name").Value,
                Abbrev = st.Element("abbreviation").Value,
                Year = st.Element("year").Value,
                Governor = st.Element("governor").Value
            };
DataGridView1.DataSource = query.ToList();

In VB:

Dim statesXml As XElement = _
    <states>
        <state name="California">
            <abbreviation>CA</abbreviation>
            <year>1850</year>
            <governor>Schwarzenegger</governor>
        </state>
        <state name="Wisconsin">
            <abbreviation>WI</abbreviation>
            <year>1848</year>
            <governor>Doyle</governor>
        </state>
    </states>

Dim query = From st In statesXml…<state> _
            Select New With { _
                  .Name = st.@name, _
                  .Abbrev = st.<abbreviation>.Value, _
                  .Year = st.<year>.Value, _
                  .Governor = st.<governor>.Value}
DataGridView1.DataSource = query.ToList

The first part of this code builds the XML. The C# code uses the XElement.Parse function to build the XML; VB uses XML literals. This part of the code is not necessary if you are reading the XML from another source, such as a file.

The second part of the code leverages Linq to XML to process the set of state XML elements. For each element, it uses the Select New syntax to create an anonymous type. The syntax defines an unnamed (anonymous) type with properties Name, Abbrev, Year, and Governor.

[To view an overview of anonymous types, start here.]

The last line converts the results of the query to a generic list and assigns it to the DataSource property of the DataGridView. Visual Studio uses the anonymous type property names as the text for the column titles and populates the rows with each state element.

Use this technique any time you have XML that you want to display in a DataGridView.

Enjoy!

October 11, 2009

Lambda Expressions: Finding an Item in a Generic List

Filed under: C#,Lambda Expressions,LINQ,VB.NET @ 2:52 pm

In this prior post, I detailed how to build lists of things using a generic List<T>. Once you have a list, you may need to find items in the list. There are several ways to accomplish this, but using lambda expressions is the most concise.

[To begin with an overview of lambda expressions, start here.]

For example, here is how you find an item in a generic list using a for/each statement.

In C#:

Customer foundCustomer = null;
foreach (var c in custList)
{
    if (c.CustomerId == 4)
    {
        foundCustomer = c;
        break;
    }
}
Debug.WriteLine(foundCustomer);

In VB:

Dim foundCustomer As Customer = Nothing
For Each c As Customer In custList
    If c.CustomerId = 4 Then
        foundCustomer = c
        Exit For
    End If
Next
Debug.WriteLine(foundCustomer)

Notice the amount of code required to loop through each item in the list and find the one with a particular customer Id.

You may have heard about language integrated query (LINQ) and how you can use it to search a  list.

In C#:

Customer foundCustomer = null;
var query = from c in custList
            where c.CustomerId == 4
            select c;
foundCustomer = query.FirstOrDefault();
Debug.WriteLine(foundCustomer);

In VB:

Dim foundCustomer As Customer = Nothing
Dim query = From c As Customer In custList _
            Where c.CustomerId = 4 _
            Select c
foundCustomer = query.FirstOrDefault()
Debug.WriteLine(foundCustomer)

So LINQ is cool, but the code does not seem much shorter.

Now let’s look at using lambda expressions to find the item in the list.

In C#:

Customer foundCustomer = null;
foundCustomer = custList.FirstOrDefault(c =>
                        c.CustomerId == 4);
Debug.WriteLine(foundCustomer);

The lambda expression syntax in C# looks like this:

c => c.CustomerId == 4

The code begins with the set of parameters to the lambda expression. The => is the “goes to” or lambda operator. The remainder of the code is the expression itself. In this case, checking for the item in the list where CustomerId is 4.

In VB:

Dim foundCustomer As Customer = Nothing
foundCustomer = custList.FirstOrDefault(Function(c)
                        c.CustomerId = 4)
Debug.WriteLine(foundCustomer)

The lambda expression syntax in VB looks like this:

Function(c) c.CustomerId = 4

The code begins with the word “Function” along with the set of parameters to the lambda expression. The remainder of the code is the expression itself. In this case, checking the item in the list where CustomerId is 4.

.NET 3.5 added a large list of extension methods on the Enumerable class. Any object that implements IEnumerable or IEnumerable<T> (basically any object you can do a for/each over) can use these methods. Many of these extension methods (such as the FirstOrDefault method  shown in the above example) support lambda expressions.

The FirstOrDefault extension method of the Enumerable class returns the first item in the list or the default value for the object. If you pass it a lambda expression, it returns the first item in the list that matches the Boolean expression.

Enjoy!

September 4, 2009

Enumerable.Repeat

Filed under: C#,Lambda Expressions,LINQ,VB.NET @ 3:57 pm

The Enumerable class is new in .NET 3.5 and is part of the System.Linq namespace. It provides a set of static methods that allow you to query any object that implements IEnumerable, basically meaning any object that supports a for/each.

This post focuses on the Repeat method of the Enumerable class and some of the helpful things that this class can do for you.

NOTE: Be sure to set a reference to System.Core.

First, at the top of your code file, add this code.

In C#:

using System.Linq;

In VB:

Imports System.Linq

(Or for VB you can import a namespace using the project properties.)

You can then use the Enumerable.Repeat as shown in the following examples.

In C#:

// Initialize an array to -1 for each of 10 elements
int[] all1 = Enumerable.Repeat(-1, 10).ToArray();

// Initialize an array to "A" for each of 10 elements
string[] allA = Enumerable.Repeat("A", 10).ToArray();

// Convert a single value to an array
int value = 42;
int[] valueArray = Enumerable.Repeat(value, 1).ToArray();

// Generate 10 random numbers
Random rand = new Random();
int[] randomArray = Enumerable.Repeat(0, 10).Select(
                    i => rand.Next(0,10)).ToArray();

// Initialize a list with 5 Customer objects
List<Customer> custList = Enumerable.Repeat(
                    new Customer(), 10).ToList();

In VB:

‘ Initialize an array to -1 for each of 10 elements
Dim all1() As Integer = Enumerable.Repeat(-1, 10).ToArray()

‘ Initialize an array to "A" for each of 10 elements
Dim allA() As String = Enumerable.Repeat("A", 10).ToArray()

‘ Convert a single value to an array
Dim value As Integer = 42
Dim valueArray() As Integer = Enumerable.Repeat(value, 1).ToArray()

‘ Generate 10 random numbers
Dim rand As Random = New Random()
Dim randomArray() As Integer = Enumerable.Repeat(0, 10).Select( _
                    Function(i) rand.Next(0, 10)).ToArray()

‘ Initialize a list with 5 Customer objects
Dim custList As List(Of Customer) = Enumerable.Repeat( _
                    New Customer(), 10).ToList()

The first example initializes an array of 10 integers to –1.

The second example initializes an array of 10 strings to "A".

The third example takes a single value and creates an array from it. This is often necessary when a method requires an array instead of a single element.

The random number example provides another way to generate a random numbers. In this case, it generates 10 random numbers between 0 and 9. Note that this does not ensure uniqueness, so the number 2 for example could occur multiple times in the list.

The last example initializes a list of business objects to an empty set of objects. You could use object initializer syntax in this example to initialize all of the objects to some set of values, such as setting the State = "CA". This could be useful in setting up data for testing or prototyping.

Use the Repeat method any time you want to repeat values in an array or list.

Enjoy!

August 20, 2009

Adding Nodes to an XML String

Filed under: C#,Lambda Expressions,LINQ,VB.NET,XML @ 5:49 pm

I have an XML string as follows:

<States>
  <State name="Wisconsin">
    <Regions>
      <Region name="Milwaukee">
        <Area name="Mukwanago"/>
        <Area name="Germantown"/>
      </Region>
      <Region name="Fox Valley">
        <Area name="Oshkosh" />
        <Area name="Appleton" />
      </Region>    
    </Regions>
  </State>
</States>

I now want to add another area under Milwaukee called "Wauwatosa".

The code to accomplish this task is as follows.

In C#:

// Be sure to set a reference to System.Core and System.Xml.Linq
XElement states  = XElement.Load("testXML.xml");

// New element under the Milwaukee region
XElement newElement  = XElement.Parse(@"<Area name=’Wauwatosa’/>");

// Find the desired parent element
// Using LINQ
XElement parentNode;
var parentQuery = from XElement r in states.Descendants("Region")
                   where r.Attribute("name").Value == "Milwaukee"
                   select r;
parentNode = parentQuery.FirstOrDefault();

// Using Lambda expression
parentNode = states.Descendants("Region").
     Where(r => r.Attribute("name").Value ==
               
"Milwaukee").FirstOrDefault();

if (parentNode != null)
     parentNode.Add(newElement);

states.Save("Revised.xml");

In VB:

‘ Be sure to set a reference to System.Core and System.Xml.Linq
Dim states As XElement = XElement.Load("testXML.xml")

‘ New element under the Milwaukee region
Dim newElement As XElement = <Area name="Wauwatosa"/>

‘ Find the desired parent element
‘ Using LINQ
Dim parentNode As XElement
Dim parentQuery = From r As XElement In states…<Region> _
                  Where r.@<name> = "Milwaukee"
parentNode = parentQuery.FirstOrDefault()

‘ Using Lambda expression
parentNode = states…<Region>.Where(Function(r) r.@<name> =  _
                                    
"Milwaukee").FirstOrDefault

If (parentNode IsNot Nothing) Then
    parentNode.Add(newElement)
End If

states.Save("Revised.xml")

This code first loads in the XML file containing the XML at the top of this post. The code then defines the new element for Wauwatosa using XML literals in VB and the XElement properties and methods in C#.

NOTE: The XElement Descendants property works in VB as well.

The next set of code can be done using LINQ or using Lambda expressions. Use either one, but not both. :-)

If the appropriate parent element was found, the new element is added to it. The Add method adds the element as a child element.

Finally, the code saves the revised XML:

<States>
  <State name="Wisconsin">
    <Regions>
      <Region name="Milwaukee">
        <Area name="Mukwanago" />
        <Area name="Germantown" />
        <Area name="Wauwatosa" />
      </Region>
      <Region name="Fox Valley">
        <Area name="Oshkosh" />
        <Area name="Appleton" />
      </Region>
    </Regions>
  </State>
</States>

Enjoy!

August 19, 2009

Processing Files Using Anonymous Types

Filed under: C#,LINQ,VB.NET @ 4:06 pm

Another use of anonymous types is for processing directories or files. For example, your application needs to collect a set of files and then process them based on a subset of the file properties.

[To begin with an overview of anonymous types, start here.]

Here is an example.

In C#:

// Query the set of files
var fileTemplateQuery = from FileInfo f in 
            new DirectoryInfo(@"C:\temp") 
            .GetFiles("*.*", SearchOption.AllDirectories) 
                 where f.Extension.ToLower() == ".xml" || 
                       f.Extension.ToLower() == ".txt" 
                 select new 
                 { 
                    DateLastModified = f.LastWriteTime, 
                    Extension = f.Extension, 
                    Size = f.Length, 
                    FileName = f.Name 
                 };

foreach (var f in fileTemplateQuery.OrderBy(file =>
                                    file.DateLastModified))
    // Do whatever you need to with the files
    Debug.WriteLine(f.FileName);

In VB:

Dim fileTemplateQuery = From f As FileInfo In _
            My.Computer.FileSystem.GetDirectoryInfo("C:\temp") _
            .GetFiles("*.*", SearchOption.AllDirectories) _
                 Where f.Extension.ToLower = ".xml" OrElse _
                       f.Extension.ToLower = ".txt" _
                 Select New With _
                 { _
                    .DateLastModified = f.LastWriteTime, _
                    .Extension = f.Extension, _
                    .Size = f.Length, _
                    .FileName = f.Name _
                  }

For Each f In _
   fileTemplateQuery.OrderBy(Function(file) file.DateLastModified)
    ‘ Do whatever you need to with the files
    Debug.WriteLine(f.FileName)
Next

This code uses Linq to find a specific set of files. In this case, it finds all of the files in C:\temp and its subdirectories where the extension is .xml or .txt. It then uses an anonymous type to retain the set of desired file properties.

The for/each statement loops through the set of anonymous types in order by date and performs whatever operation is required to process the files.

Enjoy!

Next Page »

© 2014 Deborah's Developer MindScape   Provided by WPMU DEV -The WordPress Experts   Hosted by Microsoft MVPs