Matching rows from another sheet - excel

I am attempting to copy results from another sheet based on the cell values on the active worksheet. i.e loop through every element in array "GWworkStations()" and find a match in column B of "Col List" sheet, and then copy the corresponding values in "C:E" to an array "MatchedEntries" so I can copy them back to the active sheet.
The code is returning empty for "matchedRow", instead of reporting the row number. I am not getting an error.
dim MatchedEntries() as string
dim GWworkStations() as variant
number_of_rows = ActiveSheet.UsedRange.Rows.Count
With ActiveWorkbook.Worksheets("New Sheet")
GWworkStations() = range("B2:B" & number_of_rows)
End With
ReDim MatchedEntries(1 To r) 'Size the array to hold the results.
'for every cell that is not empty in GWworkStations(), search through column B of 'Col List ' sheet.
For i = 1 To number_of_rows
'matchedRow = Empty
On Error Resume Next 'Keep running if Excel MATCH function below doesn't find a match.
If Not IsEmpty(Cells(i, 1)) Then
matchedRow = Application.WorksheetFunction.Match(GWworkStations(i, 1), range("Col List!B:B"), 0)
If matchedRow = Empty Then Debug.Print "Empty " & matchedRow
If IsEmpty(matchedRow) Then 'No match.
MatchedEntries(i, 1) = "" 'GWworkStations(i, 1)
Else
'If GWworkStations(i, 1) = GWworkStations(i - 1) Then
If IsNumeric(matchedRow) Then 'Match was found.
MatchedEntries(i, 1) = Application.WorksheetFunction.Index(range("List!C:E"), matchedRow, 1)
Else 'MATCH function returned a non-numeric result.
MatchedEntries(i, 1) = ""
End If 'IsNumeric(MatchedRow)
End If 'IsEmpty(MatchedRow)
Else
End If
Next i
range("E2:G" & number_of_rows) = MatchedEntries() 'Write the tag name results out to range E:G.

Excel doesn't like the space in the sheet name. You can fix this by using single quotes: Range("'Col List'!B:B"), or by replacing Range("Col List!B:B")with Sheets("Col List").Columns(2).
You could also use the Range.Find method (which I would prefer):
matchedRow = Sheets("Sheet 3").Columns(2).Find(str).Row

Related

INDEX and AGGREGATE Function in Excel VBA

