Eliminate comma(,) from a column of a Data Table using LINQ - c#

I have a DataTable as shown below:
After using below LINQ Expression on above DT:
if (dt.AsEnumerable().All(row => string.IsNullOrEmpty(row.Field<string>("SameReferences"))))
BindOldReferences(dt);
else
{
var grps = from row in dt.AsEnumerable()
let RefID = row.Field<string>("ReferenceID")
let RefDescription = row.Field<string>("ReferenceDescription")
let ReferenceUrl = row.Field<string>("ReferenceUrl")
let SortOrder = row.Field<int>("sortOrder")
group row by new { RefDescription, ReferenceUrl, SortOrder } into groups
select groups;
dt = grps.Select(g =>
{
DataRow first = g.First();
if (first.Field<string>("SameReferences") != null)
{
string duplicate = first.Field<int>("SortOrder").ToString();
first.SetField("SameReferences", string.Format("{0},{1}", duplicate, first.Field<string>("SameReferences")));
}
return first;
}).CopyToDataTable();
}
After applying above LINQ to DT it becomes :
Expected DT as below : eliminate (,) comma when there is single value in column Samereferences. So what changes i have to make to LINQ to get the expected below output.
Please help..!

You can use String.Trim method like this:-
first.SetField("SameReferences", string.Format("{0},{1}", duplicate,
first.Field<string>("SameReferences")).Trim(','));
It will remove all the trailing comma.

Try this:
if (first.Field<string>("SameReferences") != null)
{
string duplicate = first.Field<int>("SortOrder").ToString();
string sameReference = first.Field<string>("SameReferences");
if (String.IsNullOrEmpty(sameReference))
first.SetField("SameReferences", duplicate);
else
first.SetField("SameReferences", string.Format("{0},{1}", duplicate, sameReference));
}

Related

LINQ Dynamic Query with group by and string variable

I want to create a dynamic query with LINQ-to-SQL and everything works except for one thing: group by. I looked at the other questions but haven't found a reason so far why it does not work.
I use using System.Linq.Dynamic; with the following query (that works):
int numberOfRankedItems = 3;
string minMaxChoice = "max";
string orderBy = "";
if (minMaxChoice == "max")
{
orderBy = "queryGroup.Sum(row => row.POSITIONVALUE) descending";
} else if (minMaxChoice == "min")
{
orderBy = "queryGroup.Sum(row => row.POSITIONVALUE) ascending";
}
string minMaxFeatureColumnName = "BRANDNAME";
string selectMinMaxFeature = "new { minMaxFeature = queryGroup.Key." + minMaxFeatureColumnName + ", sumPosition = queryGroup.Sum(row => row.POSITIONVALUE)}";
var query = (from tableRow in Context.xxx
where /* some filters */
group tableRow by tableRow.BRANDNAME into queryGroup
orderby orderBy
select selectMinMaxFeature).Take(numberOfRankedItems).ToList();
The use of variables for orderby and select works fine, but for group by not no matter what I try to replace with a variable. The query actually works if I e.g. use a variable instead of tableRow.BRANDNAME but the query returns the wrong result. The goal is to be able to set the column for the grouping dynamically.
Ideas?
Thanks
Edit: there seem to be multiple issues also with the other variables. So I generalize the question a bit: how can I generalize the following query in terms of
The column to group by
The column to order by + ASC or DESC
In the select statement the columnname of the first statement (BRANDNAME)
Here is the query:
var query = (from tableRow in ctx.xxx
where /* filter */
group tableRow by new { tableRow.BRANDNAME } into queryGroup
orderby queryGroup.Sum(row => row.POSITIONVALUE) descending
select new { minMaxFeature = queryGroup.Key.BRANDNAME, sumPosition = queryGroup.Sum(row => row.POSITIONVALUE) }).Take(numberOfRankedItems).ToList();
Would be great without expression trees ;)
I have now elaborated a solution to the question:
The solution uses as before using System.Linq.Dynamic; allows now to set certain variables. In the end it returns a dictionary.
// set variables
int numberOfRankedItems;
if (queryData.NumberOfRankedItems != null)
{
numberOfRankedItems = (int) queryData.NumberOfRankedItems;
} else
{
numberOfRankedItems = 0;
}
string minMaxChoice = queryData.MinMaxChoice;
string minMaxFeatureColumnName = queryData.MinMaxFeatureColumnName;
string orderByPrompt = "";
if (minMaxChoice == "max")
{
orderByPrompt = "it.Sum(POSITIONVALUE) descending";
} else if (minMaxChoice == "min")
{
orderByPrompt = "it.Sum(POSITIONVALUE) ascending";
}
string groupByPrompt = queryData.MinMaxFeatureColumnName;
// execute query
IQueryable query = (from tableRow in Context.xxx
where /* some filters */
select tableRow).
GroupBy(groupByPrompt, "it").
OrderBy(orderByPrompt).
Select("new (it.Key as minMaxFeature, it.Sum(POSITIONVALUE) as sumPosition)").
Take(numberOfRankedItems);
// create response dictionary
Dictionary<int?, Dictionary<string, int?>> response = new Dictionary<int?, Dictionary<string, int?>>();
int count = 1;
foreach (dynamic item in query)
{
string minMaxFeatureReturn = item.GetType().GetProperty("minMaxFeature").GetValue(item);
int? sumPositionReturn = item.GetType().GetProperty("sumPosition").GetValue(item);
response[count] = new Dictionary<string, int?>() { { minMaxFeatureReturn, sumPositionReturn } };
count++;
}
return response;

