Get distinct values from a column of DataTable in .NET 2.0 - c#

I am working on a legacy project which was developed using .NET Framework 2.0.
In this project, I get distinct values from DataRowCollection by ItemNo column. I am only interested in ItemNo. The DataRow consist of ItemNo, Qty and Date.
I am thinking of iterating the DataRowCollection and adding the unique ItemNo into a list of string as below (not tested)
var items = new List<string>();
foreach (DataRow orderItem in rows)
{
var itemNo = orderItem["ITEMNO"].ToString().Trim();
if(items.Find(delegate(string str) { return str == itemNo ;}) == null)
{
items.Add(itemNo);
}
}
Is there a better way of doing this without LINQ (.Net Framework 2.0 doesnt like LINQ)

// Given a data table:
var dt = new DataTable();
dt.Columns.Add("ITEMNO");
dt.Rows.Add("1 ");
dt.Rows.Add(" 1");
dt.Rows.Add("2");
var dict = new Dictionary<string, bool>();
foreach(DataRow dr in dt.Rows)
{
var itemNo = dr["ITEMNO"].ToString().Trim();
// Take advantage of O(1) lookup:
if (!dict.ContainsKey(itemNo))
{
dict.Add(itemNo, true);
}
}
// Get list from dictionary keys:
var items = new List<string>(dict.Keys);
If you can install .Net 3.5 on the server, and reference System.Core.dll in your application, you can leverage HashSets which would modify the above code to:
var hashSet = new HashSet<string>();
foreach(DataRow dr in dt.Rows)
{
var itemNo = dr["ITEMNO"].ToString().Trim();
// Only unique elements are added to the hash set,
// no need to check for duplicates
hashSet.Add(itemNo);
}
var items = new List<string>(hashSet);
The benefit of using HashSet over a Dictionary is admittedly trivial, but I'd prefer it since I don't care for the arbitrary bool value in the dictionary, but you'd need to meet the .Net 3.5 and reference requisites.

To get distinct values form a column you can use this method:
List<T> SelectDistict<T>(DataTable table, string column)
{
DataTable temp = new DataView(table).ToTable(true, column);
List<T> items = new List<T>();
foreach (DataRow row in temp.Rows)
items.Add(row.Field<T>(column));
return items;
}
In above method I used DataView.ToTable which by passing true as first argument, selects distinct values.
Here is the usage example:
List<string> items = SelectDistict<string>(yourDataTable, "ITEMNO");
Note
If you need to trim values, you can change above code and first create a clone copy of the DataTable. Then add a computed column which contains, trimmed value from the given column name for distinct values by assigning TRIM(column) to Expression property of column. Then follow the steps using the new trimmed column like above code.

Related

LINQ result in DataRow

I have an XML for which I have used LINQ to XML. As I wanted to capture some of the element/ attribute data, I have taken them in a string array. Later I have used a foreach loop to insert these values from string array to a DataRow; as my final goal is to get a DataTable out of it.
Following is my code
System.Data.DataTable dt = new System.Data.DataTable();
dt.Columns.Add("col_1");
dt.Columns.Add("col_2");
dt.Columns.Add("col_3");
string[] arr = new string[3];
var myData = from n in doc.Descendants("product")
select new string[]{
arr[0] = n.Attribute("name").Value,
arr[1] = n.Attribute("prodid").Value,
arr[2] = n.Attribute("price").Value
};
foreach (var item in myData)
{
dt.Rows.Add(item[0],item[1],item[2]);
}
Is is possible to combine these and directly get an output as DataTable from LINQ query instead of using foreach?
Instead of select new string[] can I use something like select new DataTable or instance of DataTable?
I understand that my Table structure should be fixed.
Update
Thanks #CodingDawg & #Rahul Singh, I would now like to know the best approach between these two.
I will check for my sample data to compare the same.
But from your experience which one is better considering large data (10000 elements => 10000 rows)?
There is way to load entire XML into DataSet but I guess you need some specific values and also need to do some custom filtering or stuffs thus you are using Linq-to-XML, you can project the datatable directly without using foreach loop like this:-
DataTable myData = doc.Descendants("product")
.Select(x =>
{
var row = dt.NewRow();
row.SetField<string>("col_1", (string)x.Attribute("name"));
row.SetField<string>("col_2", (string)x.Attribute("prodid"));
row.SetField<string>("col_1", (string)x.Attribute("price"));
return row;
}).CopyToDataTable();
myData will hold the resultant dataTable.
Use the Linq .ToList().ForEach() functions
System.Data.DataTable dt = new System.Data.DataTable();
dt.Columns.Add("col_1");
dt.Columns.Add("col_2");
dt.Columns.Add("col_3");
doc.Descendants("product")
.ToList()
.ForEach(
n => dt.Rows.Add(n.Attribute("name").Value,
n.Attribute("prodid").Value,
n.Attribute("price").Value));

