I have some logical block in my C# program, as I am new to C# programming.
I have a data-table with duplicate header column names. I have to change my duplicate header like concatenate the name of the prior column to make these headers unique. Table names are coming dynamically.
Current datatable dTable
ID |Name|Age| School | Name | state| Part| Country|Division|Part
Expected dTable
ID |Name|Age| School | Name+school | state| Part| Country|Division|Part+Division
What I have tried and blocked here below
public DataTable RemoveDuplicateRows(DataTable dTable)
{
string[] columnNames = dTable.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToArray();
for (int i = 0; i < columnNames.Length; i++)
{
columnNames[i] = columnNames[i].Split('.')[0].Trim();
}
for (int i = 0; i < columnNames.Length; i++)
{
// create nested loop for compare current values with actual value of arr
for (int j = i + 1; j < columnNames.Length; j++)
{
if (columnNames[i] == columnNames[j])
{
var previous = columnNames[i - 1];
var current = columnNames[i];
columnNames[i] = current + previous;
// blocked here
// only one header is concatenating
// how can I add this newly edited columns to my datatable
}
}
}
return dTable; //cant get updated column headers
}
Related
I'm trying to read data from an excel table which this piece of code performs perfectly
However, this will read all columns from the table which I don't need, I only need specific columns from the excel table
int fabricHeaderRow = findFabricHeader(excelRange); //row number 26
int rows = excelRange.Rows.Count;
int cols = excelRange.Columns.Count;
myNewRow3 = null;
for (int i = fabricHeaderRow + 2; i <= rows; i++)
{
myNewRow3 = bomTable.NewRow();
if (excelRange.Cells[i, 1].Value2 != null) //Checks that the item column isn't empty
{
for (int j = 1; j <= cols; j++)
{
if (excelRange.Cells[i, j].Value2 == null)
{
myNewRow3[j - 1] = string.Empty; //inserts empty string into datagrid row[i] if the cell is empty
}
else
{
myNewRow3[j - 1] = excelRange.Cells[i, j].Value2.ToString();
}
}
if (myNewRow3 != null)
{
bomTable.Rows.Add(myNewRow3); //adds a new row to datable if not null
}
}
else
{
break; //break out of the outer for-loop as it reached an empty excel row
}
}
Excel table example
For example, from the table, I only need ID, COLOR, DESCRIPTION, SUPPILER and COST columns in my datarow, I'm stuck at trying to figure out how to read specific columns with multiple rows into a datarow.
public string[] bomArr = { "ID", "COLOR", "DESCRIPTION", "SUPPLIER", "COST"};
//for loop here
if (bomArr.Any(x => excelRange.Cells[27, j].Value2.Equals(x))){
myNewRow[currentCount] = excelRange.Cells[i, j].Value2.ToString(); //string
currentCount++;
}
I'm thinking of trying this, basically it'll only add to the datarow if any values in the array matches with a column header. But I only can make it work for tables with a single row, multiple rows make it more complicated.
I have a program that I'm writing to extract certain data from various excel spreadsheet.
The process so far is:
for each spreadsheet identified:
-read in the data as a multidimensional array using interop excel (Even though it is slow, it is the best choice due to all of the different file formats I need to read in)
Sample: object[,] cellValues = (object[,])range.Value2;
-Identify the columns that I actually need and what order I need them in. This is stored in a jagged array of bytes:
byte[][] targetColumns
-THe jagged array essentially is (columnIndexFromSpreadsheet, preferredColumnOrder) e.g. if the first column in the spreadsheet should be read in as column 10 it would be (1, 10)
-I sort the jagged array by the preferred column order (that way I can just loop through the array in that order and extract those columns):
public static byte[][] SortTargetColumns(byte[][] targetColumns)
{
return targetColumns.OrderBy(x => x.Skip(1).First()).ToArray();
}
-I then extract that column by creating an array from that column index of the multidimensional array. This is the method that is called:
public static object[] ExtractColumn(object[,] dataArray ,byte columnIndex)
{
return Enumerable.Range(ArrayIndexStart, dataArray.GetLength(0)).Select(x => dataArray[x, columnIndex]).ToArray();
}
Usage:
array = ExtractColumn(dataArray, (byte) colIndex);
Now I am trying to piece these extracted arrays back together to make it readable. I will need to do some manipulation on some of the columns and then write to a text file after consolidating. The only problem is that I have no idea how to do this correctly. I have tried the following methods but continue to get a null reference exception:
// Get Row Count of dataArray
int rowCount = dataArray.GetLength(0);
// Create List to store extracted arrays
List<object[]> extractedDataList = new List<object[]>();
// Loop through target columns and extract the column as an array
for (byte colIndex = 1; colIndex <= targetColumns.Length + 1; colIndex++)
{
object[] array = ExtractColumn(dataArray, (byte) colIndex);
extractedDataList.Add(array);
}
// Create jagged array
object[][] extractedDataArray = new object[rowCount][] ;
for(int i = 0; i < extractedDataArray.GetLength(0); i++)
{
List<object> row = new List<object>();
for (int j = 0; j < extractedDataList.Count; j++)
{
row.Add(extractedDataList[j][i].ToString());
//extractedDataArray[i][j] = extractedDataList[j][i].ToString(); <-- null reference
}
extractedDataArray[i] = row.ToArray();
}
I'm at a loss of what else to try to put these column arrays back in a form that I can easily work with. Any and all tips/recommendations would be greatly appreciated.
Whenever you get confused like this, break the problem down into small pieces, and use meaningful names.
Let's say you have an array of columns, each of which has one element per row. That might be declared like this:
object[][] columns;
First, let's get the row and column counts:
var columnCount = columns.Length;
var rowCount = columns[0].Length;
Now write a small local function to accept a row and column index and return the right cell. In case not all of your columns have the same number of rows, you can include a boundary check and just return null if a cell isn't there.
object Getter(int row, int col)
{
bool outOfBounds = (row >= columns[col].Length);
return outOfBounds ? null : columns[col][row];
}
Now all we have to do is iterate over the rows to create the inner arrays:
object[][] target = new object[rowCount][]
for (int row = 0; row < rowCount; row++)
{
target[row] = new object[columnCount];
}
And add in the code that uses the getter to populate the cells:
object[][] target = new object[rowCount][];
for (int row = 0; row < rowCount; row++)
{
target[row] = new object[columnCount];
for (int col = 0; col < columnCount; col++)
{
var cellValue = Getter(row, col);
target[row][columnCount] = cellValue;
}
}
All together, it is simple to read:
var columnCount = columns.Length;
var rowCount = columns[0].Length;
object Getter(int row, int col)
{
bool outOfBounds = (row >= columns[col].Length);
return outOfBounds ? null : columns[col][row];
}
object[][] target = new object[rowCount][];
for (int row = 0; row < rowCount; row++)
{
target[row] = new object[columnCount];
for (int col = 0; col < columnCount; col++)
{
var cellValue = Getter(row, col);
target[row][columnCount] = cellValue;
}
}
I am getting the index of the cell of a word table using for loop which takes a lot of time for bigger tables, is there any way to do this without for loop?
public static int[] GetColumnIndex(Xceed.Words.NET.Table table, string columnName, int endRow,int k)
{
int[] data = { -1, -1 };
for (int j = k; j < endRow; j++)
{
for (int i = 0; i < table.Rows[j].Cells.Count; ++i)
{
if (table.Rows[j].Cells[i].Paragraphs[0].Text.Equals("«" + columnName + "»"))
{
data[0] = j;
data[1] = i;
return data;
}
}
}
return data;
}
and I am calling this function form another function
int startRow = 0, endRow = 0;
int[] ind;
DocX doc;
doc = DocX.Load(fileName);
Xceed.Words.NET.Table t;
t = doc.Tables[0];
endRow = t.Rows.Count;
System.Data.DataTable dt = new DataTable();
dt = reader(report.Query);
foreach (DataColumn col in dt.Columns)
{
ind = GetColumnIndex(t, col.ColumnName, endRow,2);
//...more code here...
}
A few things you can do to optimise your algorithm (based on your access pattern) is that you search the same table number of times (in fact, since you are searching column names in the table, number of searches increases quickly as the table gets big). Hence, it would be worth transforming the data in the table to a data structure indexed by the words (for e.g. a Sorted Dictionary).
Firstly, create a class that holds the content of the table. This way when you want to search the same table, you can use the same instance of the class and avoid recreating the data structure based on the sorted dictionary:
public class XceedTableAdapter
{
private readonly SortedDictionary<string, (int row, int column)> dict;
public XceedTableAdapter(Xceed.Words.NET.Table table)
{
this.dict = new SortedDictionary<string, (int, int)>();
// Copy the content of the table into the dict.
// If you have duplicate words you need a SortedDictionary<string, List<(int, int)>> type. This is not clear in your question.
for (var i = 0, i < rowCount; i++)
{
for (var j = 0; j < columnCount; j++)
{
// this will overwrite the index if the text was previously found:
this.dict[table.Rows[i].Cells[j].Paragraphs[0].Text] = (i, j);
}
}
}
public (int, int) GetColumnIndex(string searchText)
{
if(this.dict.TryGetValue(searchText, out var index))
{
return index;
}
return (-1, -1);
}
}
Now you loop the entire table only once and the subsequent searches will happen in O(log n). If Xceed has a function to transform data table to a dictionary, that would be quite handy. I'm not familiar with this library.
Now you can search it like:
var searchableTable = new XceedTableAdapter(doc.Tables[0]);
foreach (var col in dt.Columns)
{
ind = searchableTable.GetColumnIndex(col);
}
I need to retrieve data according to Row by row,
Example:
Id | Username | Password
001 | Xyz | Abc
002 | ghdfdhjs | dsjkhfjds
As of now, I am getting Single column values(001, Xyz, Abc), which is not giving me desired result.
I need to return result, in such a way by which single row contains all column values (001, Xyz, Abc)
Trial Code:
DataTableCollection tableCollection = result.Tables;
DataTable table = tableCollection["test"];
var rows = table.Rows;
int rowCount = rows.Count;
int colCount = table.Columns.Count;
var results = new object[rowCount, colCount];
for (int i = 0; i < rowCount; i++)
{
var row = rows[i];
for (int j = 0; j < colCount; j++)
{
results[i, j] = row[j];
}
}
return results;
Reference:
I am trying to manage it by foreach loop with the help of DataRow, which is not working
object[] temp = { };
for (int i = 0; i < rowCount; i++)
{
DataRow row = table.Rows[i];
foreach (object [] item in row.ItemArray)
{
temp = item;
}
}
return temp;
Cast Exception : System.InvalidCastException : Unable to cast object
of type 'System.String' to type 'System.Object[]'
Can someone suggest correct way to get it,
I have added a column header to my datagridview from a list in excel. Now I have a dictionary with the key as the column header and value needs to be inserted to the new row. One dictionary per row.
foreach (DataGridViewRow row in dgv_ExitPoints.Rows)
{
for (int j = 0; j < dgv_ExitPoints.Columns.Count; j++)
{
String header = dgv_ExitPoints.Columns[j].HeaderText;
if(exitPointDictionary.ContainsKey(header)
{
???
}
}
}
I have solved it. If anyone is interested in future:
`for (int j = 0; j < dgv_ExitPoints.Columns.Count; j++)
{
String header = dgv_ExitPoints.Columns[j].HeaderText;
if(exitPointDictionary.ContainsKey(header))
{
dgv_ExitPoints.Rows[counter].Cells[j].Value = exitPointDictionary[header].ToString();
}
++counter;
}`