I have a question about assigning array values (double[]) into a column in a DataTable in C#. I derived double[] from a column in my DataTable as shown below.
double[] ToBeChanged = mydatatable.AsEnumerable().Select(s => s.Field<double>("column")).ToArray<double>();
After updating some values in ToBeChanged, I want to get it back to the original column quickly. How can I implement it as simple or as quick as possible?
One approach would be using .Zip method
var pairs = mydatatable.AsEnumerble().Zip(toBeChanged, (row, value) => (row, value));
foreach (var (row, value) in pairs)
{
row.SetField("column", value);
}
But I wouldn't advise to use it for production code, because correctness will fully depend on the assumption that rows and values ordered correctly.
Other possible approaches could be:
Update values within DataTable .
Pass DataRow instance to the method which will update values
Use dictionary to associate updatable values with row identification value.
Related
I have a datatable imported from a csv. What I'm trying to do is compare all of the rows to each other to find duplicates. In the case of duplicates I am going to add the row # to a list, then write the list to an array and deal with the duplicates after that.
//find duplicate rows and merge them.
foreach (DataRow dr in dt.Rows)
{
//loop again to compare rows
foreach (DataRow dx in dt.Rows)
{
if (dx[0]==dr[0] && dx[1]==dr[1] && dx[2] == dr[2] && dx[3] == dr[3] && dx[4] == dr[4] && dx[5] == dr[5] && dx[7] == dr[7])
{
dupeRows.Add(dx.ToString());
}
}
}
for testing I have added:
listBox1.Items.AddRange(dupeRows.ToArray());
which simply outputs System.Data.DataRow.
How do I store the duplicate row index ids?
The basic problem is that you saved a string describing the type of the row (what DataRow.ToString() returns by default) at the time you decided the row was a duplicate
Assuming you've read your CSV straight in with some library/driver rather than line by line (which would have been a good time to dedupe) let's use a dictionary to dedupe:
Dictionary<string, DataRow> d = new Dictionary<string, DataRow>();
foreach(var ro in dataTable.Rows){
//form a key for the dictionary
string key = string.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{7}", ro.ItemArray);
d[key] = ro;
}
That's it; at the end of this operation the d.Values will be a deduped collection of DataRow. 1000 rows will require 1000 operations so this will likely be orders of magnitude faster than comparing every row to every other row, which would need a million operations for a thousand rows
I've used tabs to separate the values when I formed the key - assuming your data contains no tabs. Best reliability will be achieved if you use a character that does not appear in the data
If you've read your CSV line by line and done a manual string split on comma (i.e. a primitive way of reading a CSV) you could do this operation then instead; after you split you have an array that can be used in place of ro.ItemArray. Process the entire file, creating rows (and adding to the dictionary) only if d.ContainsKey returns false. If the dictionary already contains that row, skip on rather than creating a row
The output (System.Data.DataRow) that you are seeing is expected since there is no custom implementation of DataRow.ToString() found in your project, framework is calling base class's (which is System.Object) ToString() for which the default implementation returns data type of object which invokes that method.
I see three solutions here:
If possible, try to read the DataTable into custom objects (like
MyDataTable, MyDataRow) so, you can create your own ToString() like
below:
public class MyDataRow
{
public override string ToString()
{
return "This is my custom data row formatted string";
}
}
in the for loop, when you found duplicated row, either just add
index/id (sort of primary key) of dx to array and then have another
for loop to retrieve dupes.
Third is same as mentioned by Caius Jard.
I'm new to c#, Linq and .NET. I have some code that does a query to get the last record from a table in a database. The field that I'm trying to access is an int but when I try to print it I get System.Data.Objects.ObjectQuery`1[System.Int32].
Here's the code:
public void getLastProgNumber()
{
using (var db = new IntranetEntities())
{
var value = (db.table.OrderByDescending(ai => ai.NumProgHWSW).GroupBy(a => a.NumProgHWSW).Select(g => Convert.ToInt32(g.FirstOrDefault())));
MessageBox.Show(value.ToString());
}
}
I need to convert it to an int type and return it if possible, thank you. (Right now I'm using void because I'm trying to get the right result before returning it)
If you want to get the last record from the database table, there are multiple ways. But doing GroupBy is certainly not one of them.
You can order the rows by doing OrderByDescending so that row with the maximum value of that column positioned at the first and then you can do FirstOrDefault.
var val = db.table.OrderByDescending(ai => ai.NumProgHWSW).FirstOrDefault();
// val is the row with maximum value of NumProgHWSW.
// you can display the value of NumProgHWSW in messagebox by doing following.
MessageBox.Show(val.NumProgHWSW);
If you want to get the Maximum value of NumProgHWSW in a variable directly from the LINQ query. you can do this by
var val = db.table.OrderByDescending(ai => ai.NumProgHWSW).FirstOrDefault().NumProgHWSW;
MessageBox.Show(val);
You can also use Max LINQ method to get the maximum value without doing OrderByDescending.
var val = db.table.Max(ai => ai.NumProgHWSW);
MessageBox.Show(val);
Using Max is a better approach then above two as it does not order the table rows before data retrieval and that way it works faster when the table has large number of rows.
'cannot implicitly convert type string to data row[]'.
Is it possible to store the string type to data row[]? I need to store the value of the particular column in that particular data row array. Suggest me an answer please.
DataRow[] drprocess = objds.Tables[0].Rows[i]["ProcessName"].ToString();
You have declared a variable of type DataRow[] called drProcess but have not yet created an array of DataRows in which to put any values. Instead you've tried to tell the compiler that the string you're retrieving is actually a DataRow, which it isn't.
It's possible that what you want to do is to create your array of DataRows, then create a DataRow object and assign it into the array. However, I'm suspicious that this isn't actually what you're trying to achieve. Note that objds.Tables[0].Rows is already a collection of DataRows. You can actually edit or use this collection yourself if you need.
Or if you're wanting to create a new collection of process names you might be better creating a var processes = new List<string>() then calling process.Add(objds.Tables[0].Rows[i]["ProcessName"].ToString()).
It all depends what you want to do with this collection of process names afterwards.
First, a DataRow always belongs to a DataTable. To which table should these new DataRow belong? I will presume objds.Tables[0].
I also assume that you have a string-column and you want to split every field in it to a DataRow[], then we need to know the delimiter.
Presuming it is a comma:
DataRow[] drprocess = objds.Tables[0].Rows[i].Field<string>("ProcessName").Split(',')
.Select(name => {
DataRow row = objds.Tables[0].NewRow();
row.SetField("ProcessName", name);
return row;
})
.ToArray();
I'm trying to pass an ArrayList into a DataRow object, the idea being to import data into a database from a CSV.
Previously in the file, a Dictionary<string,int> has been created, with the column name as the Key, and the position index as the corresponding value.
I was planning on using this to create a temporary DataTable for each record to aid importing into the DB. My original idea was something along the lines of:
private DataRow ArrayListToDataRow(ArrayList data, Dictionary<string,int> columnPositions)
{
DataTable dt = new DataTable();
DataColumn dc = new DataColumn();
for (i=0;i<=data.Count;i++)
{
dc.ColumnName = columnPositions.Keys[i];
dt.Columns.Add(dc);
dt.Columns[columnPositions.Keys[i]].SetOrdinal(columnPositions(columnPositions.Keys[i]);
}
//TODO Add data to row
}
But of course, the keys aren't indexable.
Does anybody have an idea on how this could be achieved?
Since the size of data should be the same as the size of your columnPositions, you could try using a foreach over your dictionary instead of a for loop.
If you want to access your dictionary values based on a sortable index, you would need to change it to
Dictionary<int, string>
Which seems to make more sense, as you seem to want to read them in that order.
If you cannot change the dictionary, you can do something like this
var orderedPositions = columnPositions.OrderBy(x => x.Value);
foreach(var position in orderedPositions)
{
// do your stuff using position.Key and position.Value
}
.OrderBy comes from Linq, so yuo will need to add
using System.Linq;
to your class.
By ordering the columnPositions on their value (the columnIndex) instead of the default (the order in which items were added), you can loop trough them in the order you presumably want (seeing as you were going with a for loop and every time trying to get the next columnPosition).
I have the following C# solution using generics for representing tabular data which has different datatypes in each column. The ultimate goal is to compare the datapoints of any two tables, which is why the columns have a constraint that they implement IComparable. My difficulty is in scaling it so that it can support any number of columns the user wishes:
class TableObject<RowHeaderType, Column1Type, Column2Type>
where Column1Type : IComparable
where Column2Type : IComparable
{
List<string> columnHeaders;
Dictionary<RowHeaderType, Dictionary<string, IComparable>> tableDict;
Dictionary<string, IComparable> rowDict;
public TableObject(List<string> _columnHeaders)
{
tableDict = new Dictionary<RowHeaderType, Dictionary<string, IComparable>>();
columnHeaders = _columnHeaders;
}
public void addRow(RowHeaderType rowHeader, Column1Type colvalue_1, Column2Type colvalue_2)
{
// rowDict represents one row, with each key/value representing a column of data
rowDict = new Dictionary<string, IComparable>();
rowDict.Add(columnHeaders[1], colvalue_1); // [0] was the row header table
rowDict.Add(columnHeaders[2], colvalue_2);
// add this new row to the master table
tableDict.Add(rowHeader, rowDict);
}
}
In the constructor I pass in a List which are the column labels. Then I would call addRows which would take (ideally) a rowHeaderType value, and an array which would represent the values which make up the entire row, each corresponding to the columnType defined.
My goal is make this as generic as possible. The only restriction I have here is that the column names are strings since I ultimately wish to display this in a datagridview.
If there is an alternate design which you feel is better, please feel free to suggest that as well. Thanks.
Do you know about DataSets, DataTables, DataColumns and DataRows?
You can even work with typed data sets. There's a lot of existing support for them.