I need to replace integers values inside a datatable with a string symbol (*) if they are greater than 0 and less than 5.
So far I can loop through each row and the corresponding columns but I am unable to get the individual values contained in the datatable.
The code I have written so far is shown below:
public static DataTable SupressDataTable(DataTable cases)
{
DataTable suppressedDataTable = new DataTable();
foreach (var row in cases.Rows)
{
foreach (DataColumn column in cases.Columns)
{
if (column.IsNumeric())
{
}
}
}
return suppressedDataTable;
}
public static bool IsNumeric(this DataColumn col)
{
if (col == null)
return false;
// Make this const
var numericTypes = new[] { typeof(Byte), typeof(Decimal), typeof(Double),
typeof(Int16), typeof(Int32), typeof(Int64), typeof(SByte),
typeof(Single), typeof(UInt16), typeof(UInt32), typeof(UInt64)};
return ((IList) numericTypes).Contains(col.DataType);
}
How do I get to the values and then replace them?
You can't do that with the original table because an int or double column is not a string column. You need a new table where each numeric column is replaced with a string column:
public static DataTable SupressDataTable(DataTable cases)
{
DataTable suppressedDataTable = cases.Copy(); // Same columns, same data
DataColumn[] allNumericColumns = suppressedDataTable.Columns.Cast<DataColumn>().Where(IsNumeric).ToArray();
foreach (DataColumn numericCol in allNumericColumns)
{
int ordinal = numericCol.Ordinal; // need to store before remove
suppressedDataTable.Columns.Remove(numericCol);
suppressedDataTable.Columns.Add(numericCol.ColumnName); // string column
suppressedDataTable.Columns[numericCol.ColumnName].SetOrdinal(ordinal);
}
for (int index = 0; index < suppressedDataTable.Rows.Count; index++)
{
DataRow row = suppressedDataTable.Rows[index];
foreach (DataColumn column in cases.Columns)
{
if (IsNumeric(column))
{
dynamic numVal = cases.Rows[index][column];
string newValue = numVal > 0 && numVal < 5 ? "*" : numVal.ToString();
row.SetField(column.Ordinal, newValue);
}
}
}
return suppressedDataTable;
}
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 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();
I have searched several threads and cannot seem to find a C# solution. I have a string list and a datatable in C#. I need to be able to iterate through my data table's rows, then iterate through my list to obtain a header value (row[0]) and use that to assign to a variable.
Here is what I have so far:
private void setInformation(DataTable dt, List<string> text)
{
foreach(DataRow row in dt.Rows)
{
if (dt.Rows.IndexOf(row) != 0)
{
for (int i = 0; i <= text.Count; i++)
{
string s;
string ValueNeeded;
s = text[i];
ValueNeeded = dt.Columns.IndexOf(text).ToString();
//needs to be the current dt rows value for the header /
//row(0) with that string identifier
//in VB its something like
//DT.Rows(0).Item(text).ToString
}
}
}
}
All I have is the first row's string value; as I iterate through the DT, I'll need to get that "cell's value by leveraging the first rows value. I need the current cell each time.
You can get the value of any cell by referencing the row[index] and the column name string
string xyz= dt.Rows[0]["columnName"].ToString();
OK so assuming the List<string> text parameter contains values of the first row (not the column names) in your DataTable, and you're looking to get the corresponding cell in each row, something like this should work (haven't tested it)
private void setInformation(DataTable dt, List<string> text)
{
var cache = new Dictionary<string, int>(); //cache column numbers
foreach(var entry in text) cache[entry] = getColumnNumberByValue(dt, entry);
for(int i=1; i < dt.Rows.Count; i++)
{
foreach(var entry in text)
{
var columnIndex = cache[entry];
if(columnIndex != -1)
{
var valueNeeded = dt.Rows[i][columnIndex].ToString();
}
}
}
}
private int getColumnNumberByValue(DataTable dataTable, string text)
{
for(int i=0; i < dataTable.Columns.Count; i++)
{
if(dataTable.Rows[0][i].ToString() == text) return i;
}
return -1;
}
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