Tuesday, May 22, 2012

How to use LINQ to return one columns values (C#, LINQ)

I wanted to find a way to get all the data from one column in one pass.  I could do this with a loop or I can use LINQ to shorten my code.
To show the difference I will show you the manual loop way and the LINQ way.

DataTable ldtDataTable = new DataTable("Data");
ldtDataTable.Columns.Add("Column1", typeof(String));
ldtDataTable.Columns.Add("Column2", typeof(String));
ldtDataTable.Columns.Add("Column3", typeof(String));
ldtDataTable.Rows.Add(new object[] { "Row1Column1", "Row1Column2", "Row1Column2" });
ldtDataTable.Rows.Add(new object[] { "Row2Column1", "Row2Column2", "Row2Column2" });
ldtDataTable.Rows.Add(new object[] { "Row3Column1", "Row3Column2", "Row3Column2" });
ldtDataTable.Rows.Add(new object[] { "Row4Column1", "Row4Column2", "Row4Column2" });
ldtDataTable.Rows.Add(new object[] { "Row5Column1", "Row5Column2", "Row5Column2" });

/// I ONLY WANT TO GET THE VALUES IN Column2, separated with a ; (semi-colon)
///This is the variable that will hold my columns values
string lstrValues = string.Empty;
///Here is the manual loop
foreach(DataRow dr in ldtDataTable.Rows)
{
     lstrValues += Convert.ToString(dr["Column2"]) +"; ";
}
Console.WriteLine(lstrValues);
The output from the above code is:
Row1Column2; Row2Column2; Row3Column2; Row4Column2; Row5Column2; 
All though this is completely acceptable, there is a way to do this with out the loop. I am using the same DataTable as outlined as above as well as the same variable
Here is the LINQ way
var t = (from value in ldtDataTable.AsEnumerable() select value.Field("Column2"));
Array.ForEach((String[])t.ToArray(), z => lstrValues += z + "; ");
The output from the above code is:
Row1Column2; Row2Column2; Row3Column2; Row4Column2; Row5Column2;
As you can see this is a much cleaner and simpler way to get the same data.
If you wanted to shorten your code even more, you can do the following:
Array.ForEach((String[])(from value in ldtDataTable.AsEnumerable() select value.Field("Column2"))
.ToArray(), z => lstrValues += z + "; ");

How to use LINQ to join DataTables (C#, LINQ)

Its not often I need to join multiple DataTables in my .Net classes. I attempt to do as much joining on a Dataserver as possible, but when two tables only exists in memory I must.

You can use a DataRelation object to join my two tables or I can use LINQ to get me my results. In this example, I will show you two simple DataTables and how I join the two together.

Here is my first DataTable:
DataTable dtProducts = new DataTable("Companies");
dtProducts.Columns.AddRange(new DataColumn[] { new DataColumn("CompanyName") });
dtProducts.Rows.Add(new object[] { "Microsoft" });
dtProducts.Rows.Add(new object[] { "Google" });
dtProducts.Rows.Add(new object[] { "Apple" });
Here is my second DataTable
DataTable dtProductMappings = new DataTable("CompanyProducts");
dtProductMappings.Columns.AddRange(new DataColumn[]{new DataColumn("CompanyName"), new DataColumn("ProductName") });
dtProductMappings.Rows.Add(new object[] { "Microsoft", "Windows" });
dtProductMappings.Rows.Add(new object[] { "Microsoft", "Office" });
dtProductMappings.Rows.Add(new object[] { "Google", "Internet Search" });
dtProductMappings.Rows.Add(new object[] { "Google", "Android" });
dtProductMappings.Rows.Add(new object[] { "Apple", "iOS" });
dtProductMappings.Rows.Add(new object[] { "Apple", "iPhone" });
dtProductMappings.Rows.Add(new object[] { "Apple", "iPad" });
dtProductMappings.Rows.Add(new object[] { "Apple", "iPod" });
The first table is a list companies and the second is a list of some of their products.

The two DataTables have a common column, CompanyName. I want to return only the rows in CompanyProducts table. For this example I will create an Generic Int List of the companies I want to see products for using LINQ against my Companies DataTable. In this case, I want to see all but Microsoft's products.
List<int> lintProducts = (from s in dtProducts.AsEnumerable()
                          where s.Field<string>("CompanyName") != "Microsoft"
                          select s.Field<int>("CompanyId")).ToList<int>();
This will result in lintProducts having two integers, 2 (Google) and 3 (Apple).
Now for the query to join the two together:
var companyProducts = from a 
                          in dtProductMappings.AsEnumerable()
                          where productfound(a.Field<int>("CompanyId"), lintProducts)
                          select dtProducts.Select("CompanyId = '" + a.Field<int>("CompanyId") +"'")[0]["CompanyName"]+ " - " + a.Field<string>("ProductName");
Finally, I want to display my results. Its a simple as:
Array.ForEach(companyProducts.ToArray(), z => Console.WriteLine(z + ", "));
Here are the results:
Google - Internet Search, 
Google - Android, 
Apple - iOS, 
Apple - iPhone, 
Apple - iPad, 
Apple - iPod,

Notice that I am using a function called productFound to filter out the products I am interested in.
Here is the code for productFound:
static private bool productfound(int currentProduct, List<int> seekingProduct)
{
        bool lbReturn = false;
        foreach (int s in seekingProduct)
        {
                if (s.Equals(currentProduct))
                {
                    lbReturn = true;
                    break;
                }
        }
        return lbReturn;
}
This simply allows me to search through my DataTable to look for one or more value, in this case 2 (Google) and 3 (Apple)