Use linq to find DataTable(Name) in a DataSet using unique list of Column Names

I got roped into some old code, that uses loose (untyped) datasets all over the place.
I'm trying to write a helper method to find the DataTable.Name using the names of some columns.....(because the original code has checks for "sometimes we have 2 datatables in a dataset, sometimes 3, sometimes 4)..and its hard to know the order. Basically, the TSQL Select statements conditionally run. (Gaaaaaaaaaaaaaahhh).
Anyway. I wrote the below, and if I give it 2 column names, its matching on "any" columnname, not "all column names".
Its probably my linq skillz (again), and probably a simple fix.
But I've tried to get the syntax sugar down..below is one of the things I wrote, that compiles.
private static void DataTableFindStuff()
{
DataSet ds = new DataSet();
DataTable dt1 = new DataTable("TableOne");
dt1.Columns.Add("Table1Column11");
dt1.Columns.Add("Name");
dt1.Columns.Add("Age");
dt1.Columns.Add("Height");
DataRow row1a = dt1.NewRow();
row1a["Table1Column11"] = "Table1Column11_ValueA";
row1a["Name"] = "Table1_Name_NameA";
row1a["Age"] = "AgeA";
row1a["Height"] = "HeightA";
dt1.Rows.Add(row1a);
DataRow row1b = dt1.NewRow();
row1b["Table1Column11"] = "Table1Column11_ValueB";
row1b["Name"] = "Table1_Name_NameB";
row1b["Age"] = "AgeB";
row1b["Height"] = "HeightB";
dt1.Rows.Add(row1b);
ds.Tables.Add(dt1);
DataTable dt2 = new DataTable("TableTwo");
dt2.Columns.Add("Table2Column21");
dt2.Columns.Add("Name");
dt2.Columns.Add("BirthCity");
dt2.Columns.Add("BirthState");
DataRow row2a = dt2.NewRow();
row2a["Table2Column21"] = "Table2Column1_ValueG";
row2a["Name"] = "Table2_Name_NameG";
row2a["BirthCity"] = "BirthCityA";
row2a["BirthState"] = "BirthStateA";
dt2.Rows.Add(row2a);
DataRow row2b = dt2.NewRow();
row2b["Table2Column21"] = "Table2Column1_ValueH";
row2b["Name"] = "Table2_Name_NameH";
row2b["BirthCity"] = "BirthCityB";
row2b["BirthState"] = "BirthStateB";
dt2.Rows.Add(row2b);
ds.Tables.Add(dt2);
DataTable dt3 = new DataTable("TableThree");
dt3.Columns.Add("Table3Column31");
dt3.Columns.Add("Name");
dt3.Columns.Add("Price");
dt3.Columns.Add("QuantityOnHand");
DataRow row3a = dt3.NewRow();
row3a["Table3Column31"] = "Table3Column31_ValueM";
row3a["Name"] = "Table3_Name_Name00M";
row3a["Price"] = "PriceA";
row3a["QuantityOnHand"] = "QuantityOnHandA";
dt3.Rows.Add(row3a);
DataRow row3b = dt3.NewRow();
row3b["Table3Column31"] = "Table3Column31_ValueN";
row3b["Name"] = "Table3_Name_Name00N";
row3b["Price"] = "PriceB";
row3b["QuantityOnHand"] = "QuantityOnHandB";
dt3.Rows.Add(row3b);
ds.Tables.Add(dt3);
string foundDataTable1Name = FindDataTableName(ds, new List<string> { "Table1Column11", "Name" });
/* foundDataTable1Name should be 'TableOne' */
string foundDataTable2Name = FindDataTableName(ds, new List<string> { "Table2Column21", "Name" });
/* foundDataTable1Name should be 'TableTwo' */
string foundDataTable3Name = FindDataTableName(ds, new List<string> { "Table3Column31", "Name" });
/* foundDataTable1Name should be 'TableThree' */
string foundDataTableThrowsExceptionName = FindDataTableName(ds, new List<string> { "Name" });
/* show throw exception as 'Name' is in multiple (distinct) tables */
}
public static string FindDataTableName(DataSet ds, List<string> columnNames)
{
string returnValue = string.Empty;
DataTable foundDataTable = FindDataTable(ds, columnNames);
if (null != foundDataTable)
{
returnValue = foundDataTable.TableName;
}
return returnValue;
}
public static DataTable FindDataTable(DataSet ds, List<string> columnNames)
{
DataTable returnItem = null;
if (null == ds || null == columnNames)
{
return null;
}
List<DataTable> tables =
ds.Tables
.Cast<DataTable>()
.SelectMany
(t => t.Columns.Cast<DataColumn>()
.Where(c => columnNames.Contains(c.ColumnName))
)
.Select(c => c.Table).Distinct().ToList();
if (null != tables)
{
if (tables.Count <= 1)
{
returnItem = tables.FirstOrDefault();
}
else
{
throw new IndexOutOfRangeException(string.Format("FindDataTable found more than one matching Table based on the input column names. ({0})", String.Join(", ", columnNames.ToArray())));
}
}
return returnItem;
}
I tried this too (to no avail) (always has 0 matches)
List<DataTable> tables =
ds.Tables
.Cast<DataTable>()
.Where
(t => t.Columns.Cast<DataColumn>()
.All(c => columnNames.Contains(c.ColumnName))
)
.Distinct().ToList();
To me sounds like you're trying to see if columnNames passed to the method are contained within Column's name collection of Table. If that's the case, this should do the work.
List<DataTable> tables =
ds.Tables
.Cast<DataTable>()
.Where(dt => !columnNames.Except(dt.Columns.Select(c => c.Name)).Any())
.ToList();
(Below is an append by the asker of the question)
Well, I had to tweak it to make it compile, but you got me there..
Thanks.
Final Answer:
List<DataTable> tables =
ds.Tables.Cast<DataTable>()
.Where
(dt => !columnNames.Except(dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName))
.Any()
)
.ToList();
Final Answer (which is not case sensitive):
List<DataTable> tables =
ds.Tables.Cast<DataTable>()
.Where
(dt => !columnNames.Except(dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName), StringComparer.OrdinalIgnoreCase)
.Any()
)
.ToList();

