I have the following code in VBA to find the last cell inside a range that is greater than 0:
Set myRange = .Range(.Cells(1, 14), .Cells(1, 23))
count = 0 'Counter
For Each cll In myRange
If cll.Value > 0 Then
count = count + 1
NoZeroDir = cll.Address
End If
Next
It gets the address of the last cell greater than 0 in that range.
But, how could I get the address from the cell greater than 0 before this last one?
I was thinking of using an offset but that way I'd get the cell before the last > 0 but this cell could not be > 0.
To illustrate it a bit, as an example I have:
2 3 5 0 1 7 0 8 1 0 1
The address from the last cell > 0 would be (1,11) but I want the cell before that one > 0, that is (1,9), not (1,10) as this is 0.
To find the second last number that is >0
Option Explicit
Public Sub FindSecondLastValueGreaterZero()
Dim MyRange As Range
Set MyRange = Range("A1:K1")
Const MAXSKIPS As Long = 1 ' skip 1 number that is >0
Dim Skips As Long
Dim iCol As Long
For iCol = MyRange.Columns.Count To 1 Step -1
If MyRange(1, iCol).Value > 0 And Skips < MAXSKIPS Then
Skips = Skips + 1
ElseIf MyRange(1, iCol).Value > 0 Then
Debug.Print "Found at: " & MyRange(1, iCol).Address
Exit For
End If
Next iCol
End Sub
This will start in K loop backwards until it finds a 0 then keeps doing it until skipped >0 is 1 and print the address I1 as result.
Since this loops backwards from right to left it should find the result (in most cases) faster than your code.
Alternative using Worksheetfunction Filter() (vs. MS 365)
Based upon the newer WorksheetFunction Filter() (available since version MS/Excel 365) and using OP's range indication
=FILTER(COLUMN(A1:K1),A1:K1>0)
you are able to get an array of column numbers from cells greater than zero (0) via an evaluation of the generalized formula pattern.
If you get at least two remaining columns (i.e. an upper boundary UBound() > 1) you get the wanted 2nd last column number by i = cols(UBound(cols) - 1) and can translate it into an address via Cells(1, i).Address.
Public Sub SecondLastValGreaterZero()
'a) construct formula to evaluate
Const FormulaPattern As String = "=FILTER(COLUMN($),$>0)"
Dim rng As Range
Set rng = Sheet1.Range("A1:K1") ' << change to your needs
Dim myFormula As String
myFormula = Replace(FormulaPattern, "$", rng.Address(False, False, external:=True))
'b) get tabular column numbers via Evaluate
Dim cols As Variant
cols = Evaluate(myFormula)
'c) get the 2nd last column number of cell values > 0
Dim i As Long
If Not IsError(cols) Then
If UBound(cols) > 1 Then i = cols(UBound(cols) - 1)
End If
'd) display result
If i > 0 Then
Debug.Print "Found at column #" & i & ": " & Cells(1, i).Address
Else
Debug.Print "Invalid column number " & CStr(i)
End If
End Sub
Example result in VB Editor's immediate window
Found at column #9: $I$1
I have an excel sheet with some rows of descriptions in a single column, what I am aiming is to get a vba that would go though all those rows of descriptions and truncate it upto certain character limit for example 30 characters and if the truncation stops at 30 character in the middle of the word then I want the complete word(could extend beyond 30 characters in this case).
I tried to do this with the VBA code below, but I am not able to get what I am looking for.
Function foo(r As Range)
Dim sentence As Variant
Dim w As Integer
Dim ret As String
' assign this cell's value to an array called "sentence"
sentence = Split(r.Value, " ")
' iterate each word in the sentence
For w = LBound(sentence) To UBound(sentence)
' trim to 6 characters:
sentence(w) = Left(sentence(w), 6)
Next
' Join the array back to a string/sentence
ret = Join(sentence, " ")
'Make sure the sentence is max 20 chars:
ret = Left(ret, 20)
'return the value to your function expression:
foo = ret
End Function
I expect the code to go through all the rows of a specific column and truncate it upto 30 characters and if the truncation stops in the middle of the word, then it should keep that word.
Since you tagged it for a formula
=LEFT(A1,FIND(" ",A1,30)-1)
I think you're looking for the instr() function. This could give you the first space-character after position 30.
You would get the following:
Dim SpacePosition as Integer
'return the position for the first space-character after position 29
SpacePosition = Instr(30, r.value," ")
if SpacePosition <> 0 then
'fill ret with the substring up to the first space after position 29
ret = left(r.value, SpacePosition - 1)
else
'if there is no space-character (after position 29) then take the whole string
ret = r.value
end if
Hope that helps.
Best & brilliant solution by #scott Craner. However, In you VBA code you may Change the followings to get required result
'Join the array back to a string/sentence
'ret = Join(sentence, " ")
ret = ""
For w = LBound(sentence) To UBound(sentence)
' trim to 6 characters:
sentence(w) = Left(sentence(w), 6)
ret = ret & IIf(Len(ret) > 0, " ", "") & sentence(w)
If Len(ret) >= 30 Then Exit For
Next w
'Make sure the sentence is max 20 chars:
' ret = Left(ret, 20)
Public Function foo(r As Range, length As Integer) As String
If Len(r.Value) <= length Then
foo = r.Value
Else
foo = Left(r.Value, 1 + length)
foo = RTrim(Left(foo, InStrRev(foo, " ")))
End If
End Function
I suppose you would want to run that by passing 20 as the 2nd parameter
Loop rows from sheet 1, column A starting from row 1:
Option Explicit
Sub test()
Dim Lastrow As Long, i As Long
With ThisWorkbook.Worksheets("Sheet1")
Lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 1 To Lastrow
'Insert Code
Next i
End With
End Sub
Does anyone have an Excel VBA function which can return the column letter(s) from a number?
For example, entering 100 should return CV.
This function returns the column letter for a given column number.
Function Col_Letter(lngCol As Long) As String
Dim vArr
vArr = Split(Cells(1, lngCol).Address(True, False), "$")
Col_Letter = vArr(0)
End Function
testing code for column 100
Sub Test()
MsgBox Col_Letter(100)
End Sub
If you'd rather not use a range object:
Function ColumnLetter(ColumnNumber As Long) As String
Dim n As Long
Dim c As Byte
Dim s As String
n = ColumnNumber
Do
c = ((n - 1) Mod 26)
s = Chr(c + 65) & s
n = (n - c) \ 26
Loop While n > 0
ColumnLetter = s
End Function
Something that works for me is:
Cells(Row,Column).Address
This will return the $AE$1 format reference for you.
For example: MsgBox Columns( 9347 ).Address returns .
To return ONLY the column letter(s): Split((Columns(Column Index).Address(,0)),":")(0)
For example: MsgBox Split((Columns( 2734 ).Address(,0)),":")(0) returns .
And a solution using recursion:
Function ColumnNumberToLetter(iCol As Long) As String
Dim lAlpha As Long
Dim lRemainder As Long
If iCol <= 26 Then
ColumnNumberToLetter = Chr(iCol + 64)
Else
lRemainder = iCol Mod 26
lAlpha = Int(iCol / 26)
If lRemainder = 0 Then
lRemainder = 26
lAlpha = lAlpha - 1
End If
ColumnNumberToLetter = ColumnNumberToLetter(lAlpha) & Chr(lRemainder + 64)
End If
End Function
Just one more way to do this. Brettdj's answer made me think of this, but if you use this method you don't have to use a variant array, you can go directly to a string.
ColLtr = Cells(1, ColNum).Address(True, False)
ColLtr = Replace(ColLtr, "$1", "")
or can make it a little more compact with this
ColLtr = Replace(Cells(1, ColNum).Address(True, False), "$1", "")
Notice this does depend on you referencing row 1 in the cells object.
This is a version of robartsd's answer (with the flavor of Jan Wijninckx's one line solution), using recursion instead of a loop.
Public Function ColumnLetter(Column As Integer) As String
If Column < 1 Then Exit Function
ColumnLetter = ColumnLetter(Int((Column - 1) / 26)) & Chr(((Column - 1) Mod 26) + Asc("A"))
End Function
I've tested this with the following inputs:
1 => "A"
26 => "Z"
27 => "AA"
51 => "AY"
702 => "ZZ"
703 => "AAA"
-1 => ""
-234=> ""
This is available through using a formula:
=SUBSTITUTE(ADDRESS(1,COLUMN(),4),"1","")
and so also can be written as a VBA function as requested:
Function ColName(colNum As Integer) As String
ColName = Split(Worksheets(1).Cells(1, colNum).Address, "$")(1)
End Function
robertsd's code is elegant, yet to make it future-proof, change the declaration of n to type long
In case you want a formula to avoid macro's, here is something that works up to column 702 inclusive
=IF(A1>26,CHAR(INT((A1-1)/26)+64),"")&CHAR(MOD(A1-1,26)+65)
where A1 is the cell containing the column number to be converted to letters.
LATEST UPDATE: Please ignore the function below, #SurasinTancharoen managed to alert me that it is broken at n = 53.
For those who are interested, here are other broken values just below n = 200:
Please use #brettdj function for all your needs. It even works for Microsoft Excel latest maximum number of columns limit: 16384 should gives XFD
END OF UPDATE
The function below is provided by Microsoft:
Function ConvertToLetter(iCol As Integer) As String
Dim iAlpha As Integer
Dim iRemainder As Integer
iAlpha = Int(iCol / 27)
iRemainder = iCol - (iAlpha * 26)
If iAlpha > 0 Then
ConvertToLetter = Chr(iAlpha + 64)
End If
If iRemainder > 0 Then
ConvertToLetter = ConvertToLetter & Chr(iRemainder + 64)
End If
End Function
Source: How to convert Excel column numbers into alphabetical characters
APPLIES TO
Microsoft Office Excel 2007
Microsoft Excel 2002 Standard Edition
Microsoft Excel 2000 Standard Edition
Microsoft Excel 97 Standard Edition
This is a function based on #DamienFennelly's answer above. If you give me a thumbs up, give him a thumbs up too! :P
Function outColLetterFromNumber(iCol as Integer) as String
sAddr = Cells(1, iCol).Address
aSplit = Split(sAddr, "$")
outColLetterFromNumber = aSplit(1)
End Function
There is a very simple way using Excel power: Use Range.Cells.Address property, this way:
strCol = Cells(1, lngRow).Address(xlRowRelative, xlColRelative)
This will return the address of the desired column on row 1. Take it of the 1:
strCol = Left(strCol, len(strCol) - 1)
Note that it so fast and powerful that you can return column addresses that even exists!
Substitute lngRow for the desired column number using Selection.Column property!
Here is a simple one liner that can be used.
ColumnLetter = Mid(Cells(Row, LastColA).Address, 2, 1)
It will only work for a 1 letter column designation, but it is nice for simple cases. If you need it to work for exclusively 2 letter designations, then you could use the following:
ColumnLetter = Mid(Cells(Row, LastColA).Address, 2, 2)
This will work regardless of what column inside your one code line for cell thats located in row X, in column Y:
Mid(Cells(X,Y).Address, 2, instr(2,Cells(X,Y).Address,"$")-2)
If you have a cell with unique defined name "Cellname":
Mid(Cells(1,val(range("Cellname").Column)).Address, 2, instr(2,Cells(1,val(range("Cellname").Column)).Address,"$")-2)
So I'm late to the party here, but I want to contribute another answer that no one else has addressed yet that doesn't involve arrays. You can do it with simple string manipulation.
Function ColLetter(Col_Index As Long) As String
Dim ColumnLetter As String
'Prevent errors; if you get back a number when expecting a letter,
' you know you did something wrong.
If Col_Index <= 0 Or Col_Index >= 16384 Then
ColLetter = 0
Exit Function
End If
ColumnLetter = ThisWorkbook.Sheets(1).Cells(1, Col_Index).Address 'Address in $A$1 format
ColumnLetter = Mid(ColumnLetter, 2, InStr(2, ColumnLetter, "$") - 2) 'Extracts just the letter
ColLetter = ColumnLetter
End Sub
After you have the input in the format $A$1, use the Mid function, start at position 2 to account for the first $, then you find where the second $ appears in the string using InStr, and then subtract 2 off to account for that starting position.
This gives you the benefit of being adaptable for the whole range of possible columns. Therefore, ColLetter(1) gives back "A", and ColLetter(16384) gives back "XFD", which is the last possible column for my Excel version.
Easy way to get the column name
Sub column()
cell=cells(1,1)
column = Replace(cell.Address(False, False), cell.Row, "")
msgbox column
End Sub
I hope it helps =)
The solution from brettdj works fantastically, but if you are coming across this as a potential solution for the same reason I was, I thought that I would offer my alternative solution.
The problem I was having was scrolling to a specific column based on the output of a MATCH() function. Instead of converting the column number to its column letter parallel, I chose to temporarily toggle the reference style from A1 to R1C1. This way I could just scroll to the column number without having to muck with a VBA function. To easily toggle between the two reference styles, you can use this VBA code:
Sub toggle_reference_style()
If Application.ReferenceStyle = xlR1C1 Then
Application.ReferenceStyle = xlA1
Else
Application.ReferenceStyle = xlR1C1
End If
End Sub
Furthering on brettdj answer, here is to make the input of column number optional. If the column number input is omitted, the function returns the column letter of the cell that calls to the function. I know this can also be achieved using merely ColumnLetter(COLUMN()), but i thought it'd be nice if it can cleverly understand so.
Public Function ColumnLetter(Optional ColumnNumber As Long = 0) As String
If ColumnNumber = 0 Then
ColumnLetter = Split(Application.Caller.Address(True, False, xlA1), "$")(0)
Else
ColumnLetter = Split(Cells(1, ColumnNumber).Address(True, False, xlA1), "$")(0)
End If
End Function
The trade off of this function is that it would be very very slightly slower than brettdj's answer because of the IF test. But this could be felt if the function is repeatedly used for very large amount of times.
Here is a late answer, just for simplistic approach using Int() and If in case of 1-3 character columns:
Function outColLetterFromNumber(i As Integer) As String
If i < 27 Then 'one-letter
col = Chr(64 + i)
ElseIf i < 677 Then 'two-letter
col = Chr(64 + Int(i / 26)) & Chr(64 + i - (Int(i / 26) * 26))
Else 'three-letter
col = Chr(64 + Int(i / 676)) & Chr(64 + Int(i - Int(i / 676) * 676) / 26)) & Chr(64 + i - (Int(i - Int(i / 676) * 676) / 26) * 26))
End If
outColLetterFromNumber = col
End Function
Function fColLetter(iCol As Integer) As String
On Error GoTo errLabel
fColLetter = Split(Columns(lngCol).Address(, False), ":")(1)
Exit Function
errLabel:
fColLetter = "%ERR%"
End Function
Here, a simple function in Pascal (Delphi).
function GetColLetterFromNum(Sheet : Variant; Col : Integer) : String;
begin
Result := Sheet.Columns[Col].Address; // from Col=100 --> '$CV:$CV'
Result := Copy(Result, 2, Pos(':', Result) - 2);
end;
This formula will give the column based on a range (i.e., A1), where range is a single cell. If a multi-cell range is given it will return the top-left cell. Note, both cell references must be the same:
MID(CELL("address",A1),2,SEARCH("$",CELL("address",A1),2)-2)
How it works:
CELL("property","range") returns a specific value of the range depending on the property used. In this case the cell address.
The address property returns a value $[col]$[row], i.e. A1 -> $A$1.
The MID function parses out the column value between the $ symbols.
Sub GiveAddress()
Dim Chara As String
Chara = ""
Dim Num As Integer
Dim ColNum As Long
ColNum = InputBox("Input the column number")
Do
If ColNum < 27 Then
Chara = Chr(ColNum + 64) & Chara
Exit Do
Else
Num = ColNum / 26
If (Num * 26) > ColNum Then Num = Num - 1
If (Num * 26) = ColNum Then Num = ((ColNum - 1) / 26) - 1
Chara = Chr((ColNum - (26 * Num)) + 64) & Chara
ColNum = Num
End If
Loop
MsgBox "Address is '" & Chara & "'."
End Sub
Column letter from column number can be extracted using formula by following steps
1. Calculate the column address using ADDRESS formula
2. Extract the column letter using MID and FIND function
Example:
1. ADDRESS(1000,1000,1)
results $ALL$1000
2. =MID(F15,2,FIND("$",F15,2)-2)
results ALL asuming F15 contains result of step 1
In one go we can write
MID(ADDRESS(1000,1000,1),2,FIND("$",ADDRESS(1000,1000,1),2)-2)
this is only for REFEDIT ... generaly use uphere code
shortly version... easy to be read and understood /
it use poz of $
Private Sub RefEdit1_Change()
Me.Label1.Caption = NOtoLETTER(RefEdit1.Value) ' you may assign to a variable var=....'
End Sub
Function NOtoLETTER(REFedit)
Dim First As Long, Second As Long
First = InStr(REFedit, "$") 'first poz of $
Second = InStr(First + 1, REFedit, "$") 'second poz of $
NOtoLETTER = Mid(REFedit, First + 1, Second - First - 1) 'extract COLUMN LETTER
End Function
Cap A is 65 so:
MsgBox Chr(ActiveCell.Column + 64)
Found in: http://www.vbaexpress.com/forum/showthread.php?6103-Solved-get-column-letter
what about just converting to the ascii number and using Chr() to convert back to a letter?
col_letter = Chr(Selection.Column + 96)
Here's another way:
{
Sub find_test2()
alpha_col = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,W,Z"
MsgBox Split(alpha_col, ",")(ActiveCell.Column - 1)
End Sub
}
I am new at excel VB and seek assistant in the following problem below:
I have a column A with following values below:
column A
"VL50s"
"M50s"
"H50s"
"VL50s"
"H50s"
I would like to extract the numbers and run the following arithmetic function below into coloumn B.
key:
x is a number
VLx --> (x) + 1
Mx -->(x) + 2
Hx --> (x) + 3
the output should look like the following using the key above:
coloumn B
51
52
53
51
53
I would like to ask how would i go about doing this function in VBA. Thank you for your assistance.
Because you say the number of letter/number combos is much greater than in your example I think this is a problem for VBA and not a worksheet function. A WS function would become to hard to maintain and to beastly very quickly.
I made these 4 functions. The GetCharArray function parses the text of the string you pass it to return that text as an array of characters (even though BA doesn't have a char type just a string type so I am returning a string. Same idea)
Then given that we can call GetNumberFromChars to get the 50 from VL50s and call GetLeftMostLetters to get the VL from VL50s.
Then is some worksheet I made a named range called keys where column 1 of the range is letters like "VL", "H", "M" ... and the corresponding value associated with it is in column 2. It would look like
Col1 Col2
VL 1
M 2
H 3
... ...
We can use the vlookup worksheet function with the Range("keys") and the result of GetLeftMostLetters to find the number that should be added to the result of GetNumberFromChars.
Function GetNewNumber(inString As String) As Double
Dim searchString As String, numberToAddFromKeys As Double, numberToAddToFromCell As Long, cellChars() As String
cellChars = GetCharArray(inString)
searchString = GetLeftMostLetters(cellChars)
numberToAddToFromCell = GetNumberFromChars(cellChars)
'use the keys named range where column 1 is your letters ("VL","H"...)
'and column 2 is the corresponding value for that letter set
numberToAddFromKeys = WorksheetFunction.VLookup(searchString, Range("keys"), 2, 0)
GetNewNumber = CDbl(numberToAddFromKeys) + CDbl(numberToAddToFromCell)
End Function
Function GetNumberFromChars(inChars() As String) As Long
Dim returnNumber As String, i As Long, numberStarted As Boolean
For i = 1 To UBound(inChars)
If IsNumeric(inChars(i)) Then
If Not numberStarted Then numberStarted = True
returnNumber = returnNumber & inChars(i)
Else
If numberStarted Then
'this will ignore that "s" on the end of your sample data
'hopefully that's what you need
GetNumberFromChars = returnNumber
Exit Function
End If
End If
Next
End Function
Function GetLeftMostLetters(inChars() As String) As String
Dim returnString As String, i As Long
For i = 1 To UBound(inChars)
If Not IsNumeric(inChars(i)) Then
returnString = returnString & inChars(i)
Else
GetLeftMostLetters = returnString
End If
Next
End Function
Function GetCharArray(inText As String) As String()
Dim s() As String, i As Long
ReDim s(1 To Len(inText))
For i = 1 To UBound(s)
s(i) = Mid$(inText, i, 1)
Next
GetCharArray = s
End Function
So it can be used as such...
Dim cell As Range, rng As Range
'set this range to your actual range.
Set rng = Sheets("your sheet name").Range("A1:A5")
For Each cell In rng
'put this resulting value wherever you want.
Debug.Print GetNewNumber(cell.Value)
Next cell
You don't even have to use VBA for that, you can use a (very ugly) formula to determine this:
=SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(A1, "VL",""), "M",""), "H", ""),
"s", "") + IF(LEFT(A1, 2) = "VL", 1, IF(LEFT(A1, 1) = "M", 2,
IF(LEFT(A1,1) = "H", 3, 0)))
In reality this formula should be on one line, but I've broken it up here so that it's readable. Place the formula in cell B1, and then copy it down to any other cells you need. It strips out all instances of "VL", "M", "H" and "s", and then adds the extra number based on the left 1 or 2 characters of the A cell.
This will return the first number found in the input value:
Function GetNumber(val)
Dim re As Object
Dim allMatches
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "(\d+)"
re.ignorecase = True
re.Global = True
Set allMatches = re.Execute(val)
If allMatches.Count > 0 Then
GetNumber = allMatches(0)
Else
GetNumber = ""
End If
End Function
EDIT: just noticed your question title says "decimal" numbers - will your values have any decimal places, or all they all whole numbers?