I have a string array, and in each slot of the array is a 'range of columns' in a string format e.g "B:J" "k:W" "AC:AG"
The method is being passed a string which is a column name e.g "C"
I need to search see if "C" is inside "B:J".
So basically it needs to check to see if "C" is in "BCDEFGHIJ" which it is and if it is break from a loop
But if I input "A" it should then go to the next slot in the array.
Thank
This function will return the position in the array where the single column is included in the range. It uses the Intersect property to determine if the two ranges intersect.
Function ArrayPos(sColLetter As String, vaRanges As Variant) As Long
Dim i As Long
Dim sh As Worksheet
Dim lReturn As Long
Set sh = Sheet1
For i = LBound(vaRanges) To UBound(vaRanges)
If Not Intersect(sh.Columns(sColLetter), sh.Columns(vaRanges(i))) Is Nothing Then
lReturn = i
Exit For
End If
Next i
ArrayPos = lReturn
End Function
It's used like this, from the Immediate Window for example
?arraypos("M",array("B:J","K:W"))
1
You would want to add some error checking to make sure the arguments can be converted to ranges, but I'll leave that to you. You could do this without Range objects by splitting the range string on colon and comparing the ASCII values of the letters.
You might take a look at
How to convert a column number (eg. 127) into an excel column (eg. AA)
sheet.Application.ActiveCell.Column //for current selected column
int col=sheet.Range("A").Column; // for any choosen column
returns current column number, just apply the same for your range B:J, check if J (10) > C (3) > B ( 2)
Then you can say that it is inside that range.
In case of need the other way around this function with the link I gave you.
ConvertToAlphaColumnReferenceFromInteger(sheet.Application.ActiveCell.Column)
Done for C#/.NET
Related
I have a string that can take a couple of values (e.g. Madison, Chicago,...) and a couple of ranges named (Madison_Range, Chicago_Range,...)
Depending on the input of the user (more specifically, he drops an 'x' in a certain cell), the string takes on a certain value and I want the corresponding (hidden) range to be shown.
However,
Dim X as string
Dim Combination as string
x = Range("ACity:ZCity").Find("x").Name.Name
Combination= x & "_Range"
Range("Combination").EntireRow.Hidden
Does not do the trick, probably because vba looks for the named range "combination". Any ideas how to procede? I know that there is possibly a more elegant case, but this is a quick fix solution as part of a wide problem.
Dim X As String
Dim target As Range
X = Range(Range("Acity"),Range("zcity")).Find("x").Name.Name
'this is potentially dodgy - you assume that the find will find an x - if it doesn't it will error. Also you need to refer to each name with a separate Range function to turn it into a cell reference and then an external range function to turn those two cell references into a full range.
Set target = Range(X & "_Range") 'point to the range
'to hide
'target.EntireColumn.Hidden = True
'to unhide
target.EntireColumn.Hidden = False
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
I'm trying to write a code, I believe it needs to be a function. I want to select the first row in a column and it will create a range with all the rows below it (until empty cell). I think the problem is the syntax for the 'Range'.
(Ultimately my goal is to be ably to select two cells in two different columns, automatically create 2 ranges and plot them against each other in a scatter plot. but I'm struggling with just the first part)
My code looks something like:
`
Function rng1(x as variant)
Dim ji as string, jf as string
Dim rng1 as range
ji=x.Address
jf=x.End(xldown).Address
rng1=Range(ji:jf)
'
I removed the variable declaration for "rng1" since you can't name a variable the same as your function. You simply need to concatenate your strings together. You also have to set the function since a range is an object type and not a primitive.
Function rng1(x as variant) as Range
Dim ji as string, jf as string
ji=x.Address
if x.offset(1,0) <> "" then
jf=x.End(xldown).Address
else
jf=ji
end if
set rng1=Range(ji & ":" & jf)
End Function
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")))
I need to extract the data from an excel worksheet to an array that will be used in an application that uses VBScript as scripting language (Quick Test Professional). We can use the following code for that:
' ws must be an object of type Worksheet
Public Function GetArrayFromWorksheet(byref ws)
GetArrayFromWorksheet = ws.UsedRange.Value
End Function
myArray = GetArrayFromWorksheet(myWorksheet)
MsgBox "The value of cell C2 = " & myArray(2, 3)
All nice and well, but unfortunately the array that gets returned does not only contain the literal text strings, but also primitives of type date, integer, double etc. It happened multiple times that that data got transformed.
[edit] Example: when entering =NOW() in a cell and set the cell formatting to hh:mm makes the displayed value 17:45, the above method retuns a variable of type double and a value like 41194.7400990741
The following solution worked better: I can get the literal text from a cell by using the .Text property, but they only work on one cell and not on a range of cells. I cannot do this at once for an array as I could with the .Value property, so I have to fill the array one cell at a time:
Public Function GetArrayFromWorksheet_2(byref ws)
Dim range, myArr(), row, col
Set range = ws.UsedRange
' build a new array with the row / column count as upperbound
ReDim myArr(range.rows.count, range.columns.count)
For row = 1 to range.rows.count
For col = 1 to range.columns.count
myArr(row, col) = range.cells(row, col).text
Next
Next
GetArrayFromWorksheet_2 = myArr
End Function
But ouch... a nested for loop. And yes, on big worksheets there is a significant performance drop noticable.
Does somebody know a better way to do this?
As we covered in the comments, in order to avoid the issue you will need to loop through the array at some point. However, I am posting this because it may give you a significant speed boost depending on the type of data on your worksheet. With 200 cells half being numeric, this was about 38% faster. With 600 cells with the same ratio the improvement was 41%.
By looping through the array itself, and only retrieving the .Text for values interpreted as doubles (numeric), you can see speed improvement if there is a significant amount of non-double data. This will not check .Text for cells with Text, dates formatted as dates, or blank cells.
Public Function GetArrayFromWorksheet_3(ByRef ws)
Dim range, myArr, row, col
Set range = ws.UsedRange
'Copy the values of the range to temporary array
myArr = range
'Confirm that an array was returned.
'Value will not be an array if the used range is only 1 cells
If IsArray(myArr) Then
For row = 1 To range.Rows.Count
For col = 1 To range.Columns.Count
'Make sure array value is not empty and is numeric
If Not IsEmpty(myArr(row, col)) And _
IsNumeric(myArr(row, col)) Then
'Replace numeric value with a string of the text.
myArr(row, col) = range.Cells(row, col).Text
End If
Next
Next
Else
'Change myArr into an array so you still return an array.
Dim tempArr(1 To 1, 1 To 1)
tempArr(1, 1) = myArr
myArr = tempArr
End If
GetArrayFromWorksheet_3 = myArr
End Function
Copy your worksheet into a new worksheet.
Copy Paste values to remove formulas
Do a text to columns for each column, turning each column into Text
Load your array as you were initially doing
Delete the new worksheet
You cant do this quickly and easily without looping through the worksheet.
If you use the technique above with 2 lines of code it must a variant type array.
I've included a real example from my code that does it in 6 lines because I like to A) work with the worksheet object and B) keep a variable handy with the original last row.
Dim wsKeyword As Worksheet
Set wsKeyword = Sheets("Keywords")
Dim iLastKeywordRow As Long
iLastKeywordRow = wsKeyword.Range("A" & wsKeyword.Rows.Count).End(xlUp).Row
Dim strKeywordsArray As Variant
strKeywordsArray = wsKeyword.Range("A1:N" & iLastKeywordRow).Value
Note your array MUST be a variant to be used this way.
The reason that Variants work like this is that when you create an array of variants, each 'cell' in the array is set to a variant type. Each cell then get's it's variant type set to whatever kind of value is assigned to it. So a variant being assigned a string gets set to variant.string and can now only be used as a string. In your original example it looks like you had time values which were kind of stored as variant.time instead of variant.string.
There are two ways you can approach your original problem
1) loop through and do the process with more control, like the double nested for loop. explained in another answer which gives you complete control
2) store all the data in the array as is and then either re-format it into a second array, or format it as desired text as you use it (both should be faster)