Getting maximum row/column number from Excel Range - c#

I have a certain Range, for example:
Range rng = activeWorksheet.Range["A1", "B50"];
I've been retrieving the maximum row and column number used in this Range by looping through it and assigning a row number to my variable, but there must be a better way? I've been doing this:
int maxRow = 0;
foreach (Range row in rng.Rows)
maxRow = row.Row;

I think I found the most elegant solution for this, something like this:
int maxRow = rng.Row + rng.Rows.Count - 1;
int maxColumn = rng.Column + rng.Columns.Count - 1;
rng.Row will retrieve the first used row number in the range, rng.Rows.Count - 1 will retrieve the total amount of rows used in this range and we also deduct 1 to get the correct maximum row number.

How about you search for the last used Row?
public int FindLastFilledRowInRange(Range range)
{
var cell = range.Find("*", SearchOrder: Excel.XlSearchOrder.xlByRows, SearchDirection: Excel.XlSearchDirection.xlPrevious);
return cell.Row;
}
If you start later than row 1 you can just do some additional math on the returned row...

If you don't mind evaluating the string Address...
by using ".EntireRow.Address(False, False)" we get a string that's easily parsed...
C#...
int maxRow = range.EntireRow.Address(false, false).Split(",:".ToCharArray()).Select(n => int.Parse(n)).Max();
string maxCol = range.EntireColumn.Address(false, false).Split(",:".ToCharArray()).Select(s => s.PadLeft(3)).Max().Trim();
VB... ;
Dim maxRow As Integer = -1
Dim maxCol As String = ""
range = ws.Range("$BB9:$C11")
maxRow = range.EntireRow.Address(False, False).Split(",:".ToCharArray()).Select(Function(n) Integer.Parse(n)).Max() ' 9:11
maxCol = range.EntireColumn.Address(False, False).Split(",:".ToCharArray()).Select(Function(s) s.PadLeft(3)).Max().Trim() ' C:BB
given range BB9:C11
maxRow is 11 from 9:11 ...if not parsed to int, then the Max() would be 9 due to string sorting
maxCol is "BB" from C:BB ...if not padded left, then the Max() would be "C" due to string sorting
range = ws.Range("A1:A3,AE15:AE9,C4:C7")
maxRow = range.EntireRow.Address(False, False).Split(",:".ToCharArray()).Select(Function(n) Integer.Parse(n)).Max() ' 1:3,9:15,4:7
maxCol = range.EntireColumn.Address(False, False).Split(",:".ToCharArray()).Select(Function(s) s.PadLeft(3)).Max().Trim() ' 1:3,9:15,4:7
and given range non-contiguous range: A1:A3,AE15:AE9,C4:C7
maxRow is 15 from 1:3,9:15,4:7 ...even with non-contiguous range, and referenced out of order= AE15:AE9
maxCol is "AE" from C:AE ...even with non-contiguous range
this DOES work with whole COLUMN selection "A:A"
this does NOT work with whole row selection "3:3"

I had this on a previous project:
wb = excel.Workbooks.Open(fileName);
worksheet = wb.ActiveSheet;
Range usedRange = worksheet.UsedRange;
this.lastRow = usedRange.Rows.Count;
this.lastCell = usedRange.Columns.Count;
Using lastRow will give you the emptylines which in my case we not useful.

Related

Formula in Excel - Logarithmic Average

I need to implement this equation:
In c# it is pretty straightforward:
static double LogarithmicSummed(IReadOnlyList<double> values)
{
double outVal = 0;
for (int i = 0; i < values.Count; i++)
{
outVal = outVal + Math.Pow(10, values[i] / 10);
}
return 10 * Math.Log10(outVal);
}
I need to verify the data in an excel sheet where I print out the data programmatically. That is the raw data, my c# calculations on the raw data AND the excel formual that should match my c# calculations. I just do not know how to write the formula in excel.
I know the rows and columns where the data are and they are next to each other. So for example, if I needed the arithmetic average, ie:
I could print out for each row:
// If row = 1, startColumn = A, endColumn = D, noOfColumn = 4. This would print: =SUM(A1:D1)/4
mean[row] = #"=SUM(" + startColumn + row + ":" + endColumn + row + ")/" + noOfColumns;
What would I print out to match the first formula I wrote? Or what would I need to write in Excel?
without VBA:
Put your data in A1 through A10 and in B1 enter:
=10^(A1/10)
and copy down. Then in another cell enter:
=10*LOG10(SUM(B1:B10))
You can avoid the "helper" column (column B) by using:
=10*LOG10(SUMPRODUCT(10^((A1:A10)/10)))
If that's how you enter your formulas, then why not use the literal form of the sum? Ie use a for loop over the columns and fill in:
10*log(10^(A1/10)+10^(A2/10)+10^(A3/10)+10^(A4/10))

Excel Open xml sdk - Copy formula between cells with range modification

