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;
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");
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 facing a strange issue where I'm assigning a List of Strings to a GridViewData but instead of showing me the list of string, GridViewData is showing numbers.
Code is as following:
private void ShowListofObjects()
{
dgvShowObjects.DataSource = showObjects(dtMenu, dtAllActiveUsersAccessRights);
}
private List<string> showObjects(DataTable dtMenu, DataTable dtAllActiveUsersAccessRights)
{
DataTable dtFinalTable = new DataTable();
dtFinalTable.Columns.Add("Object", typeof(String));
dtFinalTable.Columns.Add("Add", typeof(String));
dtFinalTable.Columns.Add("View", typeof(String));
dtFinalTable.Columns.Add("Modify", typeof(String));
List<string> l = new List<string>();
int counter = 0;
for (int i = 0; i < dtMenu.Rows.Count; i++)
{
string obj = dtMenu.Rows[i][1].ToString(); // first item in Menu Table
for (int j = 0; j < dtAllActiveUsersAccessRights.Rows.Count; j++)
{
string s = dtAllActiveUsersAccessRights.Rows[j][1].ToString();
if (s == obj)
{
l.Add(dtAllActiveUsersAccessRights.Rows[j][1].ToString());
counter++;
}
}
}
lblCounter.Text = counter.ToString();
return l;
}
On debugging, on line return l; I can see List l populated with string values but when I'm assigning this list to GridViewData on line dgvShowObjects.DataSource = showObjects(dtMenu, dtAllActiveUsersAccessRights); and run the application, its showing a list of numbers.
Can anyone see why?
Thanks in advance.
I have a data set from which I need to isolate and feedback non-unique values across multiple columns. (Think multi column primary key violation in database table). I am currently doing this by concatenating those columns on each row into a List<string>. Doing a count of the item in the list and if greater than 1 adding an error message to another column on the same row - that bit's important, I need to be able to provide feedback on the position of the duplicate/s, not just the fact that there is a duplicate.
The problem is speed, although this technically works, it doesn't work well as a practical solution because I will potentially be working with data sets of several hundred thousand rows
Code:
List<string> mylist = new List<string>();
string ThisConcat = "";
for (int i = 0; i < dtLogData.Rows.Count-1; i++)
{
foreach (int ColNum in ColNumList)
{
ThisConcat += dtLogData.Rows[i].Field<string>(ColNum-1);
}
mylist.Add(ThisConcat);
ThisConcat = "";
}
Then:
for (int i = 0; i < dtLogData.Rows.Count-1; i++)
{
int count = mylist.Count(j => j == mylist[i]);
if (count > 1)
{
dtLogData.Rows[i][ColCnt] = MyErrorString;
}
}
I'll give you a slightly different solution. This assumes you are willing to add another column to the DataTable:
dtLogData.Columns.Add("hash");
Then you cast the table:
var t = dtLogData.AsEnumerable();
First you compute the concatenated string and get a hold of the rows. You can do this in two ways. If you want to limit the columns to certain indexes (like your original code):
var rows = t.Select(row =>
{
StringBuilder builder = new StringBuilder();
ColNumList.ForEach(i => builder.Append(row[i]));
row["hash"] = builder.ToString();
return row;
}
);
Or if you just want to use all columns:
var rows = t.Select(row =>
{
row["hash"] = string.Join("", row.ItemArray.Select(i => i.ToString()));
return row;
}
);
You then grab all the rows with duplicates and mark them accordingly:
foreach (var dataRow in rows.GroupBy(r => r["hash"]).Where(g => g.Count() > 1).SelectMany(g => g))
{
dataRow[ColCnt] = MyErrorString;
}
You could use Dictionary<string, List<DataRow>> instead of List<string> and store row references to each ThisConcat tupel.
Dictionary<string, List<DataRow>> mylist = new Dictionary<string, List<DataRow>>();
string ThisConcat = "";
for (int i = 0; i < dtLogData.Rows.Count - 1; i++) {
foreach (int ColNum in ColNumList) {
ThisConcat += dtLogData.Rows[i].Field<string>(ColNum - 1);
}
if (! mylist.ContainsKey(ThisConcat))
mylist[ThisConcat] = new List<DataRow>();
mylist[ThisConcat].Add(dtLogData.Rows[i]);
ThisConcat = "";
}
foreach (var p in mylist) {
if (p.Value.Count > 1) {
foreach (var r in p.Value) {
r[ColCnt] = MyErrorString;
}
}
}
You call mylist.Count for each row. If you have n rows with almost different tuples, you get quadratic runtime complexity O(n^2). The code above should have linear complexity O(n).
You try can replacing the first bit of code with this one:
List<string> mylist = new List<string>();
StringBuilder sb;
foreach (DataRow row in dtLogData.Rows)
{
sb = new StringBuilder();
ColNumList.ForEach(i => sb.Append(row[i - 1]));
mylist.Add(sb.ToString());
}
And the second part with:
mylist.Select((a, b) => new { count = mylist.Where(c => c.Equals(a)).Count(), index = b })
.Where(e => e.count > 1).ToList().ForEach(f => dtLogData.Rows[f.index][ColCnt] = MyErrorString);
Gotta love them downvotes with no explanation
I am trying to convert an array of strings to a DataGridView with little success so far. The method below is called for that purpose, but it doesn't seem to return any table.
While debugging I can see that I fetch the correct values from cont.GetAllEmployee() but when the debug goes to ConvertListToDataTable(list) the debugging does not show that method which is something unusual.
This is my current code :
private DataTable ConvertListToDataTable(List<WindowsFormsApplication1.ServiceReference1.ArrayOfString> list)
{
DataTable table = new DataTable();
List<string> columnNames = list[0];
for (int i = 0; i < columnNames.Count; i++)
{
table.Columns.Add(columnNames[i].ToString());
}
foreach (var array in list)
{
table.Rows.Add(array);
}
return table;
}
List <WindowsFormsApplication1.ServiceReference1.ArrayOfString> list = cont.GetAllEmployee();
dataGridView.DataSource = ConvertListToDataTable(list);
New rows must be instanciated by DataTable.NewRow() method to obtain new DataRow with the same scheme as DataTable.
private DataTable ConvertListToDataTable(List<WindowsFormsApplication1.ServiceReference1.ArrayOfString> list)
{
DataTable table = new DataTable();
List<string> columnNames = list[0];
for (int i = 0; i < columnNames.Count; i++)
{
table.Columns.Add(columnNames[i].ToString());
}
foreach (var array in list)
{
var NewRow = table.NewRow();
for(int i = 0; i < columnNames.Count)
{
NewRow[columnNames[i]] = array[i];
}
table.Rows.Add(NewRow);
}
return table;
}
List <WindowsFormsApplication1.ServiceReference1.ArrayOfString> list = cont.GetAllEmployee();
dataGridView.DataSource = ConvertListToDataTable(list);
dataGridView.DataBind();