I want to be able remove DataRows in a DataTable that contain null values for arrays containing no numeric data.
Trying to modify the code below which transposes the DataTable for use by Google Visualisation API:
private DataTable TransposeOpiate(DataTable inputTable)
{
DataTable outputTable = new DataTable();
// Add columns by looping rows
// Header row's first column is same as in inputTable
outputTable.Columns.Add(inputTable.Columns[0].ColumnName.ToString());
// Header row's second column onwards, 'inputTable's first column taken
foreach (DataRow inRow in inputTable.Rows)
{
string newColName = inRow[0].ToString();
outputTable.Columns.Add(newColName);
}
// Add rows by looping columns
for (int rCount = 1; rCount <= inputTable.Columns.Count - 1; rCount++)
{
DataRow newRow = outputTable.NewRow();
// First column is inputTable's Header row's second column
newRow[0] = inputTable.Columns[rCount].ColumnName.ToString();
for (int cCount = 0; cCount <= inputTable.Rows.Count - 1; cCount++)
{
string colValue = inputTable.Rows[cCount][rCount].ToString();
newRow[cCount + 1] = colValue;
}
outputTable.Rows.Add(newRow);
}
return outputTable;
}
For example in the intellisense output shown below the row for improved, where the values after contain no data (this would normally be numeric values) instead contains empty indexes:
I need to be able to delete these rows if they are in the above format, how can I modify my code to perform this?
Why not set a condition which prevents adding the row if all the row cells have a null value?
if (newRow.ItemArray.Any(x => x != null))
{
outputTable.Rows.Add(newRow);
}
If you need to check for columns as well, do this right before returning outputTable
for (int col = outputTable.Columns.Count - 1; col >= 0; col--)
{
bool toDelete = true;
for (int row = 0; row < outputTable.Rows.Count; row++)
{
if (outputTable.Rows[row][col] != null)
{
toDelete = false;
}
}
if (toDelete)
{
outputTable.Columns.RemoveAt(col);
}
}
You can try with something like this (comments are in code):
private void CheckColumns()
{
//table which we want to check
DataTable table = new DataTable();
//add column definition - first column will be string, other two are int columns
table.Columns.Add("string column", typeof(string));
table.Columns.Add("int column 1", typeof(int));
table.Columns.Add("int column 2", typeof(int));
//add data - in this example rows "abc" and "ghi" are valid because they have at least one numeric column
table.Rows.Add(new object[] { "abc", 1, 2 });
table.Rows.Add(new object[] { "def", null, null });
table.Rows.Add(new object[] { "ghi", null, 2 });
table.Rows.Add(new object[] { "jkl", null, null });
//filter rows in a way, using Linq, that rows are filtered where at least one column has numeric value
var validRows = table.AsEnumerable().Where(r => r.ItemArray.Any(c => IsNumeric(c))).ToList();
}
//this is helper method that code will call for each value in each row
private bool IsNumeric(object value)
{
int outputValue;
return int.TryParse(value.ToString(), out outputValue);
}
If you'll have decimal values, then you should une decimal.TyrParse in IsNumeric method.
If you need to be sure that all columns except first one have numeric values, you can do it like this (skipping first value from each row and checking if all other values are numeric)...
var validRows = table.AsEnumerable().Where(r => r.ItemArray.Skip(1).All(c => IsNumeric(c))).ToList();
once you have those 'valid' rows, you can write that data into another table or do whatever you need to do with those data...
Instead of .ToList() you can use .CopyToDataTable() method so you'll get new DataTable with those 'valid' rows. For example:
DataTable newDataTable = table.AsEnumerable().Where(r => r.ItemArray.Skip(1).All(c => IsNumeric(c))).CopyToDataTable();
Related
I am a new C# developer and I am struggling now with converting the columns in the following DataTable into rows. The current DataTable structure I have is:
Id Value
1 Test#1
1 Test#2
2 Car#1
2 Car#2
3 Airplane#1
3 Airplane#2
I need to convert it into the following table structure:
1 2 3
Test#1 Car#1 Airplane#1
Test#2 Car#2 Airplane#2
I tried to implement it using the following method I found in Google, but I am still not getting the desired result:
private DataTable GenerateTransposedTable(DataTable inputTable)
{
DataTable outputTable = new DataTable();
// Add columns by looping rows
// Header row's first column is same as in inputTable
outputTable.Columns.Add(inputTable.Columns[0].ColumnName.ToString());
// Header row's second column onwards, 'inputTable's first column taken
foreach (DataRow inRow in inputTable.Rows)
{
string newColName = inRow[0].ToString();
outputTable.Columns.Add(newColName);
}
// Add rows by looping columns
for (int rCount = 1; rCount <= inputTable.Columns.Count - 1; rCount++)
{
DataRow newRow = outputTable.NewRow();
// First column is inputTable's Header row's second column
newRow[0] = inputTable.Columns[rCount].ColumnName.ToString();
for (int cCount = 0; cCount <= inputTable.Rows.Count - 1; cCount++)
{
string colValue = inputTable.Rows[cCount][rCount].ToString();
newRow[cCount + 1] = colValue;
}
outputTable.Rows.Add(newRow);
}
return outputTable;
}
Could you please tell me how I can transpose the first DataTable into the second one?
Using an extension method, you can pivot the original DataTable by creating the implied row number column that groups the answer rows together:
public static class DataTableExt {
// Transpose a DataTable to a new DataTable
// over field creates new column names
// value field is value for new columns
// Original datatable must be sorted in OverColFieldName then row number order
public static DataTable Transpose(this DataTable dt, string OverColFieldName, string WithValueFieldName) {
var res = new DataTable();
if (dt.Rows.Count > 0) {
var ColCount = dt.AsEnumerable().Select(r => r.Field<int>(OverColFieldName)).Distinct().Count();
var rowCount = dt.Rows.Count / ColCount;
var rowNumbers = Enumerable.Range(0, rowCount*ColCount).Select(rn => rn % rowCount +1);
var rowGroups = dt.AsEnumerable()
.Zip(rowNumbers, (r, rn) => new { Row = r, RowNum = rn }) // associate an answer Row Number with each row
.GroupBy(rrn => rrn.RowNum, rrn => new { Over = rrn.Row[OverColFieldName].ToString(), With = rrn.Row[WithValueFieldName] }); // group the columns for each answer row together
var valueDataType = dt.Columns[WithValueFieldName].DataType;
var colNames = rowGroups.SelectMany(rg => rg.Select(r => r.Over)).Distinct().OrderBy(n => n);
foreach (var n in colNames)
res.Columns.Add(n, valueDataType);
foreach (var rowGroup in rowGroups) {
var newr = res.NewRow();
foreach (var r in rowGroup)
newr[r.Over] = r.With;
res.Rows.Add(newr);
}
}
return res;
}
}
And you can use it by passing in the Column Names for the source of the new columns and the values to be placed in each column:
var ans = dt.Transpose("Id", "Value");
Need an easy method to sum row values(int columns) when a string column value has duplicates in a datatable. And need to remove rows having duplicate values. Below is a datatable for example.
Below table have duplicate "AAA" values, so need to sum Int_Col3,Col4,Col5,Col6,Col7. Note that the columns with Int are integer columns.
Expected Datatable:
Here is a simple method to achieve what you want, in-place. (The same data table is converted to the expected state)
private static void CombineDuplicatesInPlace(DataTable dt)
{
Dictionary<string, DataRow> cachedRows = new Dictionary<string, DataRow>();
for (int i = 0; i < dt.Rows.Count; i++)
{
DataRow dataRow = dt.Rows[i];
string key = Convert.ToString(dataRow["String_Col1"]);
if (cachedRows.ContainsKey(key))
{
DataRow existingRow = cachedRows[key];
foreach (DataColumn dc in dt.Columns)
if (dc.DataType == typeof(int))
existingRow[dc] = (int)existingRow[dc] + (int)dataRow[dc];
dt.Rows.Remove(dataRow);
i--;
}
else { cachedRows[key] = dataRow; }
}
}
i have a data table having several rows. I want to increment a variable say i, only if it is the first row of the data table. How can I do that?
foreach (DataRow row in dt.Rows)
{
address = (row["address"].ToString());
Counter += 1;
int count = Convert.ToInt16(Dt.Rows[i]["limit"]);
//for the first row
if (Counter == count)
{
i += 1;
}
// also if it is the last row
exit;
You should work with a for loop - hat makes the index access easier
for (int i = 0; i < dt.Rows.Count; i++)
{
string address = dt.Rows[i].Field<string>("address");
if (i == 0)
{
//first row, increment here
}
if (i == dt.Rows.Count - 1)
{
//last row
}
}
Why not a simple flag, say isFirstRow?
bool isFirstRow = true;
foreach (DataRow row in dt.Rows)
{
if (isFirstRow)
{
isFirstRow = false;
//TODO: Increment here
}
...
Edit: In case of last row we can do postprocessing:
DataRow lastRow = null;
foreach (DataRow row in dt.Rows) {
// Candidate for the last row
lastRow = row;
...
}
if (lastRow != null) {
//TODO: Put relevant code for the last row
}
You can use code like below. Use IndexOf method of DataRowCollection class to determine if it's the first row. I have created a sample DataTable object before the loop. The main part of this answer is the if statement within the loop's body that checks for row index.
Keep in mind that the index of row in DataTable starts from 0 and not 1. So, first row has an index of 0, second row has an index of 1, third row row has index of 2 and so on.
DataTable dt = new DataTable();
dt.Columns.Add("EmployeeId", typeof(int));
dt.Columns.Add("FirstName", typeof(string));
dt.Columns.Add("LastName", typeof(string));
dt.Columns.Add("Grade", typeof(int));
// Here we add five DataRows.
dt.Rows.Add(11, "John", "Smith", 9);
dt.Rows.Add(92, "Sunita", "Mali", 7);
dt.Rows.Add(2, "Anil", "Kumar", 4);
dt.Rows.Add(5, "Mike", "Reb", 11);
dt.Rows.Add(1, "Sunil", "Dev", 12);
//your counter variable
int i= 0;
foreach(DataRow row in dt.Rows) {
//use IndexOf method to check if it's the first row
if( dt.Rows.IndexOf(row) == 0) {
i++;//only first row causes increment of i
}
if(dt.Rows.IndexOf(row) == (dt.Rows.Count -1)) {
//its the last row of datatable
}
}
Can't seem to set indentation level of a row to 1. AddRows results in an InvalidRowLocation exception.
static string[] DeserializeJson(string json)
{
try
{
SortedDictionary<string, string> values = JsonConvert.DeserializeObject<SortedDictionary<string, string>>(json);
return new string[] { values["Status"], values["ServerIP"], values["ClientIP"], values["Date"] };
}
catch
{
return new string[] { json };
}
}
static Row MakeRow(Sheet sheet, string[] values)
{
List<Cell> cells = new List<Cell>();
Cell cell;
IEnumerator<Column> cols = sheet.Columns.GetEnumerator();
DateTime date;
for (int n = 0; n < values.Length; n++)
{
cols.MoveNext();
Column col = cols.Current;
if (col.Type == ColumnType.DATE)
{
date = DateTime.ParseExact(values[n], dateFormat, CultureInfo.InvariantCulture);
cell = new Cell.AddCellBuilder(col.Id, DateTime.Now).Build();
}
else
cell = new Cell.AddCellBuilder(col.Id, values[n]).Build();
cells.Add(cell);
}
Row row = new Row.AddRowBuilder(null, true, null, null, null).SetCells(cells).Build();
if (values.Length > 1)
row.Indent = 1; // Results in an exception
return row;
}
static void AddRows(SmartsheetClient client, Sheet sheet, string[] lines)
{
List<Row> rows = new List<Row>();
foreach (string line in lines)
rows.Add(MakeRow(sheet, DeserializeJson(line)));
client.SheetResources.RowResources.AddRows(sheetId, rows);
}
Indenting in Smartsheet creates a "parent-child" relationship between the rows. So, to indent a new row you'll need to give it the rowId of its parent row as the parentId.
It would look something like this
Row row = new Row.AddRowBuilder(null, true, 7531436244775314, null, null).SetCells(cells).Build();
You can only use the Indent property when updating existing rows. Since you are adding new rows, you need to set the ParentId property instead. See docs: http://smartsheet-platform.github.io/api-docs/#row-location
I'm having some problem while trying to set column value.
I'v had a dataTable which get some values from SQL and then im adding two new columns by :
dataTable.Columns.Add("dest", typeof(int));
dataTable.Columns.Add("amount", typeof(int));
Which works great but now i want to put 0 in every row in column name dest - and later user will edit this, and then i want to set amount value as
amount = all(this column is in dataTable before I add these 2 columns) + dest;
int columnNumber = 5; //Put your column X number here
for (int i = 0; i < yourDataTable.Rows.Count; i++)
{
yourDataTable.Rows[i][columnNumber] = "0";
}
You can use foreach too.
foreach (DataRow row in myDataTable.Rows)
//if (row["X"] has condition) // or if any condition
row["colName"] = row[colIndex] = "abc";