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");
Related
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 am new to C# programming and topic of operating with jagged array.
I have some data stored in my string[][] arrayname and want to show it in datagridview.
Will be very grateful if you could advice me on the case.
You need to create dataset, usually I use a DataTable, I have drafted a solution to your problem, but you have to using Linq:
var ListName = arrayname.ToList();
//get number of column, probalby you dont need it
int cols = ListName.Select(r => r.Length).Max();
//Create a datasource
DataTable dt = new DataTable();
//Write column, probalby you dont need it
for (int f = 0; f < cols; f++)
dt.Columns.Add("Col " + (f+1));
foreach (var row in ListName) {
//make a row
List<string> Lrow = new List<string>();
Lrow.AddRange(row);
//if row is too short add fields
if (Lrow.Count < cols)
for (int i = Lrow.Count; i < dt.Columns.Count; i++) {
Lrow.Add("");
}
//at last add row to dataTable
dt.Rows.Add(Lrow.ToArray());
}
//and set dataGridView's DataSource to DataTable
dataGridView1.DataSource = dt;
The result should be this
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";
In the code below, i'm returning data that will have a variable number of columns.
So it could return columns 'a,b,c,d,e' or it could return columns 'a,b,c,g,m,n' - the first three columns are set, but then there could be any number of additional columns. I then need to return the data as an anonymous type list. In the code below, i'm showing how i get the data, pivot it, and add the columns into ArrayList objDataColumn , and then create a datatable, which does get me close, but i'm not able to figure out how to return the data as a list.
Instead of having it return a list of datatable rows, i'd like to have it return a generic list of anonymous type like the following (in this case 'Attribute' is the only dynamic column):
{ EntitlementId = 477653184, FileSetTypeID = 146, FileTypeCode = "test", SourceSystemKey = "userkey", Entitlement = "Chg Mgrs - AppDev Sour GUI", Attribute = "Change Manager" }
////Applying linq for geting pivot output
var d = (from f in result
group f by new { f.EntitlementId, f.FileSetTypeID, f.FileTypeCode, f.SourceSystemKey, f.Entitlement }
into myGroup
where myGroup.Count() > 0
select new
{
myGroup.Key.EntitlementId,
myGroup.Key.FileSetTypeID,
myGroup.Key.FileTypeCode,
myGroup.Key.SourceSystemKey,
myGroup.Key.Entitlement,
ColumnName = myGroup.GroupBy(f => f.ColumnName).Select(m => new { Col = m.Key, Value = m.Max(c => c.Value) })
}).ToList();
//PART 2 - Distinct ColumnName
var cols = (
from a in result
select new { ColumnName = a.ColumnName }
).Distinct().ToList();
//PART 3 - Creating array for adding dynamic columns
ArrayList objDataColumn = new ArrayList();
//Fixed columns
objDataColumn.Add("FileTypeCode");
objDataColumn.Add("SourceSystemKey");
objDataColumn.Add("Entitlement");
//Add Subject Name as column in Datatable
for (int i = 0; i < cols.Count; i++)
{
objDataColumn.Add(cols[i].ColumnName);
}
//Add dynamic columns name to datatable dt
DataTable dt = new DataTable();
for (int i = 0; i < objDataColumn.Count; i++)
{
dt.Columns.Add(objDataColumn[i].ToString());
}
//PART 4 - Add data into datatable with respect to dynamic columns and dynamic data
for (int i = 0; i < d.Count; i++)
{
List<string> tempList = new List<string>();
tempList.Add(result[i].FileTypeCode.ToString());
tempList.Add(result[i].SourceSystemKey.ToString());
tempList.Add(result[i].Entitlement.ToString());
var res = d[i].ColumnName.ToList();
for (int j = 0; j < res.Count; j++)
{
tempList.Add(res[j].Value.ToString());
}
dt.Rows.Add(tempList.ToArray<string>());
}
//END PIVOT
IEnumerable<DataRow> rows = dt.AsEnumerable();
Thanks in advance, let me know if any additional info is needed, hope this isnt too confusing.
Was able to solve this using a dynamic list and the ExpandoObject class.
Here's the code, hope it helps someone else:
List<dynamic> dynList = new List<dynamic>();
for (int i = 0; i < d.Count; i++)
{
dynamic dynObj = new ExpandoObject();
((IDictionary<string, object>)dynObj).Add("FileTypeCode", result[i].FileTypeCode.ToString());
((IDictionary<string, object>)dynObj).Add("Fullname", result[i].Fullname.ToString());
((IDictionary<string, object>)dynObj).Add("Entitlement", result[i].Entitlement.ToString());
var res = d[i].ColumnName.ToList();
for (int j = 0; j < res.Count; j++)
{
((IDictionary<string, object>)dynObj).Add(res[j].Col.ToString(), res[j].Value.ToString());
}
dynList.Add(dynObj);
}
var dList = (from da in dynList select da).ToList();
return dList;