VBA Loop of Vlookup with dynamic colums - vba

For a small project i want to use a vlookup or match in VBA to derive data from another sheet. I found it a difficult task even with the help of google I couldn't find the solution.
I divided the project into 3 phases:
First phase is creating a working Vlookup in VBA (can't get it working)
Search for the range where it needs to be executed (dynamic) (I think I managed)
Loop through all the cells in the table (totally stuck on "For each" statement)
I managed to fetch the dynamic range #Firstcell and #Lastcell
But I'm really stuck at the loop and vlookup.
I want to create the vlookup in such way that for each cell the X will be A & rownumber and Y will be Columnletter & "4".
The vlookup needs to be executed from Firstcell to Lastcell.
Sub Match_Values()
' Variables
Dim X As Integer
Dim Y As Integer
Dim Firstcell As Integer
Dim Lastcell As Integer
' Range determination
With ActiveSheet
Range("A3").Select
Selection.End(xlToRight).Select
Firstcell = ActiveCell.Offset(1, 1).Range("A1").Select
End With
With ActiveSheet
Lastcell = ActiveCell.SpecialCells(xlLastCell).Select
End With
' For each cell create a vlookup with on rowindex en on y the columnindex loop statement
' Vlookup
With WorksheetFunction
c04 = .VLookup(X, [Pivot!A4:CC99], .Match(Y, [Sheet1!A4:CC4], 0), False)
End With
MsgBox c04
End Sub
Thanks in advance, if i need to provide additional feedback please let me know.
Edit, Thanks for the feedback
I uploaded the example file: https://ufile.io/48evu
(i'm sorry didn't see how to disclose in stackoverflow)
Picture 1
Picture 2

I found a way that made my script work.
I agree with Peh that I should look more into VBA, but it works.
Maybe this could be useful for someone that try's something similar and it makes the question answered.
Thanks for the support!
Private Sub Match_Values()
' Variables
Dim Firstcell As String
Dim Lastcell As String
Dim sht As Worksheet
' Range determination
With ActiveSheet
Range("A3").Select
Selection.End(xlToRight).Select
ActiveCell.Offset(1, 1).Range("A1").Select
Firstcell = ActiveCell.Address
End With
With ActiveSheet
ActiveCell.SpecialCells(xlLastCell).Select
Lastcell = ActiveCell.Address
End With
Dim rng As Range: Set rng = Application.Range("Overview!" & Firstcell & ":" & Lastcell)
Dim cel As Range
For Each cel In rng.Cells
cel.FormulaR1C1 = _
"=IFERROR(VLOOKUP(RC1,Pivot!R4C1:R100C50,MATCH(Overview!R2C,Pivot!R4C1:R4C50,0),FALSE),""Error"")"
Next cel
End Sub
Greetings!

Related

Find Multiple Instances of Value in Spreadsheet

I'm trying to create a macro in VBA that will search through a column in "PasteSheet" for any cell that contains the word "conversion". Once I have that cell, I can gather other information in other columns that correspond to that row. The problem I am encountering is creating some type of loop that will run through the entire database to return all instances of the word "conversion". Here is my code so far:
Sub Conversion()
Dim Comment As Range
Dim i As String
Worksheets("PasteSheet").Activate
Range("Comment").Find("conversion").Select
Worksheets("sheet1").Range("a1") = Selection.Offset(0, -8)
End Sub
Help please!
Here's one way you can do it. This will save the addresses of the found word in an array, and you can use that array however you like at the end. I used column D as my example column. Change anything as necessary
Sub getCells()
Dim rng As Range, cel As Range
Dim celAddress() As Variant
Dim i As Long
i = 0
Set rng = Range("D1:D" & Cells(Rows.Count, 4).End(xlUp).Row)
ReDim cellAddress(rng.Cells.Count)
For Each cel In rng
If cel.Value = "conversion" Then
cellAddress(i) = cel.Address
i = i + 1
End If
Next cel
ReDim Preserve cellAddress(i - 1)
For i = LBound(cellAddress) To UBound(cellAddress)
' Do whatever with each cell address found
Debug.Print cellAddress(i)
Next i
End Sub

Copy-Paste Range from Min to Max

It might be a simple question but after hours of trying about to give up...
I want the macro to find the range from a minimum to a maximum. This range should be copied and pasted to some kind of a "summary sheet".
I was able to make the macro find the min and the max and I also got a copy-paste instruction that works.
Could somebody please help me to combine these instructions into one?
Here is my macro as far as I came:
Sub Enter_Formula()
Dim blatt
Dim sheetName As String
For i = 1 To Sheets.Count
Sheets(i).Select
Range("=Min(A59:A86):=Max(A:A)").Copy Range("C1")
Next
End Sub
Thank you!!
I'd go as follows:
Sub Enter_Formula()
Dim sht As Worksheet, summarySht As Worksheet
Set summarySht = Worksheets("Summary") '<--| change "Summary" to your actual "Summary" sheet name
For Each sht In Worksheets
If sht.Name <> summarySht.Name Then
With sht.Range("A59:A86")
.Parent.Range(.Find(what:=WorksheetFunction.Min(.Cells), lookat:=xlWhole, LookIn:=xlValues), .EntireColumn.Find(what:=WorksheetFunction.Max(.EntireColumn), lookat:=xlWhole, LookIn:=xlValues)).Copy summarySht.Cells(1, Columns.Count).End(xlToLeft).Offset(, 1)
End With
End If
Next
End Sub
Might be a bit faster to evaluate the expression directly (tested):
Dim ws As Worksheet
For Each ws In Worksheets
ws.Range("Index(A59:A86,Match(Min(A59:A86),A59:A86,0)):Index(A:A,Match(Max(A:A),A:A,0))").Copy ws.Range("C1")
Next

Excel 2010 VBA - Update Graph using range defined by variable

I have an Excel sheet that is updating daily. I am trying to automatically update a graph with the new data (1 row) that is added daily.
So far I have:
Sub UpdateGraphs()
Dim latestRow As Integer
Sheets("DailyJourneyProcessing").Select
Range("A500").Select
Do Until ActiveCell.Value = ""
If ActiveCell.Value <> "" Then
ActiveCell.Offset(1, 0).Select
End If
Loop
ActiveCell.Offset(-1, 0).Select
Application.CutCopyMode = False
ActiveCell.EntireRow.Copy
ActiveCell.Offset(1, 0).Select
ActiveCell.EntireRow.PasteSpecial (xlPasteAll)
Application.CutCopyMode = False
latestRow = ActiveCell.row
Dim str1 As String
Dim rng1 As Range
str1 = "=DailyJourneyProcessing!$F$180:$F$" & latestRow
Set rng1 = Range(str1)
Debug.Print "Got this far..."
Set ActiveChart.SeriesCollection(1).Values = Range(str1)
I know that this looks like I simply copy the previous row but the formula's included take car of the changes in data.
The Integer / row at the moment is around 520, so I want to do:
ActiveChart.SeriesCollection(1).Values = "=DailyJourneyProcessing!$F$180:$F$520"
Where the row number changes daily. This is one of about 20 range updates I need to automate, but once I have solved one the others should be the same.
I have tried everything I can find online, but nothing quite worked.
At the moment, I get a run-time error 91: Object or With block variable not set.
Any help would be appreciated.
There is actually no need for VBA to accomplish this. You will find the method in this link much easier to manage and maintain than VBA code. Also, its really best not to use VBA when you don't have to!
However, so that you can see a more efficient way to code what you were trying to do, I've offered the code below. It very well may need some tweaks to fit your actual data set.
Sub UpdateGraphs()
Dim wks As Worksheet, rng1 As Range
Dim latestRow As Long ' changed to long to handle rows over 32,000 (whatever number Integer stops at)
Set wks = Sheets("DailyJourneyProcessing")
With wks
latestRow = .Range("F" & .Rows.Count).End(xlUp).Row
str1 = "=DailyJourneyProcessing!$F$180:$F$" & latestRow
Set rng1 = Range(str1)
Dim myChart As Chart
Set myChart = .ChartObjects("myChartName")
myChart.SeriesCollection(1).Values = rng1
End With
End Sub

Effective Looping Checkup VBA

Summary: My company has two different spreadsheets with many policies on each. They want me to match up policies by a policy ID and transfer all the old notes from the old spreadsheet to the new spreadsheet.
Reasoning: my issue is not with not understanding how to do this, but the BEST way to do this. Since joining StackOverflow I've been told things I should and shouldn't do. I've been told different times it is better to use a For Each loop instead of a simple Do loop. Also, I've been told I shouldn't use .Select heavily (but I do).
How I Would Normally Do It: I would normally just use a Do Loop and go through the data just selecting the data with .Find and using ActiveCell and when I wanted to interact with other Columns in that current row I would just use ActiveCell.Offset(). I tend to love .Select and use it all the time, however on this project I'm trying to push myself out of the box and maybe change some bad coding habits and start using what may be better.
Question: How would I go about doing the equivalent of an ActiveCell.Offset() when I'm using a For Each loop?
My Code So Far: **Questions/Criticisms welcome
Sub NoteTransfer()
transferNotes
End Sub
Function transferNotes()
Dim theColumn As Range
Dim fromSheet As Worksheet
Dim toSheet As Worksheet
Dim cell As Range
Dim lastRow As Integer
Set fromSheet = Sheets("NotesFrom")
Set toSheet = Sheets("NotesTo")
With fromSheet 'FINDING LAST ROW
lastRow = .Range("B" & .Rows.Count).End(xlUp).Row
End With
Set theColumn = fromSheet.Range("B5:B" & lastRow)
For Each cell In theColumn 'CODE FOR EACH CELL IN COLUMN
If cell.Text = "" Then
'do nothing
Else
With toSheet 'WANT TO FIND DATA ON THE toSheet
Cells.find(What:=cell.Text, After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
End With
End If
Next cell
End Function
Example
Bottom of the sheet
First, your question:
Question: How would I go about doing the equivalent of an ActiveCell.Offset() when I'm using a For Each loop?
Doesn't make much sense given the code you posted. It's a very general question, and would need some context to better understand. It really depends on your loop. If you are looping a contiguous range of cells from the ActiveCell then you could say ...
For each cel in Range
myValue = ActiveCell.Offset(,i)
i = i + 1
Next
To get the column next to each cell in the loop. But in general I wouldn't call that great programming. Like I said, context is important.
As far as your code goes, see if this makes sense. I've edited and commented to help you a bit. Oh yeah, good job not using Select!
Sub transferNotes() '-> first no need for a function, because you are not returning anything...
'and no need to use a sub to call a sub here as you don't pass variables,
'and you don't have a process you are trying to run
Dim theColumn As Range, cell As Range '-> just a little cleaner, INMHO
Dim fromSheet As Worksheet, toSheet As Worksheet '-> just a little cleaner, INMHO
Dim lastRow As Integer
Set fromSheet = Sheets("NotesFrom")
Set toSheet = Sheets("NotesTo")
With fromSheet ' -> put everything you do in the "fromSheet" in your With block
lastRow = .Range("B" & .Rows.Count).End(xlUp).Row 'FINDING LAST ROW
Set theColumn = .Range("B5:B" & lastRow)
theColumn.AutoFilter 1, "<>"
Set theColumn = theColumn.SpecialCells(xlCellTypeVisible) '-> now you are only looping through the cells are that are not blank, so it's more efficient
For Each cell In theColumn
'-> use of ActiveCell.Offset(), it's not ActiveCell.Offset(), but it uses Offset
Dim myValue
myValue = cell.Offset(, 1) '-> gets the cell value in the column to the right of the code
'WANT TO FIND DATA ON THE toSheet
toSheet.Cells.Find(What:=cell.Text, After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
Next cell
End With
End Sub
This is my suggestion so far.
Function transferNotes()
Dim SourceColumn As Range
Dim fromSheet As Worksheet
Dim toSheet As Worksheet
Dim cell As Range
Dim lastRow As Long '<--changed to Long
Set fromSheet = Sheets("NotesFrom")
Set toSheet = Sheets("NotesTo")
With fromSheet 'FINDING LAST ROW
lastRow = .Range("B" & .Rows.Count).End(xlUp).Row
End With
Set SourceColumn = fromSheet.Range("B5:B" & lastRow)
For Each cell In SourceColumn 'CODE FOR EACH CELL IN COLUMN
If cell.Value = "" Then 'the .Text property can
'make for some confusing errors.
'Try to avoid it.
'nothng to search for
Else
With toSheet 'WANT TO FIND DATA ON THE toSheet
Dim destRng As Range
Set destRng = .Range("A:A").Find(What:=cell.Value)
If Not destRng Is Nothing Then
.Cells(destRng.Row, <your mapped column destination>)
= fromSheet.Cells(cell.Row,<your mapped column source>)
' you can either repeat the above line for all of your non-contiguous
'sections of data you want to move from sheet to sheet
'(i.e. if the two sheets are not arranged the same)
'if the two sheets are aranged the same then change
'the .cells call to call a range and include
'the full width of columns
Else
'nothing was found
End If
End With
End If
Next cell
End Function

Vba macro to copy row from table if value in table meets condition

i'm trying to make a macro which:
goes through a table
looks if value in column B of that table has a certain value
if it has, copy that row to a range in an other worksheet
The result is similar to filtering the table but I want to avoid hiding any rows
I'm kinda new to vba and don't really know where to start with this, any help much appreciated.
That is exactly what you do with an advanced filter. If it's a one shot, you don't even need a macro, it is available in the Data menu.
Sheets("Sheet1").Range("A1:D17").AdvancedFilter Action:=xlFilterCopy, _
CriteriaRange:=Sheets("Sheet1").Range("G1:G2"), CopyToRange:=Range("A1:D1") _
, Unique:=False
Try it like this:
Sub testIt()
Dim r As Long, endRow as Long, pasteRowIndex As Long
endRow = 10 ' of course it's best to retrieve the last used row number via a function
pasteRowIndex = 1
For r = 1 To endRow 'Loop through sheet1 and search for your criteria
If Cells(r, Columns("B").Column).Value = "YourCriteria" Then 'Found
'Copy the current row
Rows(r).Select
Selection.Copy
'Switch to the sheet where you want to paste it & paste
Sheets("Sheet2").Select
Rows(pasteRowIndex).Select
ActiveSheet.Paste
'Next time you find a match, it will be pasted in a new row
pasteRowIndex = pasteRowIndex + 1
'Switch back to your table & continue to search for your criteria
Sheets("Sheet1").Select
End If
Next r
End Sub
Selects are slow and unnescsaary. The following code will be far faster:
Sub CopyRowsAcross()
Dim i As Integer
Dim ws1 As Worksheet: Set ws1 = ThisWorkbook.Sheets("Sheet1")
Dim ws2 As Worksheet: Set ws2 = ThisWorkbook.Sheets("Sheet2")
For i = 2 To ws1.Range("B65536").End(xlUp).Row
If ws1.Cells(i, 2) = "Your Critera" Then ws1.Rows(i).Copy ws2.Rows(ws2.Cells(ws2.Rows.Count, 2).End(xlUp).Row + 1)
Next i
End Sub
you are describing a Problem, which I would try to solve with the VLOOKUP function rather than using VBA.
You should always consider a non-vba solution first.
Here are some application examples of VLOOKUP (or SVERWEIS in German, as i know it):
http://www.youtube.com/watch?v=RCLUM0UMLXo
http://office.microsoft.com/en-us/excel-help/vlookup-HP005209335.aspx
If you have to make it as a macro, you could use VLOOKUP as an application function - a quick solution with slow performance - or you will have to make a simillar function yourself.
If it has to be the latter, then there is need for more details on your specification, regarding performance questions.
You could copy any range to an array, loop through this array and check for your value, then copy this value to any other range. This is how i would solve this as a vba-function.
This would look something like that:
Public Sub CopyFilter()
Dim wks As Worksheet
Dim avarTemp() As Variant
'go through each worksheet
For Each wks In ThisWorkbook.Worksheets
avarTemp = wks.UsedRange
For i = LBound(avarTemp, 1) To UBound(avarTemp, 1)
'check in the first column in each row
If avarTemp(i, LBound(avarTemp, 2)) = "XYZ" Then
'copy cell
targetWks.Cells(1, 1) = avarTemp(i, LBound(avarTemp, 2))
End If
Next i
Next wks
End Sub
Ok, now i have something nice which could come in handy for myself:
Public Function FILTER(ByRef rng As Range, ByRef lngIndex As Long) As Variant
Dim avarTemp() As Variant
Dim avarResult() As Variant
Dim i As Long
avarTemp = rng
ReDim avarResult(0)
For i = LBound(avarTemp, 1) To UBound(avarTemp, 1)
If avarTemp(i, 1) = "active" Then
avarResult(UBound(avarResult)) = avarTemp(i, lngIndex)
'expand our result array
ReDim Preserve avarResult(UBound(avarResult) + 1)
End If
Next i
FILTER = avarResult
End Function
You can use it in your Worksheet like this =FILTER(Tabelle1!A:C;2) or with =INDEX(FILTER(Tabelle1!A:C;2);3) to specify the result row. I am sure someone could extend this to include the index functionality into FILTER or knows how to return a range like object - maybe I could too, but not today ;)