Update and replace datatable rows value throughout all columns - c#

I have a one generic datatable which contains any number of columns and any number of rows, which i want to update at once through linq query if a row's column contains a quote (").
e.g.
A quick brown "fox" jumps over the lazy "dog"
I want this to be
A quick brown fox jumps over the lazy dog
Snapshot
I have tried
var rows = dt.AsEnumerable().Where(r => r.Field<string>("display").Contains("\""));
In this case column name is "display" is the column name, which I don't want to mention. Because this is not fixed as this quote (") could be in any column's row.
My problem is that I cannot find that specific columns for which I have tried this. It will give me all columns but not that specific columns.
string[] columnNames = dt.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToArray();
Any hints? I don't want this to be done with traditional foreach loop over columns and then on rows and replace text then.

You can do something like this:
//get columns which are of type "string" and then get their names
var columnNames = dt.Columns.OfType<DataColumn>().Where(c => c.DataType == typeof(string)).Select(c => c.ColumnName).ToList();
//get all dataTable's rows and with each column name (from previous step) get row's value of that column
// and if that value contains double qoutes, replace it with empty char :)
dt.AsEnumerable().ToList().ForEach(
r => columnNames.ForEach(c => r.SetField<string>(c, r.Field<string>(c).Replace("\"", ""))));
//or, as one-liner :)
dt.AsEnumerable().ToList()
.ForEach(r => dt.Columns.OfType<DataColumn>()
.Where(c => c.DataType == typeof(string))
.Select(c => c.ColumnName).ToList()
.ForEach(c => r.SetField<string>(c, r.Field<string>(c).Replace("\"", ""))));
I'm guessing that could be achieved a bit more simplier, but this can give you an idea :)

This is another way to get a list of all cells that contain a quote in your dataTable.
You can then do a ForEach on the result to get rid of the quotes.
var result = (from DataColumn column in table.Columns
let columnName = column.ColumnName
from DataRow row in table.AsEnumerable()
where row[columnName].ToString().Contains("\"")
select row[columnName]).ToList();

Related

How can I rearrange list indexing

I am working on google spreadsheets, user connect its spreadsheet, and the system pushed data to user connected spreadsheet at day end.
here are spreadsheet columns
The sequence of columns is
"PatientName","DOB","StripeId","Card","ChargeCreated","Status","Amount"
here is the code is used
var GoogleSpreadSheetData = db.GoogleSpreadSheetDatas
.ToList()
.Select(x => new List<string>{
x.PatientName.Trim(),
x.DOB.Trim(),
x.StripeId.Trim(),
x.Card.Trim(),
x.ChargeCreated.Trim(),
x.Status.Trim(),
x.Amount.Trim(),
})
.ToList();
the sequence of google spread sheet columns and query return is same sequence like
"PatientName","DOB","StripeId","Card","ChargeCreated","Status","Amount"
and data will mapped on exact column in spread sheet Reference image.
if user changes sequence of columns like this image, replaced PatientName column to DOB. when next time data push to spreadsheet PatientName data is pushed in the DOB column and vice versa.
Before pushing data to the spreadsheet, firstly I call google API(using Developer Metadata) to get a sequence of that spreadsheet, I got the updated column sequence in response.
My question is
How can I change the sequence on query select?
Can I rearrange the list on query select or after?
Please help to solve this problem.
Thank you.
Assuming your MetaData query provides you with a List<string> of column names:
List<string> spreadsheetColSeq;
Then you can query the database to retrieve the rows:
var dbData = db.GoogleSpreadSheetDatas
.Select(x => new List<string> {
x.PatientName.Trim(),
x.DOB.Trim(),
x.StripeId.Trim(),
x.Card.Trim(),
x.ChargeCreated.Trim(),
x.Status.Trim(),
x.Amount.Trim(),
})
.ToList();
And then you can create a mapping from the query order to the spreadsheet order and reorder the each row's List<string> to match the new order:
var origSeq = new[] { "PatientName", "DOB", "StripeId", "Card", "ChargeCreated", "Status", "Amount" };
var colOrder = spreadsheetColSeq
.Select(colName => Array.IndexOf(origSeq, colName))
.ToList();
var GoogleSpreadSheetData = dbData
.Select(row => colOrder.Select(idx => row[idx]).ToList())
.ToList();
Note: If columns may be deleted, this will crash since Array.IndexOf will return -1 for a missing value. You could filter colOrder by adding .Where(idx => idx > -1) before the ToList() to skip missing columns.

Searching for a value in a DataTable column returns empty when the value exists