I have a cell with formula (CellFormula). I don't know the formula content (Might be Sum, ifs or any other calculation).
The formula might include range.
When you drag the cell to other cells (manually at excel application itself) the formula range get updated.
I want the formula programatic copy to behave the same way.
If the formula contains range specifiers I want to update them to the current row.
Is there some built-in way to do so without regular expression manipulations?
I don't know if it's a good solution that cover all possibilities but...
//Formula update
if (cloneFromCell.CellFormula != null && (cloneFromCell.CellFormula.FormulaType == null || !cloneFromCell.CellFormula.FormulaType.HasValue || cloneFromCell.CellFormula.FormulaType.Value == CellFormulaValues.Normal))
{
uint cloneRowIndex = OXMLTools.GetRowIndex(cloneFromCell.CellReference);
uint offset = rowIndex - cloneRowIndex;
exCell.CellFormula.Text = OXMLTools.GetUpdatedFormulaToNewRow(cloneFromCell.CellFormula.Text, offset);
}
public static string GetUpdatedFormulaToNewRow(string formula, uint offset)
{
return Regex.Replace(formula, #"[A-Za-z]+\d+", delegate(Match match)
{
//Calculate the new row for this cell in the formula by the given offset
uint oldRow = GetRowIndex(match.Value);
string col = GetColumnName(match.Value);
uint newRow = oldRow + offset;
//Create the new reference for this cell
string newRef = col + newRow;
return newRef;
});
}

Convert text data into multidimensional array in C#:

