I need a Macro (must be a macro) that will select a cell if - and only if - two cells match. =IF(a3=k8) select k9. A3 is static, but the matching data can be anywhere along a row range. Therefore, I need to look for the data in a3 in the range k8:bz8, and in every case, follow by selecting the cell directly below it. Basically HLOOKUP. Once that cell is selected i will call another macro to populate that cell.
I am using Office 2016 for Mac (which sux)
Thanks
Don
Presumably you shouldn't have any trouble with the WorksheetFunction object using HLOOKUP function with a wildcard search.
dim val as variant
with activesheet
on error goto no_match
val = worksheetfunction.hlookup(chr(42) & .range("a3").value & chr(42), .range("k8:bz9"), 2, false)
on error goto 0
end with
debug.print val
no_match:
if err.number = 1004 Then _
debug.print "no match"
I think I would do a Do Until Loop. Something like:
Range("A8").Select
Do Until Selection = Range("A3").Value
ActiveCell.Offset(0, 1).Select
Loop
ActiveCell.Offset(0, 1).Select
To test it I put the value 14 in cell A3. Then I put a bunch of different values in each cell along row 8. I put 14 in k8. The macro will look at each cell and see if it equals 14 and then move on. You'll need an exit condition if you run out of data but that should get you started.
Related
Yesterday I learned here how to copy a row to a second sheet.
Sub maJolieProcedure(Texte As String)
With Worksheets("employes").Range("A:A")
Set c = .Find(what:=Texte)
If Not c Is Nothing Then
firstAddress = c.Row
Worksheets("employes").Rows(firstAddress).Copy _
Destination:=Worksheets("rapport").Range("A1")
MsgBox "Ok"
Else
MsgBox "Nok"
End If
End With
End Sub
To respect the formatting of the second sheet, I want to copy and paste the contents of each cell one by one.
I can identify the line number. However, I can't figure out how the Range object can return each cell one by one. For example, C3 content if Rows = 3.
Thanks a lot.
If you don't want to paste the formatting from one range to another paste values only.
Worksheets("employes").Rows(firstAddress).Copy
Worksheets("rapport").Range("A1").PasteSpecial xlValues
That's the same for all ranges, whether 1 cell or a million. The Copy process copies the subject to memory as one block. Any parsing must be done before the Copy instruction. An alternative is to read a range into an array.
Dim Arr As Variant
Arr = Worksheets("employes").Rows(firstAddress).Value
This will create a 3D array of 1 row and about 16000 columns. You might decide to limit your enthusiasm to only what you need.
With Worksheets("employees")
Arr = .Range(.Cells(firstAddress, 1), .Cells(firstAddress, .Columns.Count).End)xlToLeft)).Value
End With
Pay attention to the leading periods within the With statement. Each such period stands for the object mentioned in the With statement.
If your goal is to respect the formating of the second sheet, you don't need to loose time copying cell by cell.
It is more effective to do a paste special, like you do with the mouse:
Range("A1").Copy
Range("B1").PasteSpecial Paste:=xlPasteValues
works very well also with bigger ranges if you need:
Range("A1:A12").Copy
Range("B1:B12").PasteSpecial Paste:=xlPasteValues
or even
Range("A1:A12").Copy
Range("D3").PasteSpecial Paste:=xlPasteValues
If your goal is to really access all cell of a range individually , you just iterate on the range. For example:
For Each cell In Range("A1:A12")
cell.Value = cell.Value + 2
Next cell
I receive excel files from a bank that contains information that I need in a certain column. So I want to loop through the specific column and get the values of those cells. Lets say I select column B. I start at B1 and loop through the column cells. But once I get to a merged cell, which there are quite a lot of, the merged cell throws me off of column B when I try to move past it. I'm using Offset(1, 0) to go down the column.
'Here is a quick example of how the selected cell will move
'I'm using an index to move down 15 cells
'Merge cell A2 and B2 before running the macro
Sub test()
Dim index As Integer
index = 0
Range("B1").Select
Do While index < 15
Selection.Offset(1, 0).Select
index = index + 1
Loop
End Sub
The selection moves from B1 to B2, which is merged with A2, then continue to A3 instead of B3.
Using Select is not a best practice, but if you need it for some visual reasons, the code below would work. It gets a starting cell startingRange and each time it loops one row down from it - startingRange.Offset(rowoffset:=index).Select
Sub TestMe()
Dim index As Long
index = 0
Dim startingRange As Range
Set startingRange = Worksheets(1).Range("B1")
Do While index < 15
startingRange.Offset(rowoffset:=index).Select
Application.Wait (Now + #12:00:01 AM#)
index = index + 1
Loop
End Sub
The Application.Wait (Now + #12:00:01 AM#) is added in order to visualize the Seleced cell better.
You should do this without using Select, VBA can address any cell in your workbook without selecting it first and find out what value it contains. There are doubtless different ways of doing what you want to achieve, but this example explains how to move through a column without it deviating:
Sub test()
For Each Cell In Range("B1:B14").Cells
'Finds the MergeArea of the Cell and gets the value from the top left cell
MsgBox Cell.MergeArea.Cells(1, 1).Value
Next
End Sub
When you run the sub it'll fire off Message Boxes containing the values of the cells in column B, rows 1-14, including those that are merged.
This is probably an incomplete answer, your final answer may be a mixture of the code in this example plus the code in your original question.
I have the following code that I've written to take some names and use them to populate a timesheet.
Sub InitNames()
Dim i As Integer
i = 0
Dim name As String
Windows("Employee Data.xlsx").Activate
Sheets("Employees").Select
Range("A2").Select
Do Until IsEmpty(ActiveCell)
name = ActiveCell.Value
Workbooks("Timesheet").Sheets("ST").Range("A9").Offset(i * 9).Value = name
ActiveCell.Offset(1, 0).Select
Loop
End Sub
Basically, the cells in the target sheet are spaced 9 rows away from each other, so the first name would go in cell A9, the second in A18, the third in A27, and so on. I'm sure I'm missing something incredibly simple, but I'm not getting any feedback from Excel whatsoever (no error messages). The cells in the timesheet are merged cells, but I cannot change them (locked by the owner), but I don't think that has anything to do with it.
EDIT: I added a line: OriginalValue = Workbooks("Timesheet").Sheets("ST").Range("A10").Offset((x - 2) * 9, 0).Value so I could watch to see what values were being overwritten in my Timesheet and I noticed something interesting: OriginalValue only grabs the first cell's text (A9), thereafter, for every cell (A18, A27, etc.) the debugger indicates that OriginalValue = "" even though those cells also contain names. However, when I open another worksheet and reference A9, A18, etc., I AM pulling the names.
EDIT 2: I modified the test line to read Workbooks("Timesheet").Sheets("ST").Range("A" & ((x - 1) * 9)).Value = "Test" which does change the values in all the target cells. Why would VBA allow me to assign "Test" to a cell value but not the names in the other worksheet?
Try something like this. It will accomplish the task you are requesting without using .Select or .Activate
Sub InitNames()
Dim i As Integer
Dim Wksht as Worksheet
i = 0
Set Wksht = Workbooks("Employee Data.xlsx").Sheets("Employees")
For i = 2 to Wksht.Range("A" & Wksht.Rows.Count).End(xlUp).Row
Workbooks("Timesheet").Sheets("ST").Range("A9").Offset((i-2) * 9,0).Value = Wksht.Range("A" & i).Value
Next i
End Sub
So, in Sheet1 I have base of some names and it looks like this:
In Sheet2 I'm working with these names from Sheet1. I'm doing that in a way that I'm entering Code value in column A and in column B I get the Name, in column C I get the Last Name. That looks like this:
I've done this with formulas, entering it in the formula bar. For column A(or Name) I've used this formula: =IFERROR(VLOOKUP(A2;Sheet1!A:C;2;FALSE);"") and for column B(or Last Name) I've used this one: =IFERROR(VLOOKUP(A2;Sheet1!A:C;3;FALSE);""). I've dragged these formulas to row 20 and it works great.
Now, what I'd like to do is to put these formulas into Excel VBA code and them to work for noted range. I've just started to use VBA and I don't know how to do it in it, tried something but doesn't work, ..., I've done this so far. I'm new to this Excel/Macro/VBA thing so any help would be appreciated.
The below code will work if you type in your Code values in sheet2 and highlight them, and run this macro:
Selection.Offset(0, 1).FormulaR1C1 = "=IFERROR(VLOOKUP(RC[-1],Sheet1!C[-1]:C,2,FALSE),"""")"
Selection.Offset(0, 2).FormulaR1C1 = "=IFERROR(VLOOKUP(RC[-2],Sheet1!C[-2]:C,3,FALSE),"""")"
Selection.Offset(0, 1).Value = Selection.Offset(0, 1).Value
Selection.Offset(0, 2).Value = Selection.Offset(0, 2).Value
Edit: If you are wanting to update values as you type use (thank you #PeterAlbert for added optimisation!):
Private Sub Worksheet_Change(ByVal Target As Range)
'end if the user made a change to more than one cell at once?
If Target.Count > 1 Then End
'stop system activating worksheet_change event while changing the sheet
Application.EnableEvents = False
'continue if column 1(A) was updated
'and
'dont continue if header or row 1 was changed
If Target.Column = 1 And Target.Row <> 1 Then
With Target.Offset(0, 1) 'alter the next cell, current column +1 (column B)
'RC1 = current row and column 1(A) e.g. if A2 was edited, RC1 = $B2
'C1:C2 = $A:$B
.FormulaR1C1 = "=IFERROR(VLOOKUP(RC1,Sheet1!C1:C2,2,FALSE),"""")"
.Value = .Value 'store value
End With
With Target.Offset(0, 2) 'alter the next cell, current column +2 (column C)
'C1:C3 = $A:$C
.FormulaR1C1 = "=IFERROR(VLOOKUP(RC1,Sheet1!C1:C3,3,FALSE),"""")"
.Value = .Value 'store value
End With
End If
Application.EnableEvents = True 'reset system events
End Sub
Explinatioin of RC:
The FormulaR1C1 formula types are good to use when referencing a cell with respect to the current cell. There a few rules to remember:
The R stands for Row and C is for Column and the integer after it, if any, defines the row or column;
As a basis the RC formula references itself;
Any number following the R or C wraped in [] is an offset to itself, e.g. if you are in cell A1 and use R[1]C[1] you would be referencing cell B2;
Also any number following the R and C is an exact, e.g. if you reference R2C2 no matter the cell you are in would also point to B2; and
To complicate things if you were in cell C5, e.g. using Range("C5").FormulaR1C1 = and coded the follwing:
"=RC[-1]" references cell B5
"=RC1" references cell A5, more rightly $A5
"=R[1]C[-2]" references cell A6
"=Sum(C[-1]:C5)" is =Sum(B:E), more rightly =Sum(B:$E)
If I understand your question and comments correctly, you want to ensure that columns B&C always show you the right values based on your formula, but also want to protect (and maybe even hide the formula) from the users.
I'd suggest you use sheet protection instead: all you need to do is to unlock the cells you want the users to edit, i.e. select column A and in the _ Format cells_ dialog uncheck "Locked" in the Protection tab. Similarly for columns B&C, check "Hidden". Now right click the sheet name and select Protect Sheet. Once this is done, the user can edit column A - but will not see the formula in B&C and cannot edit those cells.
If for some reasons you need to ensure this in VBA, use the following code:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False 'to prevent endless loop
With Target.Offset(, 2 - Target.Column).Resize(, 2)
.FormulaR1C1 = "=IFERROR(VLOOKUP(RC1,Sheet1!C1:C3,COLUMN(RC),0),"""")"
.Value = .Value
End With
Application.EnableEvents = True
End Sub
You need to place this in the module of the worksheet.
I've written a macro in VBA that simply fills in a given cell's value from another cell in that sheet. I do this for lots of cells in the sheet, and I'm doing it like so:
Range("B3").Value = Range("B200")
Range("B4").Value = Range("B201")
'etc.
Now, I am often adding values by inserting new rows, so I might insert a new row
between B200 and B201, which will break the macro because it doesn't autoupdate when
I insert the new row.
How can I code the macro so it autoupdates the cell references when I insert new rows or columns?
My suggestion would be to make sure the ROW you want to retrieve values from has a unique value in it that you can .FIND anytime you want, then grab your values from column B of that found cell's row. So right now you want to get a value in B200 and A200 always has the text in it: "Final Total" and that is unique.
Dim MyRNG As Range
Set MyRNG = Range("A:A").Find("Final Total", LookIn:=xlValues, LookAt:=xlWhole)
Range("B3").Value = Range("B" & MyRNG.Row)
Range("B4").Value = Range("B" & MyRNG.Row + 1)
This is not an answer but an alternative.
Naming your range is the way to go as Shiin suggested but then if you have 500 cells then like I mentioned earlier, naming 500 cells and using them in your code can be very painful. The alternative is to use smart code. Let's take an example
Let's say you have a code like this
Sub Sample()
Range("B3").Value = Range("B200")
Range("B4").Value = Range("B201")
Range("B5").Value = Range("B201")
' And
' So On
' till
Range("B500").Value = Range("B697")
End Sub
The best way to write this code is like this
Sub Sample()
Dim i As Long
For i = 200 To 697
Range("B" & i - 197).Value = Range("B" & i)
Next i
End Sub
and say if you insert a line at say row 300 then simply break the above code in two parts
Sub Sample()
Dim i As Long
For i = 200 To 299
Range("B" & i - 197).Value = Range("B" & i)
Next i
For i = 301 To 698
Range("B" & i - 197).Value = Range("B" & i)
Next i
End Sub
So every time you insert a row, simply break the for loop into an extra part. This looks tedious but is much better than naming 500 cells and using them in your code.
If you are planning to use the macro only once (i.e for 1 time use) then read ahead.
If you are worried that when the user inserts the row then the cells are not updated then you can instead of assigning a value, assign a formula.
For example
Range("B3").Formula = "=B200"
This will put a formula =B200 in cell B3. So next time when you insert a row so that the 200th row moves it's position, you will notice that the formula automatically gets updated in cell B3
HTH
Try giving a name to the range. If you refer to the range by name Excel searches for it and retrieves the rows that defines it. Range names update their definition when new rows are added.
Adding to the above, i think this tutorial illustrates my point:
http://www.homeandlearn.co.uk/excel2007/excel2007s7p6.html this is how to define the name of the range.
This tutorial explains how to use it on macros and vba:
http://excel.tips.net/T003106_Using_Named_Ranges_in_a_Macro.html
I hope this helps :D