Performing Select Operation in Linq Using IN Clause/

I have a string with Names seperated by comma and I need to perform Select Operation Using In clause for the string I have..
here is my Code
protected void btnSubmitt_Click(object sender, EventArgs e)
{
try
{
List<string> ids = new List<string>();
for (int i = 0; i < RadListBox2.Items.Count; i++)
{
ids.Add(RadListBox2.Items[i].Text);
}
string result = string.Join(",", ids);
//result I have Data Something like This
result=name1,name2
var ProductAttributeRowId = string.Join(",", from m in dt.AsEnumerable() where m.Field<string>("ProductAttributeName") in result select m.Field<long>("ProductAttributeRowId"));
string json = "{'ProductRowId':" + hdnId.Value+ ",'ProductAttributeRowId':'" + ProductAttributeRowId +"'}";
statuslabel.Text = ClsUtility.HttpPost(url + "Services/Product.svc/ProductAttributesID", json);
}
catch (Exception Err)
{
}
BindGrid();
}
it shows the error when I used 'in' Linq Query pls help me
Why do you concatenate the ID's with comma if you need to split them two lines behind?
Use Contains with the original list, i guess this is what you want:
var ProductAttributeRowId = dt.AsEnumerable()
.Where(row => ids.Contains(row.Field<string>("ProductAttributeName")))
.Select(row => row .Field<long>("ProductAttributeRowId"));
To perform a IN query, use the following syntax:
var array = new List<String>(...);
var contains = (from elm in collection where array.Contains(elm)).Any();
That is, you invert the logic: a collection containing values contains your iterator variable.
var ProductAttributeRowId =string.Join("," ,dt.AsEnumerable()
.Where(row => ids.Contains(row.Field<string>("ProductAttributeName")))
.Select(row => row.Field<long>("ProductAttributeRowId")));