My DataTable currently looks like this (name = dtRecipient):
Using the following LINQ statement, I am trying to find records who have a RecipientId value equal to e.g. "marcus":
var found = dtRecipients.AsEnumerable().Where(row => String.Equals(row.Field<string>("RecipientId"), "marcus"));
But no results are returned whereas, as you see in the picture, there is a record for "marcus". What gives?
No spaces or other invisible characters?
You can remove leading and trailing spaces (or new-line/tab-characters) with Trim:
var found = dtRecipients.AsEnumerable()
.Where(row => String.Equals((row.Field<string>("RecipientId") ?? "").Trim(), "marcus"));
If you want to remove all non-printable characters you can use Char.IsControl to detect them:
var found = dtRecipients.AsEnumerable()
.Where(row => String.Equals(
new String(
(row.Field<string>("RecipientId") ?? "").Trim()
.Where(c => !char.IsControl(c))
.ToArray()),
"marcus", StringComparison.OrdinalIgnoreCase));
I've also used StringComparison.OrdinalIgnoreCase to show how to ignore the case.

how to get a DataRow object from a datatable using Select function on an array in C#

I'm trying to use the select function filtered by a list as oposed to a value
if dt_old is a datatable, CFKEY is a column of dt_old, this statement uses Select for a specific Value and it work fine.
DataRow[] dt_oldDuplicateRow = dt_old.Select("CFKEY = '1'");
I can't find a way to use select to filter on an array or list based on an other datatable, I would like to do something like this.
DataColumn dc = dt_new.Columns["CFKEY"];
DataRow[] dt_oldDuplicateRow = dt_old.Select("CFKEY in " + dc );
where dt_new is the same format as st_old. any idea?
I am not sure if the list of keys are your choice or a list obtained from dt_new. Anyway,
List<string> listOfDuplicateKeys = createTheKeyList();
DataRow[] dt_oldDuplicateRow = dt_old.Rows.Cast<DataRow>()
.Where(dr => listOfDuplicateKeys.Contains(dr["CFKEY"].ToString()))
.ToArray();
You must refrence to System.Data.DataSetExtensions and use AsEnumerable() for datatable
var results = dt_old.AsEnumerable().Select()
check this question LINQ query on a DataTable
Thanks serdar, it's exactly what I needed.
My list was obtained from dt_new and I slightly changed your code.
var list = dt_new.Rows.OfType<DataRow>()
.Select(dr => dr.Field<string>("CFKEY"))
.ToList();
DataRow[] dt_oldDuplicateRow = dt_old.Rows.OfType<DataRow>()
.Where(dr => list.Contains(dr["CFKEY"].ToString()))
.ToArray();

DataTable - Dynamic Linq OrderBy using Lambda expressions

I'm getting a collection of records in a DataTable and binding it to a grid control. Before binding it I'm sorting the data based on few conditions. For brevity I'm will explain a test scenario.
I've two fields Category and Country. I want to first sort the records based on category and then by country. But the catch here is I want to push all the empty category values to the end and then sort based on the alphabetical order.
For that I'm doing -
var rows = dt.AsEnumerable()
.OrderBy(r => string.IsNullOrEmpty(Convert.ToString(r["Category"]))) //push empty values to bottom
.ThenBy(r => Convert.ToString(r["Category"]))
.ThenBy(r => Convert.ToString(r["Country"]))
But now, the fields based on which I need to sort, is dynamic which I'm having in an array.
How can I use the lambda expressions to order the records dynamically based on the fields? (pushing the empty values to the end)
I assume the array you're talking about is an array of strings.
var columns = new string[] { "Category", "Country" };
var rows = dt.AsEnumerable().OrderBy(x => 0);
foreach(var columnName in columns)
{
rows = rows.ThenBy(r => string.IsNullOrEmpty(Convert.ToString(r[category])))
.ThenBy(r => Convert.ToString(r[category]));
}
Because LINQ uses deferred execution, your query will not be evaluated until you actually need results. That's why you can construct it in multiple steps like in the example above.

C# LINQ Ignoring empty values in datatable

I have a datatable that I have grouped as follows:
var result = from data in view.AsEnumerable()
group data by new {Group = data.Field<string>("group_no")}
into grp
select new
{
Group = grp.Key.Group,
PRAS = grp.Average(c => Convert.ToDouble(c.Field<string>("pAKT Total")))
};
Now, the average function is also counting the empty cells in it's calculation. For example, there are 10 cells with only 5 populated with values. I want the average to be the sum of the 5 values divided by 5.
How can I ensure that it does what I want?
Thanks.
Maybe something like this:
PRAS = grp.Select(row => row.Field<string>("pAKT Total"))
.Where(s => !String.IsNullOrEmpty(s))
.Select(Convert.ToDouble)
.Average()
To my knowledge, that's not possible with the Average method.
You can however achieve the result you want to, with the following substitute:
PRAS = grp.Sum(c => Convert.ToDouble(c.Field<string>("pAKT Total"))) / grp.Count(c => !c.IsDBNull)
This only makes sense, when you want to select the "empty" rows in the group, but just don't want to include them in your average. If you don't need the "empty" rows at all, don't select them in the first place, i.e. add a where clause that excludes them.

Categories