Compare two datatables to find matching values

I have 2 data tables. Each one has one column and I want to compare them and get same values on them but it does not work.
This is my code:
string CurrentRequestUrl = (HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.ToString());
DataTable dt_Item = ERP.BLL_Menu_Item.Custom_Item_ID(CurrentRequestUrl);
DataTable dt2_SysRole = ERP.BLL_Sys_User_Role.Custom_Role(Convert.ToInt64(App.UserID));
var dtOne = (dt_Item.AsEnumerable()).ToList();
var dtTwo = (dt2_SysRole.AsEnumerable()).ToList();
IEnumerable<DataRow> objIntersectResult = ((dtOne).Intersect((dtTwo))).ToList();
How can I find the matching values?
Intersect does not work here because on DataRow it just compares references. Because all rows are different references you get an empty list. Instead you want to compare values. Therefore you can use Join. But which row do you want to return from both tables? If you want both rows you could create an anonymous type of both:
var objJoinResult = from rowItem in dt_Item.AsEnumerable()
join rowSysRole in dt2_SysRole.AsEnumerable()
on rowItem.Field<string>("ColumnName") equals rowSysRole.Field<string>("ColumnName")
select new { rowItem, rowSysRole };
Output:
foreach (var both in objJoinResult)
{
Console.WriteLine("rowItem:{0} rowSysRole:{1}",
string.Join(",", both.rowItem.ItemArray),
string.Join(",", both.rowSysRole.ItemArray));
}

C# Hashtable values get reset with each new key

I have a function that returns a Hashtable. The var dt gets a bunch of RevenueGroupIDs and ProductIDs from the database that map together in a 1-to-many structure. For example:
RevenueGroupID ProductID
1 312
1 313
1 315
2 317
2 319
3 401
3 410
3 411
3 415
The combination of these 2 numbers are always unique -- no repeats. The function builds a Hashtable dictionary of key-value pairs in which the key is always a RevenueGroupID and the value is a List<int> of all the ProductIDs for that RevenueGroupID. The problem is: each time a key-value pair is added, all previous key-value pairs get overwritten with the current one. So at the end, ALL key value pairs are identical to the final one. I have stepped through the code and verified that each key-value pair is correct and unique. I can't see any reason for the reset. I have looked suspiciously at "productIDs.Clear();", but I can't see why that would be messing up the hashtable.
public static Hashtable GetAllProductIDsInAllRevenueGroups()
{
var productIDs = new List<int>();
var ht = new Hashtable();
string sql = #" {my sql here}";
var dt = Utilities.GetDataTableForQuery(sql, null);
int counter = 0;
int revenueGroupID = 0;
int lastRevenueGroupID = 0;
foreach (DataRow row in dt.Rows)
{
revenueGroupID = Utilities.SafeInt(row["RevenueGroupID"]);
int productID = Utilities.SafeInt(row["ProductID"]);
if (revenueGroupID != lastRevenueGroupID && counter > 0)
{
ht.Add(lastRevenueGroupID, productIDs);
productIDs.Clear();
}
productIDs.Add(productID);
lastRevenueGroupID = revenueGroupID;
counter++;
}
ht.Add(lastRevenueGroupID, productIDs);
return ht;
}
This is because you keep adding productIDs list to a hash table without making a copy, and then clear the content:
ht.Add(lastRevenueGroupID, productIDs);
productIDs.Clear(); // This removes all entries from the item stored at the lastRevenueGroupID key
This means that the same object is added over and over again, so you end up with multiple copies of the list that has the content of the last entry.
An easy fix is to make a new list before adding it to hash table, like this:
ht.Add(lastRevenueGroupID, productIDs.ToList());
productIDs.Clear();
The problem is that you are only using one list instead of creating a new list for each item. Adding the list to the hash table doesn't create a copy of the list, it just adds the reference. When you clear the list you will clear the list for all previously added items in the hash table, because they are all the same list.
You can create a new list and add to the hash table when you start a new group. As you keep the reference to the list, you can keep adding numbers to it after it is places in the hash table:
public static Hashtable GetAllProductIDsInAllRevenueGroups()
{
var productIDs;
var ht = new Hashtable();
string sql = #" {my sql here}";
var dt = Utilities.GetDataTableForQuery(sql, null);
int counter = 0;
int revenueGroupID = 0;
int lastRevenueGroupID = 0;
foreach (DataRow row in dt.Rows)
{
revenueGroupID = Utilities.SafeInt(row["RevenueGroupID"]);
int productID = Utilities.SafeInt(row["ProductID"]);
if (counter == 0 || revenueGroupID != lastRevenueGroupID)
{
productIDs = new List<int>();
ht.Add(revenueGroupID, productIDs);
}
productIDs.Add(productID);
lastRevenueGroupID = revenueGroupID;
counter++;
}
return ht;
}
Note: Consider using the strictly typed Dictionary<int, List<int>> instead of Hashtable.