Compare two DataRows excluding some columns

I have two DataTables, e.g. OriginalEntity and Entity.
Interface application modifies Entity dataTable's rows. While saving I want to check DataRows that is modified or different from OrigianlEntity.
But, also I need to exclude few fields while comparing e.g. modified date and other audit fields.
Currently I am looping through each rows of datatable, like this:
List<string> auditFields = new List<string>(){"createdon","modifiedon"};
string IdentityKeyName = "id";
object ent,orgEnt;
foreach(string columnName in columnList) // ColumnList is List of columns available in datatable
{
foreach(DataRow dr in Entity.Rows)
{
ent = dr[columnName];
orgEnt = OriginalEntity.Select(IdentityKeyName + " = " + dr[IdentityKeyName].ToString())[0][columnName];
if(!ent.Equals(orgEnt) && !auditFields.Contains(columnName))
{
isModified = true;
break;
}
}
}
I just want an efficient way to achieve above. Please suggest.
Thanks everyone for your suggestion, and this is my (as I don't have primary key defined)
Solution:
public bool isModified(DataTable dt1, DataTable dt2, string IdentityKeyName)
{
bool isModified = false;
List<string> auditFields = new List<string>() { "createdon", "modifiedon" };
isModified = isModified || (dt1.Rows.Count != dt2.Rows.Count);
if(!isModified)
{
//Approach takes 150 ms to compare two datatable of 10000 rows and 24 columns each
DataTable copyOriginalEntity = dt1.Copy();
DataTable copyEntity = dt2.Copy();
//Exclude field you don't want in your comparison -- It was my main task
foreach(string column in auditFields)
{
copyOriginalEntity.Columns.Remove(column);
copyEntity.Columns.Remove(column);
}
for(int i=0;i<copyOriginalEntity.Rows.Count;i++)
{
var origItems = copyOriginalEntity.Rows[i].ItemArray;
var entityItem = copyEntity.Select(IdentityKeyName + " = " + copyOriginalEntity.Rows[i][dentityKeyName].ToString())[0].ItemArray;
if(string.Concat(origItems) != string.Concat(entityItem)){ isModified = true; break; }
}
}
return isModified;
}
You are going to have to loop though the columns to compare. This compare ent.Equals(orgEnt) in your code is comparing if the object references are the same. This doesn't seem like what you want and you want to compare values.
public bool IsChanged(DataTable original, DataTable source, string idKeyName, params string[] ignoreColumns)
{
// make sure "key" column exist in both
if (!original.Columns.Contains(idKeyName) || !source.Columns.Contains(idKeyName))
{
throw new MissingPrimaryKeyException("Primary key column not found.");
}
// if source rows are not the same as original then something was deleted or added
if (source.Rows.Count != original.Rows.Count)
{
return false;
}
// Get a list of columns ignoring passed in and key (key will have to be equal to find)
var originalColumns =
original.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName)
.Where(n => !ignoreColumns.Contains(n) && n != idKeyName)
.ToArray();
// check to make sure same column count otherwise just fail no need to check
var sourceColumnsCount =
source.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName).Count(originalColumns.Contains);
if (originalColumns.Length != sourceColumnsCount)
{
return false;
}
//Switch to linq
var sourceRows = source.AsEnumerable();
return sourceRows.All(sourceRow =>
{
// use select since not real key
var originalCheck = original.Select(idKeyName + " = " + sourceRow[idKeyName]);
if (originalCheck.Length != 1)
{
// Couldn't find key or multiple matches
return false;
}
var originalRow = originalCheck.First();
//Since using same array we can use linq's SequenceEqual to compare for us
return
originalColumns.Select(oc => sourceRow[oc])
.SequenceEqual(originalColumns.Select(oc => originalRow[oc]));
});
}
There might be some micro optimizations but I think no matter what you will have to check each column.

