Comparing Two Major Workbooks By Two Columns and many Params - vba

I'm writing a code to compare two rows in two different workbooks that can be located at different places among the column. The first column is usually grouped (multiple of the same value) in one section. This is why In this code I search by the first column, then pick the other identifier column and offset one at a time while both workbook sheets match each other
I am trying to activate the cell that I find via the .Find function in this code, but apparently you can't do that. "Active method of range class failed"
I believe that I am on the right track with this code, but I'm sure there are still issues, I'm trying to solve one problem at a time with my limited skills!
Thanks for the help :)
Sub Compare2()
Dim layer As String
Dim Pno As String
Dim firstAddress As String
Dim i As Long
Dim c As Range
Option Explicit
For i = 5 To 1000 Step 1
layer = Sheets("MP Parameters").Range("A" & i).Value
Pno = Sheets("MP Parameters").Range("H" & i).Value
With Sheets("Compare").Range("a1:a1500")
Set c = .Find(layer, LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
c.Activate
Do
Sheets("Compare").ActiveCell.Offset(rowOffset:=0, columnOffset:=7).Activate
If Sheets("Compare").ActiveCell = Pno Then
Sheets("Compare").ActiveCell.Offset(rowOffset:=0, columnOffset:=9).Activate
If Sheets("Compare").ActiveCell.Value <> Sheets("MP Parameters").Range("P" & i).Value Then
Sheets("MP Parameters").Range("P" & i).Interior.ColorIndex = 46
End If
End If
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
i = i + 1
End If
End With
Next i
End Sub

You can only Activate a cell if it's on the ActiveSheet. Since your code references then Activates cells on different sheets, you will cause errors.
You have two options.
Either activate the appropriate sheet first:
Sheet1.Activate
Range("A2").Activate
Or, don't activate a sheet to do a conditional check. You don't have to activate a cell to determine what it's value is. As an example, if you wanted to check what value is in sheet1, cell B2 (irrespective of which sheet is active in the workbook):
If Sheet1.Range("B2").Value = "Yes" ...

The code within your If Not c Is Nothing ... End If block can be changed to:
firstAddress = c.Address
Do
If c.Offset(0, 7).Value = Pno Then
If c.Offset(0, 16).Value <> Sheets("MP Parameters").Range("P" & i).Value Then
Sheets("MP Parameters").Range("P" & i).Interior.ColorIndex = 46
End If
End If
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
i = i + 1
(I'm not sure what the i = i + 1 is meant to be doing. I've always found it dangerous to modify the loop counter manually. But I left it there as hopefully it is doing what you want it to do.)

Related

Macro to search columns for list of specific words that may be similar to each others

I have an Excel macro that search a list of string in a column.
For example : let's say i'm searching for "sun" in my column.
If one cell value is "We have a beautiful sunshine", then the cell contains the string "sun" and "sun" is written next to the cell, for me to identify the matches later
My problem is : if, in the list of string i'm looking for, there is first "sunshine", then "sun", then the macro will first write "sunshine" next to "We have a beautiful sunshine", then it will write "sun".
It's technically correct, this is what my macro do, but it's more relevant to me to know if there's the entire word "sunshine" than just "sun".
I need to add a condition like "find if there is this string in each cell of my column, and if there's no other string from the list i'm looking for."
And I don't quite know where to get started..
Here is my code if it can help :
Sub Reperer_Ch_Caracteres()
y = Range("B" & Rows.Count).End(xlUp).Row
For i = 2 To Range("A" & Rows.Count).End(xlUp).Row
Keyword = Range("A" & i)
Set c = ActiveSheet.Columns(2).Find(Keyword, LookIn:=xlValues, lookat:=xlPart)
If Not c Is Nothing And IsEmpty(Range("C" & i)) = True Then
firstAddress = c.Address
Do
c.Offset(0, 1).Value = Keyword
Set c = ActiveSheet.Columns(2).FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
Next i
End Sub

LOOP: Copy Cells Value (in a list) from one Sheet to Another

The purpose of this macro is copy one cell value (from a long list) to another cell located in a different sheet.
here's my code:
Sub journalben()
Set rawben = Sheets("BEN")
Set finaljnl = Sheets("JNL_BEN")
Set Rng = Range("G2:G1048576")
For Each cell In Rng
'test if cell is empty
If cell.Value <> "" Then
finaljnl.Range("L4").Value = rawben.Range("G5").Value
finaljnl.Range("K4").Value = rawben.Range("L5").Value
End If
Next
End Sub
With the help of the image, I will explain what I'm trying to achieve:
From Sheet1 ("BEN") there's a list sitting in columns G and L.
I will copy the cell G5 from Sheet1 and paste it in Sheet2 ("JNL_BEN") Range K4.
Next is I will copy the cell L5 from Sheet1 and paste it in Sheet2 ("JNL_BEN") Range L4.
Copy the next in line and do the same process just like No.2 and 3 but this time, it will adjust 1 row below.
Copy the whole list. That means up to the bottom. The list is dynamic, sometimes it will go for 5,000 rows.
For some reasons, copying the entire column is not an option to this macro due to requirement that cells from sheet1 MUST be pasted or placed in Sheet2 from left to right (or horizontally).
I hope you could spare some time to help me. My code didn't work, I guess the implementation of FOR EACH is not correct. I'm not sure if FOR EACH is the best code to use.
I appreciate anyone's help on this. Thank you very much! May the force be with you.
Try this:
Sub journalben()
Dim i As Long, lastRow As Long
Set rawben = Sheets("BEN")
Set finaljnl = Sheets("JNL_BEN")
lastRow = rawben.Cells(Rows.Count, "G").End(xlUp).Row
For i = 5 To lastRow
'test if cell is empty
If rawben.Range("G" & i).Value <> "" Then
finaljnl.Range("K" & i - 1).Value = rawben.Range("G" & i).Value
finaljnl.Range("L" & i - 1).Value = rawben.Range("L" & i).Value
End If
Next i
End Sub
I am starting FOR from 5 as the data in your image starts from cell G5 (not considering the header).
It'll be easier to use a numeric variable for this :
Sub journalben()
Set rawben = Sheets("BEN")
Set finaljnl = Sheets("JNL_BEN")
Set Rng = rawben.Range("G4:G1048576")
For i = Rng.Cells(1,1).Row to Rng.Cells(1,1).End(xlDown).Row
'test if cell is empty
If rawben.Range("G" & i).Value <> vbNullString Then
finaljnl.Range("L" & i - 1).Value = rawben.Range("G" & i).Value
finaljnl.Range("K" & i - 1).Value = rawben.Range("L" & i).Value
End If
Next i
End Sub
You should use a simple for loop. It is easier to work with.
Also, to have it dynamic and to go to the last cell in the range, use the SpecialCells method.
And your range needs to be set correctly from row 5.
Here is the code:
Sub journalben()
Set rawben = Sheets("BEN")
Set finaljnl = Sheets("JNL_BEN")
Set Rng = Range("G5:G1048576")
For i = Rng.Cells(1,1).Row to Rng.SpecialCells(xlCellTypeLastCell).Row
If rawben.Range("G" & i).Value <> vbNullString Then
finaljnl.Range("L" & CStr(i - 1)).Value = rawben.Range("G" & CStr(i)).Value
finaljnl.Range("K" & CStr(i - 1)).Value = rawben.Range("L" & CStr(i)).Value
End If
Next i
End Sub

With For each loop in Excel VBA how do I reference the cell address within the formula?

The code I commented out originally required a user to drag down the formula once the formula was appended to a cell. I have revised the procedure below and I switched from a For each cell loop - could this have been done with a For each loop structure? Utilizing Cell.address or something along the lines? Please assume my variables are all defined.
Dim client_row As Long
'Dim v As Long
Dim v As Variant
Dim i As Integer
i = 2
client_row = 0
LASTROW2 = Range("B" & Rows.Count).End(xlUp).Row
Set rng2 = Range("N2:N" & LASTROW2)
' For Each cell In rng2
' If cell.Offset(0, -13) <> "" Then
' cell.Formula = "=IFERROR(TEXT(IF(F2=""GBP"",($H2-(IF(LEN($C2)=7,BDH($C2&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH($C2&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/$H2/100,IF(E2=""EQ"",($H2-(IF(LEN($C2)=7,BDH($C2&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH($C2&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/$H2,($H2-(IF(LEN($C2)=7,BDH($C2&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH($C2&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/$H2*100)),""0.00""),""PLEASE REVIEW"")"
' Debug.Print cell
' End If
' Next cell
For Each v In rng2
If v.Offset(0, -13) <> "" Then
v.Formula = "=IFERROR(TEXT(IF($F" & i & "=""GBP"",($H2-(IF(LEN($C2)=7,BDH($C2&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH($C2&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/$H2/100,IF(E2=""EQ"",($H2-(IF(LEN($C2)=7,BDH($C2&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH($C2&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/$H2,($H2-(IF(LEN($C2)=7,BDH($C2&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH($C2&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/$H2*100)),""0.00""),""PLEASE REVIEW"")"
i = i + 1
Debug.Print i
End If
Next v
In this case you could use the rng2.FormulaR1C1 property instead. It allows you to specify a relative reference which means you won't need to keep track of the current row.
The commented out section could be written as below:
Set rng2 = Range("N2:N" & LASTROW2)
For Each cell In rng2
If cell.Offset(0, -13) <> "" Then
cell.FormulaR1C1 = "=IFERROR(TEXT(IF(RC[-8]=""GBP"",(RC[-6]-(IF(LEN(RC[-11])=7,BDH(RC[-11]&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH(RC[-11]&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/RC[-6]/100,IF(RC[-9]=""EQ"",(RC[-6]-(IF(LEN(RC[-11])=7,BDH(RC[-11]&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH(RC[-11]&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/RC[-6],(RC[-6]-(IF(LEN(RC[-11])=7,BDH(RC[-11]&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH(RC[-11]&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/RC[-6]*100)),""0.00""),""PLEASE REVIEW"")"
Debug.Print cell
End If
Next cell
As stated here in your linked questions, you could also use v.Column to get the column of the current cell in the loop. There is no special structure like For Each cell In range. cell is in this scenario just an object variable pointing to a cell, just like v in your example.
To make your life a little bit easier just declare v as Range, then IntelliSense should show you possible properties and methods for v.
Luke's answer is another nice way to set relative addresses though ;)

How to avoid need to activate worksheet every loop

I've set up some VBA code in Excel that asks the user to select a second worksheet, then searches it for a value (a shared key linking the two sets of data, found 6 columns after Rng, where I want to add the retrieved value) in the second table and adds a value from that row to a column in the original table. The part of the program that I would like to adjust is the loop below.
It works fine if when I leave in the line to activate the CurFile workbook. But it means my screen is flashing a lot back and forth between the two workbooks. And once I start getting into hundreds or thousands of lines of data it will be ridiculously slow.
When I comment out that line, the value for FindCID doesn't change and it seems to just keep on refilling the same line, even though the value for r is updating. If after a few loops I add the activate line back in, it resumes properly filling in the results several lines down.
How can I streamline this? I originally was using ThisWorkbook references but even with explicitly defining CurFile (CurFile = ActiveWorkbook.Name) earlier it doesn't seem to go back to that workbook to look up the next value to search for, unless I reactivate the sheet.
Do While r <= maxRows
With Workbooks(CurFile).Worksheets("Sheet1")
Set Rng = .Range(Cells(r, c), Cells(r, c))
End With
FindCID = Rng.Offset(0, 6).Value
If Trim(FindCID) <> "" Then
With Workbooks(FN) ' found earlier by a function
.Activate
End With
With Sheets("Sheet1").Range("D:D")
Set FoundCell = .Find(What:=FindCID)
If Not FoundCell Is Nothing Then
PathLen = FoundCell.Offset(0, 2).Value
Workbooks(CurFile).Sheets("Sheet1").Activate 'If I comment out this line it doesn't work
Rng.Value = PathLen
MsgBox "CID found in " & FoundCell.Address & " Its value is " & PathLen
Else
MsgBox "Nothing found"
End If
End With
End If
On Error Resume Next
r = r + 1
Loop
Actually when working with objects, in most of the cases, there is no need to activate the workbooks\worksheets.
This is your code with some modifications in this regard:
Application.ScreenUpdating = False '(as suggested by CBRF23)
'......
'begining of your code
'......
Do While r <= maxRows
With Workbooks(CurFile).Worksheets("Sheet1")
Set Rng = .Cells(r, c) '(1)
End With
FindCID = Rng.Offset(0, 6).Value2
If Trim(FindCID) <> "" Then
Set FoundCell = Workbooks(FN).Sheets("Sheet1").Range("D:D").Find(What:=FindCID)
If Not FoundCell Is Nothing Then Rng.Value = FoundCell.Offset(0, 2).Value2
End If
r = r + 1
Loop
'......
'rest of your code
'......
Application.ScreenUpdating = True
(1) Notice that way the Range is defined as it’s made of only once Cell; but if the range has more than one Cell i.e. from Cell(r,c) to Cell(r,c+5) then you need to use the form:
Set Rng = Range(.Cells(r, c), .Cells(r, c+5))
There is no need to add a period . before Range as the range is defined by the Cells within the Range command. By using the period . before the Cell command they are referred as part of the
With Workbooks(CurFile).Worksheets("Sheet1")
However if the Range is defined as A1:F1 then the period . has to be added before the Range as in:
Set Rng = .Range(“A1:F1”)
I removed the MsgBox commands as I believe they were just for testing purposes. Not really showing these messages for hundreds or thousands lines of data. Isn’t it?

VBA Get values from the filter function in Excel 2007

When using the Filter function from the Edition menu in Excel 2007, little arrows shows on the bottom right of the header cells. When clicking on one, a list of every different value from that column popup with the option to select them.
How can you get thoses values and loop throug them using VBA?
I've tried this :
Dim Filter As Range
For Each Filter In Range(Cells(2, 1), Cells(2, 1).End(xlDown)).SpecialCells(xlCellTypeVisible).Cells
MsgBox (Filter.value)
Next Filter
But it dosen't work (it loop trough all the cells of the column). Maybe it's because the arrow is not "clicked" when running the macro. I've found the this For Each loop in a post talking about Excel 2002.
[EDIT]
The following is not the solution that I'm looking for, as it takes far more time to execute than the native Excel way, but it is an acceptable workaround.
Dim values As New Collection
Dim RowCount As Long
RowCount = Cells(Rows.Count, "A").End(xlUp).Row
Dim IsUnique As Boolean
For i = 2 To RowCount
IsUnique = True
For Each value In values
If value = Range("A" & i).value Then
IsUnique = False
End If
Next value
If IsUnique Then
values.Add Range("A" & i).value
End If
Next i
Find returns a Range object that represents the first cell where that information is found.
You can use the FindNext and FindPrevious methods to repeat the search
This example finds all cells in the range A1:A500 on worksheet one that contain the value 2 and changes it to 5.
With Worksheets(1).Range("a1:a500")
Set c = .Find(2, lookin:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
c.Value = 5
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
End With