I'm trying to move an in cell formula to VBA, because otherwise it's always recalculating, even when I deactivate the excel option, it comes back when I reopen the file. That's why I want to move that formula to VBA, where it happens only when I press a button, which is much smarter.
I have a master table with data, which I aggregate and index and express it on another sheet in a table. -> column A to S are in the master table, in the aggregated table, I will only have column A,C,E,G,H,I,J,K,L,M and P
The formula I want to move to VBA is the following:
=IFERROR(INDEX(Endkontrolle!$A:$S;AGGREGATE(15;6;ROW(Endkontrolle!$A:$S)/((FIND($B$3;Endkontrolle!$F:$F;1)>0)*(Endkontrolle!$S:$S="x"));ROW()-32)-0;1);"")
Can somebody help me translate that formula to VBA script?
thank you very much
Try this code:
Sub Button1_Click2()
'Declarations.
Dim RngTable As Range
Dim RngTarget As Range
Dim StrColumnsIndex As String
'A string is used to stores the index of the columns to be copied.
StrColumnsIndex = "1;3;5;7;8;9;10;11;14;15;16"
'RngTable is set as the range that will host the aggregated table.
Set RngTable = Sheets("Aggregated sheet").Range("A33:K34") '< EDIT THIS LINE ACCORDGLY TO YOU NEED
'Clearing RngTable.
RngTable.ClearContents
'Checking if StrColumnsIndex and RngTable are compatible.
If UBound(Split(StrColumnsIndex, ";")) + 1 <> RngTable.Columns.Count Then
MsgBox "The number of columns requested via StrColumnsIndex and the number of columns avaiable in RngTable do not match. Redefine the variables properly. The aggregated table will not be updated.", vbCritical + vbOKOnly, "Variable mismatch"
Exit Sub
End If
'Covering each cell in RngTable.
For Each RngTarget In RngTable
'The result is reported in each cell. The [row] element of the INDEX is obtained by subtracting _
RngTable.Row from the RngTarget.Row and adding one. This way each row is properly reported. The _
[col] element of the INDEX is obrained by splitting StrColumnsIndex using the difference between _
the RngTarget.Column and RngTable.Column as index. This way each requested column as listed in _
StrColumnsIndex is reported.
'RngTarget.Formula = "=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x""))," & RngTarget.Row - RngTable.Row + 1 & ")-0," & Split(StrColumnsIndex, ";")(RngTarget.Column - RngTable.Column) * 1 & "),"""")"
RngTarget.Value = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x""))," & RngTarget.Row - RngTable.Row + 1 & ")-0," & Split(StrColumnsIndex, ";")(RngTarget.Column - RngTable.Column) * 1 & "),"""")")
'If RngTarget contains nothing then it's assumed there are no more results to be reported and the macro is terminated.
If RngTarget.Value = "" Then Exit Sub
Next
End Sub
Thanks for that. I implemented it and it works for 1 row. If I want to add the next data set from the main table, that does only repeat the content from previous row. How can I achieve, that it lists me more than 1 line of aggregated data?
Expected result:
it picks the relevant rows of data and lists it (different data according the find criteria)
Actual result:
it picks only 1 row and repeats it for the second line
Now I defined following code:
Sub Button1_Click()
Cells(33, 1) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,1),"""")")
Cells(33, 2) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,3),"""")")
Cells(33, 3) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,5),"""")")
Cells(33, 4) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,7),"""")")
Cells(33, 5) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,8),"""")")
Cells(33, 6) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,9),"""")")
Cells(33, 7) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,10),"""")")
Cells(33, 8) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,11),"""")")
Cells(33, 9) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,14),"""")")
Cells(33, 10) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,15),"""")")
Cells(33, 11) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,16),"""")")
Cells(34, 1) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,1),"""")")
Cells(34, 2) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,3),"""")")
Cells(34, 3) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,5),"""")")
Cells(34, 4) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,7),"""")")
Cells(34, 5) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,8),"""")")
Cells(34, 6) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,9),"""")")
Cells(34, 7) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,10),"""")")
Cells(34, 8) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,11),"""")")
Cells(34, 9) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,14),"""")")
Cells(34, 10) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,15),"""")")
Cells(34, 11) = Evaluate("=IFERROR(INDEX(Endkontrolle!A:S,AGGREGATE(15,6,ROW(Endkontrolle!A:S)/((FIND(B3,Endkontrolle!F:F,1)>0)*(Endkontrolle!S:S=""x"")),ROW()-32)-0,16),"""")")
End Sub
Here's a sample of the data in the main table "Endkontrolle":
Date
Product
Employee
...
Date Range
22.04.2022
MOTI
AKAH
...
x
23.04.2022
MOTI_BG
AKAH
...
x
26.04.2022
MOTI
AKAH
...
On the reporting page, I would like to list down up to 20 rows of Data, which are in the Date Range ('x') from the "Endkontrolle" worksheet.
In the upper example, it should list row 1+2, but not 3.

Stop a macro if rows generated in a structured table repeat X number of times

I've got a workbook containing a Summary sheet and 200 numbered sheets that the user fills in one after the other.
The following macro checks about 125 cell values on every numbered sheet, and fills in the Summary, one line per numbered sheet.
If a numbered sheet hasnt been used yet, the macro fills in every column from column D to column DV with the minus sign "-" and goes on to check every numbered sheet one after the other till there's no more to check.
Is there a way to set it so that if an arbitrary number (let's say 10 lines) of the newly generated lines contain only the minus sign "-" from D to DV (Iw,4 to Iw, 126), then the macro would reach its end as it means all the remaining numbered sheets aren't used yet?
Sub SummaryMacro()
Dim Sh As Worksheet
Range("B2:L1000").ClearContents
Iw = 2 ' Index Write
For Each Sh In ActiveWorkbook.Sheets
If Sh.Name = "Summary" Then GoTo EndConsolidation
Cells(Iw, 1).Select
With Selection
.Hyperlinks.Add Anchor:=Selection, Address:="", SubAddress:="'" & Sh.Name & "'" & "!" & "A1", TextToDisplay:="Go to"
End With
Cells(Iw, 2) = Sh.Name
If Sh.Range("D8") = "" Then
Cells(Iw, 3) = "-"
Else
Cells(Iw, 3) = Sh.Range("D8")
End If
'Here the rest of the process (Iw, 4 till Iw, 125)
'The process also includes a few variations:
'Something like 20 of those with various text
If Sh.CheckBoxes("Check Box 1").Value = 1 Then Cells(Iw, 40) = "Declared" Else Cells(Iw, 40) = "-"
'Something like 30 of those with various text
If Sh.Range("H33") = "Issued" Then
Cells(Iw, 42) = "-"
Else
Cells(Iw, 42) = Sh.Range("H33")
End If
'But all in all they are mostly like that
If Sh.Range("C134") = "" Then
Cells(Iw, 126) = "-"
Else
Cells(Iw, 126) = Sh.Range("C134")
End If
Iw = Iw + 1
EndConsolidation:
Next Sh
End Sub
Try adding this code to your For loop at the end:
If (WorksheetFunction.CountIf(Range("D" & Iw & ":DV" & Iw), "-") = 123) Then
Cntr = Cntr + 1 'Blank sheet found
Else
Cntr = 0 'Not blank - Restart counter
End If
If (Cntr = 10) Then Exit For
This counts the number of - in your row and if it equals 123 (D-DV) then it increments the counter otherwise it clears the counter. When Cntr reaches 10 it exits the loop.
HTH
Add this code before your For loop ends
Dim counter As Integer
Dim previousRowBlank As Boolean
counter = 0
previousRowBlank = True
'count if all the 123 cells contain - string
If (WorksheetFunction.CountIf(Sheets("Summary").Range("D" & Iw & ":DV" & Iw), "-") = 123) Then
If (counter = 0) Then
counter = counter + 1
previousRowBlank = True
Else
If (previousRowBlank = True) Then
counter = counter + 1
End If
End If
Else
previousRowBlank = False
counter = 0
End If
'assuming you want to exit when 10 consecutive rows are blank
If (counter = 10) Then
Exit Sub
End If
When I have something like this I Dim a Boolean variable (perhaps call it isPopulated) which only gets switched to true when one of the cells has a value to act on. Then for your case after 10 (or however many you choose) lines, insert an If isPopulated = False Then Exit For to skip the remaining sheets.
EDIT; another idea I just had for you - if all the cells you're checking are supposed to have numeric values then you could use the below;
If Not WorksheetFunction.Concat(Range("D8"), Range("C134"), etc) Like "*#*" Then
'Code here to skip this and remaining sheets.
Obviously you'd need to add the relevant ranges inside the concat() brackets. What that will do is join the contents of those cells together, then check the result for any numbers "*#*" (you could also check for any letters using "*?*"). That gives you a one-code-line answer to the basic question 'is this sheet populated or not'.
I'm sure it's a bad idea to terminate the macro prematurely, based on such an imprecise criterion as the number of "empty" sheets in series. If data starts again on the 11th, 15th or 30th sheet, then you will not process it, you will lose it.
Your macro is not very complex, it shouldn't take longer than a few seconds. For modern Excel, 25K cells are very few
Your code can be shortened a little, simplified. After all, you know all the addresses of the cells that you need to check on each sheet, you enter them in the macro code sequentially, right? Write them on one line separated by commas and put them in a constant.
After that, the whole code will become much shorter:
Sub SummaryMacro()
Const REQUIRED_CELLS_ADDRESS As String = "D8,...<all other source cells>...,B6"
Const SUM_SHEETNAME As String = "Summary"
Dim ws As Worksheet
Dim wsSum As Worksheet
Dim rCell As Range
Dim oTargetCell As Range
Dim oSumCell As Range
Dim aAddress As Variant
Dim i As Integer
aAddress = Split(REQUIRED_CELLS_ADDRESS, ",")
Set wsSum = ActiveWorkbook.Worksheets(SUM_SHEETNAME)
wsSum.UsedRange.Offset(1, 0).ClearContents
Set oTargetCell = wsSum.Range("A1")
For Each ws In ActiveWorkbook.Worksheets
If ws.Name <> SUM_SHEETNAME Then
Set oTargetCell = oTargetCell.Offset(1, 0)
wsSum.Hyperlinks.Add Anchor:=oTargetCell, Address:="", SubAddress:="'" & ws.Name & "'" & "!" & "A1", TextToDisplay:="Go to"
oTargetCell.Resize(1, 123).Value = "-"
Set oSumCell = oTargetCell.Offset(0, 1)
oSumCell.Value = ws.Name
For i = LBound(aAddress) To UBound(aAddress)
Set rCell = ws.Range(aAddress(i))
Set oSumCell = oSumCell.Offset(0, 1)
If Not IsEmpty(rCell) Then oSumCell.Value2 = rCell.Value2
Next i
End If
Next ws
End Sub
Update Everyone knows that working with an array in RAM is much faster than working with sheet cells. Therefore, the outer loop - iterating over the sheets of the book - remains the same, but we change the code inside the loop in this way:
Sub SummaryMacro()
Const SUM_SHEETNAME As String = "Summary"
Dim ws As Worksheet
Dim wsSum As Worksheet
Dim oTargetCell As Range
Dim aResData As Variant
aAddress = Split(REQUIRED_CELLS_ADDRESS, ",")
Set wsSum = ActiveWorkbook.Worksheets(SUM_SHEETNAME)
wsSum.UsedRange.Offset(1, 0).ClearContents
Set oTargetCell = wsSum.Range("A1")
For Each ws In ActiveWorkbook.Worksheets
If ws.Name <> SUM_SHEETNAME Then
Set oTargetCell = oTargetCell.Offset(1, 0)
wsSum.Hyperlinks.Add Anchor:=oTargetCell, Address:="", SubAddress:="'" & ws.Name & "'" & "!" & "A1", TextToDisplay:="Go to " & ws.Name
aResData = validateData(ws.Range("A1:L140").Value2) ' Or "D8:C134" or any other
oTargetCell.Offset(0, 1).Resize(1, UBound(aResData) + 1).Value = aResData
End If
Next ws
End Sub
The main trick is hidden in this line aResData = validateData(ws.Range("A1:L140").Value2)
We call our function and pass it as a parameter an array of cell values ​​from the entire next sheet. Further work on analysis and processing will be carried out with the elements of this array. However, this is not the whole trick.
The validateData() function is very simple and looks like this:
Function validateData(aD As Variant) As Variant
validateData = validateValues(aD(1, 5), aD(2, 8), aD(3, 1), aD(2, 11), _
........ , _
aD(111, 3), aD(112, 8), aD(123, 9), aD(126, 10))
End Function
In other words, we select from the entire large array of aD (the name is deliberately made short, because in this function it will have to be repeated 123 times) only those values ​​that need to be analyzed and we pass on to the next function. Despite the seeming simplicity, this is the most time-consuming part - you need to select from the sheet all cells "D8", "C134", etc. and write down their coordinates (row, column) as numbers aD(4,8), aD(3,134), etc.
Perhaps can help in this the mode R1C1 of displaying the sheet. Or some kind of helper function that will be used when preparing the code (not when executing - we agreed that we will no longer access cells for get values ​​or for .Row and .Column properties!)
What will the validateData() function get? A long one-dimensional array aData(0 To 122) of cell values ​​in the listed order. That is, as many values ​​as there are cells to be filled in the Summary row for this sheet.
The last trick is the process of processing values. It would seem that we have gained nothing from all these transformations. But you claim that there are three groups of checks - for an empty value, for a boolean value (checkbox) and for text lines. This is how it is handled:
Function validateValues(ParamArray aData() As Variant) As Variant
Dim i As Variant
Dim aResult As Variant
ReDim aResult(LBound(aData) To UBound(aData))
For i = LBound(aData) To UBound(aData)
Select Case i
Case 1, 5, 7, 9 ' Checking cells empty / value
aResult(i) = IIf(aData(i) = "", "-", aData(i))
Case 4, 6, 10 ' Checking cells boolean True / "not True" (False or blank)
aResult(i) = IIf(aData(i), "Declared", "-")
Case 0, 3, 8 ' Checking cells string "Issued" / other
aResult(i) = IIf(aData(i) = "Issued", "-", aData(i))
Case 2, 91, 118 ' Checking cells string "Pending" / other
aResult(i) = IIf(aData(i) = "Issued", "-", aData(i))
Case Else ' In a real macro, this line is not needed, it will never be executed because all the cells of the array are already listed above, this is useful only for debugging while all conditions will be written
aResult(i) = "-"
Debug.Print "Cell #" & i & " not processed yet"
End Select
Next i
validateValues = aResult
End Function
And now - again, in just one call! - we write a whole row of results:
oTargetCell.Offset(0, 1).Resize(1, UBound(aResData) + 1).Value = aResData
I am sure that these tricks will reduce the time it takes to form the summary sheet many times over. Please try this and let me know if it gets better?

Putting the results of Case Statement in an Array for dumping it in another sheet

I have the following piece of code I got there for copy a range of columns in another workbook. The issue is that I need to evaluate first some columns before copy the values. I'm using Case for this but I don't know passing the result, I was thinking in an array?
For i = LBound(arrayOrigen) To UBound(arrayOrigen)
With Workbooks("facturas.xlsx").Worksheets("FACTURAS")
lastrowOrig = Application.Max(2,Cells(.Rows.Count,arrayOrigen(i)).End(xlUp).Row)
'For each cell in column D in source ,if is 0 put 0 in each cell target(destination) column(E)_'
' is not then put a formula in target column'
If i = 3 Then
Set Column3 = .Range(.Cells(2, arrayOrigen(i)), .Cells(lastrowOrig, arrayOrigen(i)))
For Each xCell In Column3
Select Case True
Case xCell.Value = 0
result = 0
Case Else
result = "=RC[-1]*0.21"
End Select
**Sheets("RESUMEN").Range(arrayDestino(i) & lastrowDes).Resize(lastrowOrig - 2).Value =** _
result
'array is needed for store the diferents result for each cell and dumping in the destination
'column all at once?'
Next
Else 'for the rest of columns copy entire column whith the same values'
Sheets("RESUMEN").Range(arrayDestino(i) & lastrowDes).Resize(lastrowOrig - 2).Value = _
.Range(.Cells(2, arrayOrigen(i)), .Cells(lastrowOrig, arrayOrigen(i))).Value
End If
End With
Next
Now I'm not able to show the result of Case in the destination columns for each cell, instead of this, it dumps all the column every time that Case change

VBA - How to output a variable to a particular row and column in excel?

This code should find the correct cell (in the column corresponding to it's 'length' and the next empty row) in which to output a variable.
I'm getting the error message:
method range of object _worksheet failed
on lines 13 onward containing "outputcolumn"
In the MsgBox lines, the correct column and row number are being displayed, so I am not sure why it is not happy with my outputcolumn in particular.
Private Sub OutputRowAndColumn()
'Choose correct column: Find the length column and name this outputcolumn
Dim cell As Range, outputcolumn As Integer
Set cell = Range("FindLength").Find(Range("Length").Value, LookIn:=xlValues)
If Not cell Is Nothing Then
outputcolumn = cell.Column
End If
MsgBox "Output column is number " & outputcolumn & "."
'Choose correct row: If the cell to the left of "cell" is empty then this is the first row of output otherwise find next empty cell down
If Sheet1.Range(outputcolumn & "4").Offset(0, 1).Value = "" Then
outputrow = 4 ''' error msg '''
ElseIf Sheet1.Range(outputcolumn & "5").Offset(0, 1).Value = "" Then
outputrow = 5
Else
outputrow = Sheet1.Range(outputcolumn & "4").Offset(0, 1).End(xlDown).Row + 1
End If
MsgBox "Output row is number " & outputrow & "."
'Copy values 1, 2 and 3 from sheet 2 to sheet 1
Sheet1.Range(outputcolumn & outputrow).Offset(0, 1).Value = Sheet2.Range("Value1").Value ''' error msg '''
Sheet1.Range(outputcolumn & outputrow).Offset(0, 2).Value = Sheet2.Range("Value2").Value
Sheet1.Range(outputcolumn & outputrow).Offset(0, 3).Value = Sheet2.Range("Value3").Value
End Sub
outputcolumn is a numeric value (you defined it as Integer, but you always should define variables holding row or column numbers as long to avoid overflow errors).
So let's say outputcolumn gets the number 2 (column B). You write Sheet1.Range(outputcolumn & "4"). To access a range by it's address, You would have to write something like Range("B4"), but what you write is Range(2 & "4"), which means Range("24"), and that is an invalid address for a Range.
You could try to translate the column number 2 to a B, but there is an easier way to access a cell when you know the row and column number: Simply use the cells-property:
If Sheet1.Cells(4, outputcolumn).Offset(0, 1).Value = "" Then
' (or)
If Sheet1.Cells(4, outputcolumn+1).Value = "" Then
Just note that the order of the parameters is row, column.
"outputcolumn" is numeric in your case and when using .Range(), it needs to be a proper alphanumeric cell reference like "C5", not all numeric.
I haven't tried it directly but changing this ...
If Not cell Is Nothing Then
outputcolumn = cell.Column
End If
... to this ...
If Not cell Is Nothing Then
outputcolumn = Split(cell.Address, "$")(1)
End If
... will go a long way to helping you.

Excel - Match item http string and display result

I have column H that contains long GET requests on sheet 1 such as:
H
GET /profiles/text/23493495_3492/g93id93kd
GET /edit/result/393493/te3903k4d
I would like to have a second sheet with the following type of list in columns A and B:
A B
23493495 identifier1
3903k4 realid2
g93id realid3
Ultimately, I would like a function that will search sheet 1 column H for any of the values in sheet 2 column A. Most of the time there is no separator so I need it to search for strings within the GET string. Once a value in sheet 2 column A is matched with a value in sheet 1 column H, I would like the function to take the corresponding text in sheet 2 column B and print it in sheet 1 column I. There may be multiple matches in a cell, so that would need to be taken into account. So if using the example above:
In H1, there would be a match of 23493495 and g93id within the string. I would like sheet 1 column I to display:
I
identifier1, realid3
I initially started with the below code where I had to specify the list but it doesn't use a second sheet or print the corresponding text of the match. So I would rather have something that meets my needs above, but below is an example of what I have tried so far:
=ListSearchB(J2, "23493495 g93id")
With this module I found that I modified a little:
Function ListSearchB(text As String, wordlist As String, Optional caseSensitive As Boolean = False)
Dim strMatches As String
Dim res As Variant
Dim arrWords() As String
arrWords = Split(wordlist)
On Error Resume Next
Err.Clear
For Each word In arrWords
If caseSensitive = False Then
res = InStr(LCase(text), LCase(word))
Else
res = InStr(text, word)
End If
If res > 0 Then
strMatches = strMatches & word
End If
Next word
If Len(strMatches) <> 0 Then
strMatches = Right(strMatches, Len(strMatches))
End If
ListSearchB = strMatches
End Function
That gives me:
23493495g93id in column I, and I wasn't sure how to separate the two with a comma.
In general though, I would prefer, to use some way to pull the list from sheet 2 and display the value in column I as specified initially.
Give this a try - just adjust the sheet names where commented before running
Sub your_sub()
Dim sGet As Worksheet
Dim sIDs As Worksheet
Dim rget As Range
Dim rIds As Range
'ADJUST SHEET NAME
With Worksheets("GET")
Set rget = Range(.Range("H1"), .Range("h" & .Rows.count).End(xlUp))
End With
'ADJUST SHEET NAME
With Worksheets("IDs")
Set rIds = Range(.Range("A1"), .Range("A" & .Rows.count).End(xlUp))
End With
mys = vbNullString
i = 1
For Each cget In rget
For Each cIds In rIds
If InStr(cget.Value, cIds) <> 0 Then
mys = mys & ", " & cIds.Offset(0, 1).Value
End If
Next cIds
If mys <> vbNullString Then
mys = Right(mys, Len(mys) - 2)
'ADJUST SHEET NAME
Worksheets("GET").Range("I" & i).Value = mys
End If
i = i + 1
mys = vbNullString
Next cget
End Sub

Resources