Get the number of column range from a range in Excel VBA - excel

I link Get a first column range from a range in Excel VBA to undestand the content.
My question is:
is it possible to know the number of the column if I know the name of the column? for example: I would like know if the column named "mycolumn" is the numer 1 or 2 or 3 or x... in the table named "mytable"
Regards

Range("mycolumn").cells(1,1).column will give you the number of the first column in a range called "mycolumn"
EDIT: My Bad - I didn't see this was a named column in a table - Axel's answer in comments is correct.

... if the column named "mycolumn" is the numer 1 or 2 or 3 or x... in the table named "mytable".
To find the ordinal position of a column within its parent table, you need to look only at the range of the table, not te worksheet that the table resides on.
On the worksheet,
=MATCH("mycolumn", mytable[#Headers], 0)
In the following image, the above formula returns 3.
In VBA as a function,
Option Explicit
Sub main()
Debug.Print ColumnNdx("sheet2", "mytable", "mycolumn")
End Sub
Function ColumnNdx(wsnm as string, tblnm As String, colnm As String)
Dim m As Variant, hdrrng As Range
ColumnNdx = CVErr(xlErrNA)
Set hdrrng = ThisWorkbook.Worksheets(wsnm ).ListObjects(tblnm).HeaderRowRange
m = Application.Match(colnm, hdrrng, 0)
If Not IsError(m) Then ColumnNdx = CLng(m)
End Function
The above function returns 3.

Related

Get count of text of cell in several sheets in excel using VLOOKUP

I have a workbook with several sheets.
In the range from B20:B25 I have some values from a dropdown list. In the left column when that values are wrote, the workbook automatically has to set the many times of that value has selected in all sheets simultaneously.
For example if in the sheet 2 I write the text "TEXT" in cell B22, the workbook has to look up in the last sheets if the text "TEXT" exists ONLY at range B20:B25. If this text was found, so the workbook has to add 1 to the left cell of value "TEXT".
The workbook can have to 60 sheets. And for example the "TEXT" could be wrote at first time in the sheet 55, and its left number must be 1. If the "TEXT" is write again in the sheet 56, the value at its left must be 2.
So the column A must be a counter of times that value in column B is wrote.
I tried with CELL, ADDRESS and MATCH but I had not lucky.
Does exists in VBA a formula that I can create something to count such this?
This might not be the most elegant way to do this but it seems to work:
Option Explicit
Function CountThroughSheet(R1 As Range, R2 As Range)
Application.Volatile
CountThroughSheet = 0
Dim TrgAdd As String, i As Integer, R3 As Range
TrgAdd = R1.Address
For i = 1 To Application.Caller.Worksheet.Index
For Each R3 In Sheets(i).Range(TrgAdd)
If R3 = R2 Then CountThroughSheet = CountThroughSheet + 1
Next R3
Next i
End Function
To be placed in a module.
Function is used as follow:
Note that where ever you place the formula, the first argument should be as large as the largest range in previous sheet index. Meaning that if your list decrease in size, you'll need to take blank rows to match previous lists positions.

Excel: Replace text based on a substring of the text

Column D contains the substring to look for, which could be anywhere in the search field of column A. Column E contains the value to replace the content of column A with. See the results in Column B. Can anyone help me out?
After implementing suggested solution
If you are fine with it, I suggest a modification to the arrangement.
Arrange the lookup values as in the image, in columns F, G and H and the replace values in the next corresponding row. You would need 3 helper columns, (since you have 3 lookup values). Column B, C and D are helper columns.
Enter the below formula in column B, B1,
=IFERROR(IF(MATCH("*"&F$1&"*",$A1,0),F$2),"")
Just drag it to the bottom throughout the range, and then drag it to right upto column E.
Column F is your actual output. Enter the formula in column F,
=B1&C1&D1
and drag it down. Hope this helps. Let me know if you need anything else.
This works, but is not very elegant and won't easily scale.
=CONCATENATE(REPT($E$1,COUNTIF(A1,"*"&$D$1&"*")),REPT($E$2,COUNTIF(A1,"*"&$D$2&"*")),REPT($E$3,COUNTIF(A1,"*"&$D$3&"*")))
It's series of REPT(Replacement,COUNTIF(Values,*Labels*)). The *'s around the search allow it to be wildcard, essentially making the COUNTIF's flags of if the element is included or not. We then repeat the replacement value the number of times the countif is present (which in the example will always be 1), and then concatenate the results.
I have created a VB script to solve this:
Sub DetermineFeatures()
Dim featureLabel As String
Dim testString As String
testString = "FEAT_EMV_L4, Team_1, Team_IBM"
Dim stringValue As Variant
Dim lookupRange As Range
Set lookupRange = Worksheets("LookupTable").Range("A1:B100")
Dim outputWorksheet As Worksheet
Set outputWorksheet = Worksheets("Output")
Dim feature As String
Dim rowIndex As Long
For Each cell In Worksheets("Original").Columns("O").Cells
rowIndex = cell.Row
cellValue = WorksheetFunction.Trim(cell.Value)
For Each stringValue In Split(cellValue, ",")
If InStr(stringValue, "LABEL_") Then
featureLabel = Trim(stringValue)
' Call the Vlookup function to look up the actual feature name
for the supplied label. This
' returns the value from the second column of the range within
the LookupTable worksheet.
feature = Application.WorksheetFunction.VLookup(featureLabel,
lookupRange, 2, False)
outputWorksheet.Cells(rowIndex, 1) = feature
If Not IsEmpty(featureLabel) Then Exit For
End If
Next
Next cell
End Sub

Dynamic Summing Range

Currently I have a medical spread-sheet with a list of clients that we have serviced. We have 8 different clinical categories which are denoted by different acronyms - HV,SV,CV,WV,CC,OV,TS and GS.
A client can receive multiple therapies i.e. HV,SV,CV - in the background we have a counter mechanism which would increment each of these records by 1.The formula used for this counter is:
=(LEN('Parent Sheet'!F25)-LEN(SUBSTITUTE('Parent Sheet'!F25,'Parent Sheet'!$P$4,"")))/LEN('Parent Sheet'!$P$4)
At the bottom of the sheet we then have a sum which ads up all the treatments that occurred for that week.
Now the tricky part about this is that we have almost a year's worth of data in this sheet but the summing formulas are set as: SUM(COLUMN 6: COLUMN 53) but due to a need to increase the entries beyond this limit, we have to adjust the sum formula. We have 300 SUM Formulas adding up each of the 8 Criteria items and assigning them to the HV,SV,SC,WV etc. counters.
Would we have to adjust this manually one by one or is there a easier way of doing this?
Thank you very much!
To me, I think you should change the sheet layout a little, create a User Defined Function (UDF) and alter the formulas in your Sum rows for efficient row/column adding (to make use of Excel's formula fill). The only issue is that you need to save this as a Macro-Enabled file.
What you need to change in the formulas is to utilize $ to restrict changes in column and rows when the formula fill takes place.
To illustrate in an example, consider:
Assuming the first data starts at row 6, and no more than row 15 (you can use the idea of another data gap on the top). Alter the Sum row titles to begin with the abbreviation then create a UDF like below:
Option Explicit
' The oRngType refers to a cell where the abbreviation is stored
' The oRngCount refers to cells that the abbreviation is to be counted
' Say "HV" is stored in $C16, and the cells to count for HV is D$6:D$15,
' then the sum of HV for that date (D16) is calculated by formula
' `=CountType($C16, D$6:D$15)`
Function CountType(ByRef oRngType As Range, ByRef oRngCount) As Long
Dim oRngVal As Variant, oVal As Variant, oTmp As Variant, sLookFor As String, count As Long
sLookFor = Left(oRngType.Value, 2)
oRngVal = oRngCount.Value ' Load all the values onto memory
count = 0
For Each oVal In oRngVal
If Not IsEmpty(oVal) Then
For Each oTmp In Split(oVal, ",")
If InStr(1, oTmp, sLookFor, vbTextCompare) > 0 Then count = count + 1
Next
End If
Next
CountType = count
End Function
Formulas in the sheet:
Columns to sum are fixed to rows 6 to 15 and Type to lookup is fixed to Column C
D16 | =CountType($C16,D$6:D$15)
D17 | =CountType($C17,D$6:D$15)
...
E16 | =CountType($C16,E$6:E$15)
E17 | =CountType($C17,E$6:E$15)
The way I created the UDF is to lookup and count appearances of a cell value (first argument) within a range of cells (second argument). So you can use it to count a type of treatment for a big range of cells (column G).
Now if you add many columns after F, you just need to use the AutoFill and the appropriate rows and columns will be there.
You can also create another VBA Sub to add rows and columns and formulas for you, but that's a different question.
It's isn't a great idea to have 300 sum formulas.
Name your data range and include that inside the SUM formula. So each time the NAMED data range expands, the sum gets calculated based on that. Here's how to create a dynamic named rnage.
Sorry I just saw your comment. Following is a simple/crude VBA snippet.
Range("B3:F12") is rangeValue; Range("C18") is rngTotal.
Option Explicit
Sub SumAll()
Dim WS As Worksheet
Dim rngSum As Range
Dim rngData As Range
Dim rowCount As Integer
Dim colCount As Integer
Dim i As Integer
Dim varSum As Variant
'assuming that your said mechanism increases the data range by 1 row
Set WS = ThisWorkbook.Sheets("Sheet2")
Set rngData = WS.Range("valueRange")
Set rngSum = WS.Range("rngTotal")
colCount = rngData.Columns.Count
'to take the newly added row (by your internal mechanism) into consideration
rowCount = rngData.Rows.Count + 1
ReDim varSum(0 To colCount)
For i = 0 To UBound(varSum, 1)
varSum(i) = Application.Sum(rngData.Resize(rowCount, 1).Offset(, i))
Next i
'transpose variant array with totals to sheet range
rngSum.Resize(colCount, 1).Value = Application.Transpose(varSum)
'release objects in the memory
Set rngSum = Nothing
Set rngData = Nothing
Set WS = Nothing
Set varSum = Nothing
End Sub
Screen:
You can use named ranges as suggested by bonCodigo or you could use find and replace or you can insert the columns within the data range and Excel will update the formula for you automatically.

Excel VBA find a range of same values in a column

I need to write a macro that will find the cell range based on a value. A column will have the same value in a row, I need to find out what is the first and last column that has the same value in a row.
So the macro needs to find that "Jill Cross" range is a4 to a9
So far I don't have much, got a way to find the first occurrence of a value
Function GetFirstCell(CellRef As Range)
Dim l As Long
l = Application.WorksheetFunction.Match(CellRef.Value, Range("A1:A10000"), 0)
GetFirstCell = l
End Function
Now I need to loop through the next rows somehow to return the last row of an occurrence
If you have your first cell in a sorted list, a countif function will give you the last cell easily.
Function GetFirstCell(CellRef As Range) as long
Dim l As Long
l = Application.WorksheetFunction.Match(CellRef.Value, Range("A1:A10000"), 0)
GetFirstCell = l
End Function
function GetLastCell(cellRef as range, lFirstCell as long)
Dim l As Long
l = Application.WorksheetFunction.countif(Range("A1:A10000"), CellRef.Value)
GetLastCell = lFirstCell+l-1
End Function
This will work though it has its limitations (for example if the names aren't sorted and the name you are looking for is separated across multiple places in your range...). Of course you need to replace the ranges with those you want to check and "bob" with whatever name you are looking for. Also, the column is static so you may want to alter that part. This is an array formula so you will need to press [ctrl]+[shift]+[enter] in the formula bar to execute it.
="A"&MIN(IF(ROW(A1:A6)*(A1:A6="bob")=0, 99999999, ROW(A1:A6)*(A1:A6="bob")))&":A"&MAX(SI(ROW(A1:A6)*(A1:A6="bob")=0, -99999999, ROW(A1:A6)*(A1:A6="bob")))

referencing a cell in other worksheet. DYNAMIC/VARIABLE

i have got a question about referencing a cell in other worksheet.
I have got this code in VBA:
If Application.CountIf(.Rows(Lrow), '8000')= 0 And Application.CountIf(.Rows(Lrow), '9000')) = 0 Then .Rows(Lrow).Delete
Which is capable for me to delete any row WITHOUT words 8000 and 9000.
However, since there would be future update, how can i adjust the code so that the value for excel to execute can be "DYNAMIC" (i.e. not hard-cored)
Say, if i enter a number at cell (17,2) in SHEET 1, what can i make excel to look at the cell address instead of the "absolute value" from SHEET 2 by VBA?
Thanks.
You could add these couple of lines above the code you already have. This is assuming that you want to change either number by entering them into cell B17 or B18.
Dim lowerBound as Long, upperBound as Long
lowerBound = Sheet1.Range("B17").Value
upperBound = Sheet1.Range("B18").Value
Then replace '8000' in with lowerBound and '9000' with upperBound.
You can add a column X to your source sheet, headed "Condx for DELETE" and fill this column with a formula representing the DELETE condition as a Boolean value - like in your case: =NOT(OR(A2=8000,A2=9000)) (asuming values 8000, 9000, etc. appear in column A)
Then you cycle thru the source table and delete all records for which column X is TRUE. Asuming that
your source has been defined within VBA as a range object (MyRange)
the first row is the header row
no empty cells in first column between header and end of list
the condition is found in 3rd column
a purger could look like this
Sub MySub()
Dim MyRange As Range, Idx As Integer, MyCondx As Integer
Set MyRange = ActiveSheet.[A1] ' represent source data as a range
Idx = 2 ' asuming one header row
MyCondx = 3 ' asuming condition is in 3rd column
Do While MyRange(Idx, 1) <> ""
If MyRange(Idx, MyCondx) Then
MyRange(Idx, 3).EntireRow.Delete
Else
Idx = Idx + 1
End If
Loop
End Sub
This way you have full transparency, no hardcoding and you are very flexible to not only change a set of 2 values but specify whatever condition serves the business.
Hope that helps - good luck

Resources