DataTable.Select string function in where clause

I'm having problems with a DataTable.Select() where the matching values might contain leading spaces and need to be trimmed correctly to return the correct amount of records.
Currently my code is returning less records as the matching fails because of unwanted characters.
How do you handle DataTable.Select as the example SQL below suggests?
SELECT * FROM Table WHERE LTRIM(FullName) = ' Joe Smith'
I' tried
dataTable.Select("LTRIM(FullName) = ' Joe Smith'");
but it failed.
Any ideas?
I would suggest to use Linq-To-DataSet instead, it makes it a lot clearer and easier to maintain:
var rows = from row in dataTable.AsEnumerable()
where row.Field<string>("FullName").Trim() == "Joe Smith"
select row;
If you want to use LTRIM instead, you just have to replace Trim with TrimStart.
if you want an array or list, use ToArray or ToList, e.g.
DataRow[] rowsArray = rows.ToArray();
or a DataTable
dataTable = rows.CopyToDataTable();
Edit: if you insist on using DataTable.Select or you can't use linq, this should work(LTRIM is not supported):
rowsArray = dataTable.Select("TRIM(FullName) = 'Joe Smith'");
Give this a try:
string searchTerm = " Joe Smith";
string expression = String.Format("TRIM(FullName) = '{0}'", searchTerm.Trim());
dataTable.Select(expression);
DataTable excelData = objGetExcelData.DataExcel(objEntities.StrFilePath, ConfigSettings.GetAppConfigValue("select * from sheet1"));
StringBuilder strInput = new StringBuilder();
DataView view = new DataView(excelData);
DataTable distinctValues = view.ToTable(true, "GROUP_NAME");
if (distinctValues.Rows.Count > 0)
{
for (int i = 0; i < distinctValues.Rows.Count; i++)
{
strGroupName = Convert.ToString(distinctValues.Rows[i]["GROUP_NAME"]);
foreach (DataRow item in excelData.Select("GROUP_NAME = '" + strGroupName + "'"))
{
strInput.Append(Convert.ToString(item[0]));
strInput.Append("~");
strInput.Append(Convert.ToString(item[1]));
strInput.Append(",");
strDasID = Convert.ToString(item[0]);
}
}
}

Categories

Resources