dynamic datatable sorting in ascending or descending

I have created dynamic table
DataTable date = new DataTable();
date.Columns.Add("date1");
and made fill the column name "date1" with date as
date1(Column name)
05-07-2013
10-07-2013
09-07-2013
02-07-2013
and made fill my dynamic table
Now i want this dynamic table data to be sort as ascending or descending order
For eg:
date1(Column name)
02-07-2013
05-07-2013
09-07-2013
10-07-2013
This cannot be done with the original data table. However you can create a new, sorted one:
DataView view = date.DefaultView;
view.Sort = "date1 ASC";
DataTable sortedDate = view.ToTable();
You can use DataTable.Select(filterExpression, sortExpression) method.
Gets an array of all DataRow objects that match the filter criteria,
in the specified sort order.
date.Select("", "YourColumn ASC");
or
date.Select("", "YourColumn DESC");
As an alternative, you can use DataView like;
DataView view = date.DefaultView;
view.Sort = "YourColumn ASC";
DataTable dt = view.ToTable();
Thought I would give in my two cents here. Instead of using a sorting algorithm which takes time and computational performance, why not instead reverse the way in which you are adding data to your data object.
This won't work for everyone's scenario - but for my own it worked out perfectly.
I had a database which listed items in an ascending order, but or ease of use I needed to reverse the way in which people could see the data (DESC) so that the newest input shows at the top, rather then the bottom of the list.
So, I just changed my for loop so instead of working from 0 -> upwards, it started from the length of the datatable (-1 to stop an overflow) and then stops when it is >= to 0;
private Dictionary<string, string> GetComboData(string table, int column, bool id, int idField = 0)
{
SqlClass sql = new SqlClass(database);
Dictionary<string, string> comboBoxData = new Dictionary<string, string>();
if (sql.connectedToServer)
{
sql.SelectResults(SQLCommands.Commands.SelectAll(table));
for (int i = sql.table.Rows.Count-1; i >= 0; i--)
{
string tool = sql.table.Rows[i].ItemArray.Select(x => x.ToString()).ToArray()[column];
string ID = sql.table.Rows[i].ItemArray.Select(x => x.ToString()).ToArray()[idField];
comboBoxData.Add(ID, tool);
}
}
return comboBoxData;
}
using OrderByDescending()
#foreach (var rca in Model.OrderByDescending(x=>x.Id))
{
<tr class="heading">
<td>#rca.PBINo</td>
<td>#rca.Title</td>
<td>#rca.Introduction</td>
<td>#rca.CustomerImpact</td>
<td>#rca.RootCauseAnalysis</td>
</tr>
}

Ensure a datatable is ordered by a value

I have a complex algorithm which I am not going to explain here. The code pasted below is doing some processing for each row, but I need to ensure that the table is ordered by a field different than the Primary Key.
I need to do this in this code, not in SQL, or in stored procedures; it needs to be done in .net just before the foreach.
NO LINQ IS ALLOWED, ITS .NET 2.0
THX
Your help is appreciated.
List<int> distinctREFMDossierIds = GetREFMDossierIdsFromBookings();
foreach (int refmDossierId in distinctREFMDossierIds)
{
bool errorsFoundInDetails = false;
bool errorsFoundInHeaders = false;
wingsBookingInterfaceIdswithErrors.Clear();
dicRows.Clear();
sbWingsBookingInterfaceIds= new StringBuilder();
YBooking booking = new YBooking();
foreach (UC090_WingsIntegrationDataSet.WingsBookingInterfaceRow row in _uc090_WingsIntegrationDataSet.WingsBookingInterface.Rows)
{
//code
}
You can use LINQ:
foreach(var row in _uc090_WingsIntegrationDataSet.WingsBookingInterface
.OrderBy(r => r. Something))
You can sort a DataTable like this:
DataTable dt = new DataTable();
dt.DefaultView.Sort = <Sort expression>;
dt = dt.DefaultView.ToTable();
WingsBookingInterface.Rows.OrderBy(item => item.columnName);
You can sort a collection ( for example a List<> ) with the OrderBy extension method.

Categories

Resources