I have a following string, with line breaks in a textfile:
Card No. Seq Account 1 Account 2 Account 3 Account 4 Customer Name Expiry Status
0100000184998 1 2500855884500 - - /NIRMAL PRADHAN 1302 Cold
0100000186936 1 - - - /RITA SHRESTHA 1302 Cold
0100000238562 1 2500211214500 - - /HARRY SHARMA 1301 Cold
0100000270755 0 1820823730100 - - /EXPRESS ACCOUNT 9999 Cold
0100000272629 0 1820833290100 - - - /ROMA MAHARJAN 1208 Cold
0100000272637 0 2510171014500 - - /NITIN KUMAR SHRESTHA 1208 Cold
0100000272645 0 1800505550100 - - - /DR HARI BHATTA 1208 Cold
Here,
Card No., Seq has fixed digits.
Account 1, Account 2, Account 3, Account 4 can have fixed digit
number or - or null.
Customer Name can have First Name, Last Name, Middle Name etc.
My desired result is:
array[0][0] = "0100000184998"
array[0][1] = "1"
array[0][2] = "2500855884500"
array[0][3] = " "
array[0][4] = "-"
array[0][6] = "NIRMAL PRADHAN "
array[1][0] = "0100000186936"
array[1][1] = "1"
array[1][3] = " "
array[1][4] = "-"
Here, What I tried is:
var sourceFile = txtProcessingFile.Text;
string contents = System.IO.File.ReadAllText(sourceFile);
var newarr = contents.Split(new char[]{ '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select (x =>
x.Split(new char[]{ ' ' }, StringSplitOptions.RemoveEmptyEntries).ToArray()
).ToArray();
DataTable dt = new DataTable("NewDataTable");
dt.Columns.Add("CardNo");
dt.Columns.Add("SNo");
dt.Columns.Add("Account1");
and so on...
for (int row = 0; row < newarr.Length; row++)
{
for (int col = 0; col < newarr[col].Length; col++)
{
dt.Rows.Add(newarr[row]);
row++;
}
}
This works fine if data field is not empty and Customer name is only the first name or delimited.
But, here what I am trying to get is:
First Name, Middle Name or Last Name must be stored in the same
array element.
Account Number in the array element must left blank if it is blank.
How is it possible to store it correctly on the datatable ?
I suggest that you learn to use the TextFieldParser class. Yes, it's in the Microsoft.VisualBasic namespace, but you can use it from C#. That class lets you easily parse text files that have fixed field widths. See the article How to: Read From Fixed-width Text Files in Visual Basic for an example. Again, the sample is in Visual Basic, but it should be very easy to convert to C#.
If you are willing to make the compromise of not making a difference between - and null values in the account values, you may try this:
var sourceFile = txtProcessingFile.Text;
string[] contents = System.IO.File.ReadAllLines(sourceFile);
DataTable dt = new DataTable("NewDataTable");
dt.Columns.Add("CardNo");
dt.Columns.Add("SNo");
dt.Columns.Add("Account1");
dt.Columns.Add("Account2");
dt.Columns.Add("Account3");
dt.Columns.Add("Account4");
dt.Columns.Add("CustomerName");
dt.Columns.Add("Expiry");
dt.Columns.Add("Status");
for (int row = 2; row < contents.Length; row++)
{
var newRow = dt.NewRow();
var regEx = new Regex(#"([\w]*)");
var matches = regEx.Matches(contents[row].ToString())
.Cast<Match>()
.Where(m => !String.IsNullOrEmpty(m.Value))
.ToList();
var numbers = matches.Where(m => Regex.IsMatch(m.Value, #"^\d+$")).ToList();
var names = matches.Where(m => !Regex.IsMatch(m.Value, #"^\d+$")).ToList();
for (int i = 0; i < numbers.Count() - 1; i++)
{
newRow[i] = numbers.Skip(i).First();
}
newRow[newRow.ItemArray.Length - 2] = numbers.Last();
newRow[newRow.ItemArray.Length - 1] = names.Last();
newRow[newRow.ItemArray.Length - 3] = names.Take(names.Count() - 1).Aggregate<Match, string>("", (a, b) => a += " " + b.Value);
dt.Rows.Add(newRow);
}
To get around the names with a single space in them, you could try splitting on a double-space instead of a single space:
x.Split(new string[]{ " " }
This still won't fix the issue with columns that have no value in them. It appears that your text file has everything in a specific position. Seq is in position 16, Account 1 is in position 20, etc.
Once your lines are stored in newarr, you may just want to use String.Substring() with .Trim() to get the value in each column.

C# excel - extract only rows within a range of columns

I have the following code:
Excel.Range range = workSheet.UsedRange;
for (rowCount = 2; rowCount <= range.Rows.Count; rowCount++)
{
//Do something here
}
However, I encounter the following problem. If I have the following excel data:
cell1, cell2, cell3
cell4, cell5, cell6
..............cell7
range.Rows.Count will return 3. However, I don't care about column C - it has some data that is used for drop-down lists within say column 1. How can I get the range.Rows.Count only for columns A and B?
For example:
Assuming there are no blanks in column A, just walk down rows until you hit a blank row using range.Offset(x,y): (sorry, I'm more fluent with Excel VBA, you'll have to translate to C#)
Dim myCell as Range
set myCell = workSheet.Range("A2");
Do Until myCell.Formula = ""
'Do something here
set myCell = myCell.Offset(1,0) 'Moves down a row
Loop
Or just loop through numerically using Worksheet.Cells(row,col):
Dim myRow as Integer
myRow = 2
Do Until cells(myrow,2).Formula = ""
'Do something here
myRow = myRow + 1
Loop
Edit: You could also use Range("A1").End(xlDown) to find the last populated cell of the first column. This should work with your current logic. Something like (I'll try it in C#):
Excel.Range range = workSheet.Range("A1", Range("A1").End(xlDown));
for (rowCount = 2; rowCount <= range.Rows.Count; rowCount++)
{
//Do something here
}

Convert Excel Cell Value From Text To Number Using C#

I am using Windows Application. In that Application i exported the DataGrid into Excel Successfully... Now the problem is , When i exported from Grid to the Excel Sheet, The cell values are having some green color mark on left top corner in the Excel Sheet... I thought that is type cast problem . How Shall i avoid that Problem.... and How to change the cell value from text to Number ...(i.e)Convert To Number....
Can Anyone tell me the solution of this problem?
My Code for Formatting That Excel Sheet For Some Range of Values,
wksheet.Range[GetRanges[0].ToString(), GetRanges[GetRanges.Count-2].ToString()].Merge();
wksheet.get_Range(GetRanges[0].ToString(), GetRanges[GetRanges.Count-].ToString()).Interior.Color = Color.FromArgb(192, 0, 0);
I haven't a Windows machine to test on at the moment, but perhaps you would want to try changing the cell format, e.g.:
my_range.NumberFormat = "0.0"; // change number of decimal places as needed
Here's a full example from Microsoft: How to automate Microsoft Excel from Microsoft Visual C#.NET.
The following routine will dynamically fill a spreadsheet from data (text and numbers) in a text file.
The kicker is using an object array to set the values.
StreamReader STRead;
String[] STCells;
object[] cellData;
int temp;
Excel.Range tempRange;
for (int i = 0; i < FileList.Length; i++)
{
STRead = FileList[i].OpenText();
int j = 0;
while (!STRead.EndOfStream)
{
STCells = STRead.ReadLine().Split(',');
cellData = new object[STCells.Length];
for (int k = 0; k < STCells.Length; k++)
{
if (Int32.TryParse(STCells[k], out temp))
cellData[k] = temp;
else
cellData[k] = STCells[k];
}
tempRange = sheetList[i].get_Range(ReturnCellID(j, 0),
ReturnCellID(j, STCells.Length - 1));
j++;
tempRange.Value2 = cellData;
}
STRead.Close();
}
I had this problem with a excel sheet containing both regular numbers and percent (a result of an export of strings from a Crystal Report).
To change all of them at once I made a loop somewhat like this:
//example range
Excel.Range rng = mWorkSheet.get_Range("A1", "H25");
foreach (Excel.Range range in rng)
{
if (range.Value != null)
{
int number;
bool isNumber = int.TryParse(range.Value.ToString().Trim(), out number);
if (isNumber)
{
range.NumberFormat = "#,##0";
range.Value = number;
}
else
{
//the percent values were decimals with commas in the string
string temp = range.Value.ToString();
temp = temp.Replace(",", ".");
range.Value = temp;
}
}
}
Result was that both the percentage strings and the numbers got converted into the correct Excel format.
When you assign the value to the cells, try to get all the values in an 2 dimensional array first and then assign the array to the Range. this way its going to be very fast and also i guess the values in the cell will be number. try it out ...hopefully it should work
Change the cell value from text to Number is easy with Excel Jet Cell .NET component.
Cell["A1"].Style.StringFormat = "##.#"; // or "0.0" same as in MS Excel

Categories

Resources