I'm trying to loop Vlookup using "for each cell in range" from another worksheet but all lookup_value is getting results even though it doesn't MATCH with the data from table_array.
Here's a sample table & code.
table_array
When IN button was clicked, I want the quantity from each cell of table_array to add the quantity in corresponding lookup_value cells.
Private Sub cmdIN_Click()
Dim rng As Range, cell As Range
Set rng = Sheet2.Range("prodDesc")
If MsgBox("Are you sure you want to update the stock IN quantity?", vbQuestion + vbYesNo, "Fresh Herbs & Spices") = vbNo Then
Cancel = True
End If
Application.EnableEvents = False
Application.ScreenUpdating = False
Sheet2.Unprotect Password:=""
On Error Resume Next 'on error run code on next line
For Each cell In rng
cell.Offset(, 12).Value = [VLookup(prodDesc, descTable, 2, False)] + cell.Offset(, 12).Value
Next cell
Sheet2.Protect Password:="", DrawingObjects:=True, Contents:=True, Scenarios:=True
Cells(Rows.Count, "C").End(xlUp).Offset(3).Select
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
However the result is NOT as expected, because all cells in lookup_value worksheets are getting same result with or without MATCH.
lookup_value
By the way, prodDesc and descTable are dynamic table as range.
dynamic tables
After doing some research & asking questions,
Here's a working code:
Private Sub cmdIN_Click()
Dim cell As Range, qty
If MsgBox("Are you sure you want to update the stock IN quantity?", vbQuestion + vbYesNo, "Fresh Herbs & Spices") = vbYes Then
Application.EnableEvents = False
Application.ScreenUpdating = False
Sheet2.Unprotect Password:=""
For Each cell In Sheet2.Range("prodDesc")
qty = Application.VLookup(cell.Value, Range("descTable"), 2, False)
If Not IsError(qty) Then cell.Offset(, 13).Value = qty + cell.Offset(, 13).Value
Next cell
Sheet2.Protect Password:="", DrawingObjects:=True, Contents:=True, Scenarios:=True
Cells(Rows.Count, "C").End(xlUp).Offset(3).Select
Application.ScreenUpdating = True
Application.EnableEvents = True
End If
End Sub
Related
I export a schedule from MS Teams to Excel for data manipulation.
I made a macro that changes the dates field to a date format for the EU and sorts by it by date.
Then it goes to the next worksheet and checks the names of employees and creates a worksheet for each of the names.
Then it jumps back to the first worksheet, sorts by "name" criteria and copies the data for every single one to its own respective worksheet.
This is what I got so far that is OK:
Sub Temp1()
'Convert Cell Format from Text to Date and change MDY to DMY Format
Sheets("Shifts").Select
Range("D2:D1000").Select
Selection.TextToColumns Destination:=Range("D2"), DataType:=xlFixedWidth, _
FieldInfo:=Array(0, 3), TrailingMinusNumbers:=True
Range("F2:F1000").Select
Selection.TextToColumns Destination:=Range("F2"), DataType:=xlFixedWidth, _
FieldInfo:=Array(0, 3), TrailingMinusNumbers:=True
'Add the Sheets for each member of the "Members" Sheet
Sheets("Members").Select
Dim xRg As Excel.Range
Dim wSh As Excel.Worksheet
Dim wBk As Excel.Workbook
Set wSh = ActiveSheet
Set wBk = ActiveWorkbook
Application.ScreenUpdating = False
For Each xRg In wSh.Range("A2:A22")
With wBk
.Sheets.Add After:=.Sheets(.Sheets.Count)
ActiveCell.FormulaR1C1 = "Evidencija radnog vremena"
Selection.Font.Size = 20
Selection.Font.Bold = True
ActiveCell.Offset(1, 0).Range("A1").Select
ActiveCell.FormulaR1C1 = "Godina i mjesec"
Selection.Font.Size = 14
Selection.Font.Bold = True
ActiveCell.Offset(1, 0).Range("A1").Select
ActiveCell.FormulaR1C1 = "Radnik"
Selection.Font.Bold = True
ActiveCell.Offset(2, 0).Range("A1").Select
On Error Resume Next
ActiveSheet.Name = xRg.Value
Range("B2").Value = ActiveSheet.Name
If Err.Number = 1004 Then
Debug.Print xRg.Value & " already used as a sheet name"
End If
On Error GoTo 0
End With
Next xRg
Application.ScreenUpdating = True
'Sort by Date
Sheets("Shifts").Select
Range("A1").Sort Key1:=Range("D1"), Order1:=xlAscending, Header:=xlYes
After this I need some kind of loop or switch case or Foreach - i don't know what exactly.
I have it hardcoded for now, but it will become bulky, slow and problematic to maintain.
What I need to do:
Go through the list of employees, find for the employee all data and copy it to his respective worksheet - which has already been created.
Here is the hardcoded version of the code:
ActiveSheet.Range("$A$1:$L$276").AutoFilter Field:=1, Criteria1:= _
"Employee name"
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("Employee name").Select
ActiveSheet.Paste
ActiveSheet.PageSetup.Orientation = xlPortrait
Columns("A:L").AutoFit
For Each r In Range("I:I").SpecialCells(xlCellTypeConstants)
r.Interior.ColorIndex = xlNone
If r.Value Like "*Home Office*" Then r.Interior.Color = vbGreen
If r.Value Like "*Neradni dan*" Then r.Interior.Color = vbRed
If r.Value Like "*Bolovanje*" Then r.Interior.Color = vbBlue
If r.Value Like "*Godišnji odmor*" Then r.Interior.ColorIndex = 29
Next
Columns("L").EntireColumn.Delete
Columns("J").EntireColumn.Delete
Columns("H").EntireColumn.Delete
Columns("C").EntireColumn.Delete
Columns("B").EntireColumn.Delete
Columns("G").EntireColumn.Delete
I copied the whole code below.
A clarification of what it needs to do:
sort the data in the first worksheet - already handled
create the worksheets by the names in the 3rd worksheet - working
On the first sheet, that is already "sorted" - I need to go through all the names, copy the the data that is relevant to the sheet - i.e the sheets are named by names that are found in row a. so i need it to go through the first worksheet, need all the data that has the same name in the row a and copy it to the respective sheet. - PLEASE HELP :)
Sub TEMPExcelObradiTablicuZaObracunPlaca()
'Convert Cell Format from Text to Date and change MDY to DMY Format
Sheets("Shifts").Select
Range("D2:D1000").Select
Selection.TextToColumns Destination:=Range("D2"), DataType:=xlFixedWidth, _
FieldInfo:=Array(0, 3), TrailingMinusNumbers:=True
Range("F2:F1000").Select
Selection.TextToColumns Destination:=Range("F2"), DataType:=xlFixedWidth, _
FieldInfo:=Array(0, 3), TrailingMinusNumbers:=True
'Ovdje dodajem potrebne Sheetove iz Members Sheeta
Sheets("Members").Select
Dim xRg As Excel.Range
Dim wSh As Excel.Worksheet
Dim wBk As Excel.Workbook
Set wSh = ActiveSheet
Set wBk = ActiveWorkbook
Application.ScreenUpdating = False
For Each xRg In wSh.Range("A2:A22")
With wBk
.Sheets.Add After:=.Sheets(.Sheets.Count)
ActiveCell.FormulaR1C1 = "Evidencija radnog vremena"
Selection.Font.Size = 20
Selection.Font.Bold = True
ActiveCell.Offset(1, 0).Range("A1").Select
ActiveCell.FormulaR1C1 = "Radnik"
Selection.Font.Size = 14
Selection.Font.Bold = True
ActiveCell.Offset(1, 0).Range("A1").Select
ActiveCell.FormulaR1C1 = "Godina i mjesec"
Selection.Font.Bold = True
ActiveCell.Offset(2, 0).Range("A1").Select
'Range("B2).Value = DateAdd(mmmm, yyyy) -> OVDJE SAM ZAPEO TU NASTAVITI!!! - dodavanje datuma u b2 celiju!
On Error Resume Next
ActiveSheet.Name = xRg.Value
Range("B2").Value = ActiveSheet.Name
If Err.Number = 1004 Then
Debug.Print xRg.Value & " already used as a sheet name"
End If
On Error GoTo 0
End With
Next xRg
Application.ScreenUpdating = True
'Sort by Date
Sheets("Shifts").Select
Range("A1").Sort Key1:=Range("D1"), Order1:=xlAscending, Header:=xlYes
'Define LASTROW to find the last row and column in Members Sheetu!
Dim LastRow As Long, LastColumn As Long
LastRow = Cells.Find(What:="*", After:=Range("A1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
LastColumn = Cells.Find(What:="*", After:=Range("A1"), SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Range("A1").Resize(LastRow, LastColumn).Select
'Proba ForEach petlje
' Creating a range of sheet names from the data on Members
Dim SheetNamesRange As Range
Set SheetNamesRange = Sheets("Members").Range("A2:A" & LastRow)
' Iterate through all sheets in the range and write the word "Updated" in cell B2
Dim SheetName As Variant, SheetNameString As String
For Each SheetName In SheetNamesRange
' OVDJE SAM ISKOMENTIRAO OVA 2 REDA
'SheetNameString = CStr(SheetName)
'ThisWorkbook.Sheets(SheetNameString).Range("Q2") = "Updated"
Sheets("Shifts").Range("$A$1:$L$276").AutoFilter Field:=1, Criteria1:="SheetNameString"
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
'SheetNameString = CStr(SheetName)
Sheets.CStr(SheetNameString).Select
ActiveSheet.Paste
ActiveSheet.PageSetup.Orientation = xlPortrait
Columns("A:L").AutoFit
For Each r In Range("I:I").SpecialCells(xlCellTypeConstants)
r.Interior.ColorIndex = xlNone
If r.Value Like "*Home Office*" Then r.Interior.Color = vbGreen
If r.Value Like "*Neradni dan*" Then r.Interior.Color = vbRed
If r.Value Like "*Bolovanje*" Then r.Interior.Color = vbBlue
If r.Value Like "*Godišnji odmor*" Then r.Interior.ColorIndex = 29
Next
Columns("L").EntireColumn.Delete
Columns("J").EntireColumn.Delete
Columns("H").EntireColumn.Delete
Columns("C").EntireColumn.Delete
Columns("B").EntireColumn.Delete
Columns("G").EntireColumn.Delete
Next SheetName
End Sub
You are right, a For Each loop can be used here. Here is some code that outlines the basic principle:
Private Sub Shone()
' Creating a range of sheet names from the data on Sheet1
Dim SheetNamesRange As Range
Set SheetNamesRange = ThisWorkbook.Sheets("Sheet1").Range("A1:A3")
' Iterate through all sheets in the range and write the word "Updated" in cell B2
Dim SheetName As Variant, SheetNameString As String
For Each SheetName In SheetNamesRange
SheetNameString = CStr(SheetName)
ThisWorkbook.Sheets(SheetNameString).Range("B2") = "Updated"
Next SheetName
End Sub
In this example, I want to grab the names of sheets written on Sheet1, and write the word "Updated" in cell B2 on each of those sheets.
The cells A1, A2, and A3 on the sheet Sheet1 contain the following text, respectively, "Sheet1", "Sheet2", "Sheet3". First, I create a Range of data. That data is just the sheet names in cells A1:A3. It goes without saying that your Range will contain different data, but I believe that you have already taken care of that part.
Next, I iterate through that Range of data. A For Each loop requires the iterator (in this case, the variable SheetName) to be a Variant datatype. As I iterate through all of the sheets, I finally get to what I want to do: write the word "Updated" in cell B2. Finally, we reach the Next statement which tells us that the next step of the For Each loop will start, if there are any more members in the SheetNamesRange to iterate through.
I'm using visual basic to create a checkout system in an excel sheet. The sheet will be filled with information for a project, each of the projects requires that we send out a kit. This excel sheet will allow for a barcode to be scanned, when this happens, it checks for puts an "out" time. When that barcode is scanned again it puts an "in" time. The issue I'm having is that if that barcode is scanned a third time, it will only update the out time.
How do I set it up where it will see that an "in" and "out" time have been recorded and thus go the next blank cell in the row and add the barcode + new "in" or "out" time. Any help would be greatly appreciated!
This is the code I am using.
Code for on the worksheet
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("B2")) Is Nothing Then
Application.EnableEvents = False
Call inout
Application.EnableEvents = True
End If
End Sub
code for the macro
Sub inout()
Dim barcode As String
Dim rng As Range
Dim rownumber As Long
barcode = Worksheets("Sheet1").Cells(2, 2)
Set rng = Sheet1.Columns("a:a").Find(What:=barcode, _
LookIn:=xlFormulas, LookAt:=xlWhole, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
If rng Is Nothing Then
ActiveSheet.Columns("a:a").Find("").Select
ActiveCell.Value = barcode
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = Date & " " & Time
ActiveCell.NumberFormat = "m/d/yyyy h:mm AM/PM"
Worksheets("Sheet1").Cells(2, 2) = ""
Else
rownumber = rng.Row
Worksheets("Sheet1").Cells(rownumber, 1).Select
ActiveCell.Offset(0, 2).Select
ActiveCell.Value = Date & " " & Time
ActiveCell.NumberFormat = "m/d/yyyy h:mm AM/PM"
Worksheets("Sheet1").Cells(2, 2) = ""
End If
Worksheets("Sheet1").Cells(2, 2).Select
End Sub
All this goes in the worksheet code module:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("B2")) Is Nothing Then
inout 'use of Call is deprecated
End If
End Sub
Sub inout()
Dim barcode As String
Dim rng As Range
Dim newRow As Boolean
barcode = Me.Cells(2, 2)
'find the *last* instance of `barcode` in ColA
Set rng = Me.Columns("A").Find(What:=barcode, after:=Me.Range("A1"), _
LookIn:=xlFormulas, LookAt:=xlWhole, SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, MatchCase:=False, SearchFormat:=False)
'figure out if we need to add a new row, or update an existing one
If rng Is Nothing Then
newRow = True 'no matching barcode
Else
'does the last match already have an "in" timestamp?
If Len(rng.Offset(0, 2).Value) > 0 Then newRow = True
End If
If newRow Then
Set rng = Me.Cells(Me.Rows.Count, "A").End(xlUp).Offset(1, 0)
rng.Value = barcode
SetTime rng.Offset(0, 1) 'new row, so set "out"
Else
SetTime rng.Offset(0, 2) 'existing row so set "in"
End If
Me.Cells(2, 2).Select
End Sub
'set cell numberformat and set value to current time
Sub SetTime(c As Range)
With c
.NumberFormat = "m/d/yyyy h:mm AM/PM"
.Value = Now
End With
End Sub
I have a set of data in columns A to Z, if any cells in Column F is bolded, shall call to bold the entire row.
For example, F3 and F80 is bolded. A3:Z3 and A80:Z80 shall be bolded. My code only works until bolding cells in column F, can't proceed to bold the entire row.
Sub Bold()
Dim CheckRange As Range
Dim cell As Range
With ActiveSheet
Set CheckRange = .Range("F2:F" & .Cells(.Rows.Count, "F").End(xlUp).Row)
End With
With CheckRange
.FormatConditions.Delete
.FormatConditions.Add Type:=xlCellValue, Operator:=xlGreaterEqual, formula1:="1000000"
With .FormatConditions(1)
.Font.Bold = True
.StopIfTrue = False
End With
For Each cell In CheckRange
If cell(cell.Row, 6).Font.Bold = True Then
cell.EntireRow.Font.Bold = True
End If
Next
End Sub
Any help is much appreciated.
May be using a formula for the conditional formatting is better
Sub Bold()
With ActiveSheet.UsedRange
.FormatConditions.Delete
.Range("A1:Z" & .Cells(Rows.Count, 6).End(xlUp).Row).FormatConditions.Add Type:=xlExpression, Formula1:="=$F1>=1000000"
With .FormatConditions(1)
.Font.Bold = True
.StopIfTrue = False
End With
End With
End Sub
Or to adhere to your code you can use offset and resize in the loop
Sub Bold()
Dim checkRange As Range, cell As Range
With ActiveSheet
Set checkRange = .Range("F2:F" & .Cells(.Rows.Count, "F").End(xlUp).Row)
End With
With checkRange
.FormatConditions.Delete
.FormatConditions.Add Type:=xlCellValue, Operator:=xlGreaterEqual, Formula1:="1000000"
With .FormatConditions(1)
.Font.Bold = True
.StopIfTrue = False
End With
End With
For Each cell In checkRange
If cell.DisplayFormat.Font.Bold = True Then
cell.Offset(, -5).Resize(1, 26).Font.Bold = True
End If
Next cell
End Sub
The code sample is missing an end with after the first one to run.
Other than that, the issue is here: If cell(cell.Row, 6).Font.Bold
cell is already a Range type reference to the cell you need, so you don't need to look up anything, in fact doing that causes it to point elsewhere with the cell function: for example this is from the watch window, note the value difference:
Watch : : cell.Address : "$F$2" : String : Module1.Bold
Watch : : cell(cell.Row, 6).Address : "$K$3" : Variant/String : Module1.Bold
This is the full code:
Sub Bold()
Dim CheckRange As Range
Dim cell As Range
With ActiveSheet
Set CheckRange = .Range("F2:F" & .Cells(.Rows.Count, "F").End(xlUp).Row)
End With
With CheckRange
.FormatConditions.Delete
.FormatConditions.Add Type:=xlCellValue, Operator:=xlGreaterEqual, Formula1:="1000000"
With .FormatConditions(1)
.Font.Bold = True
.StopIfTrue = False
End With
End With
For Each cell In CheckRange
If cell.Font.Bold = True Then
cell.EntireRow.Font.Bold = True
End If
Next
End Sub
I would like to insert the user name of the 'creator' (person who inserts) new row. I want the user name entered in column G.
I currently have the following macro to insert the row:
Sub Insert_Row()
Dim rActive As Range
Set rActive = ActiveCell
Application.ScreenUpdating = False
With Cells(Rows.Count, "H").End(xlUp)
.EntireRow.Copy
With .Offset(1, 0).EntireRow
.PasteSpecial xlPasteFormats
.PasteSpecial xlPasteFormulas
On Error Resume Next
.SpecialCells(xlCellTypeConstants).ClearContents
On Error GoTo 0
End With
End With
rActive.Select
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
You just need to track the inserted row and set column G to the username, like this:
Sub Insert_Row()
Dim rActive As Range
Dim insertRow As Long
Set rActive = ActiveCell
Application.ScreenUpdating = False
With Cells(Rows.Count, "H").End(xlUp)
insertRow = .Row + 1
.EntireRow.Copy
With .Offset(1, 0).EntireRow
.PasteSpecial xlPasteFormats
.PasteSpecial xlPasteFormulas
On Error Resume Next
.SpecialCells(xlCellTypeConstants).ClearContents
On Error GoTo 0
End With
End With
Cells(insertRow, "G").Value = Environ("Username")
rActive.Select
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
I am attempting to create a userform to filter/display data from a worksheet that meets the input criteria.
I have successfully entered a field for user name and a date range, however I can't figure out how to create a hard stop if there is no data to display.
for example the criteria is a username or date in which there is no data for, rather than filtering the data table and displaying nothing, I want the action canceled and I would probably have a msgbox pop up.
Here's the simple test code I put together so far, I do have it so that it doesn't filter if the field is left blank so that is not my issue.
Private Sub CommandButton1_Click()
Dim DataRange As Range
Set DataRange = Range("datatable1")
Sheets("data").Visible = True
Sheets("data").Select
If ComboBox1.Value = "" Or ComboBox1.Value = Null Then
AutoFilter = False
Else: DataRange.AutoFilter Field:=9, Criteria1:=ComboBox1.Value
End If
datefilter:
If TextBox1.Value = "" And TextBox2.Value = "" Then
AutoFilter = False
ElseIf TextBox2.Value = "" Then
DataRange.AutoFilter Field:=8, Criteria1:=">=" & TextBox1.Value, Operator:=xlAnd
ElseIf TextBox1.Value = "" Then
DataRange.AutoFilter Field:=8, Criteria1:="<=" & TextBox2.Value, Operator:=xlAnd
Else: DataRange.AutoFilter Field:=8, Criteria1:=">=" & TextBox1.Value _
, Operator:=xlAnd, Criteria2:="<=" & TextBox2.Value
End If
Unload Me
End Sub
Really appreciate any assistance.
I think this does what you want. It applies the non-blank filters and if no data is visible it turns the filters off and displays a message:
Private Sub CommandButton1_Click()
Dim DataRange As Range
Set DataRange = Sheets("data").Range("datatable1")
Sheets("data").Visible = True
Sheets("data").Select
If ComboBox1.Value <> "" Then
DataRange.AutoFilter Field:=9, Criteria1:=ComboBox1.Value
End If
If TextBox1.Value <> "" Then
DataRange.AutoFilter Field:=8, Criteria1:=">=" & TextBox1.Value
End If
If TextBox2.Value <> "" Then
DataRange.AutoFilter Field:=8, Criteria1:="<=" & TextBox2.Value
End If
If DataRange.Columns(1).SpecialCells(xlCellTypeVisible).Cells.Count = 1 Then
'turn off filter
DataRange.AutoFilter
MsgBox "No matching data"
End If
Unload Me
End Sub
EDIT: Modify to work with structured table (VBA ListObject)
After our discussion in comments it sounds to me like your data is in a stuctured table. You can confirm this by clicking a cell in your data. A Table Tools/Design tab will appear in your ribbon towards the right end. To the far left of the controls for that tab will be a box with the table name, in this case "DataTable1."
As you've noted, Excel automatically creates a range with that same name, however it only includes the data, not the header. My code above fails because a filter that returns nothing has no visible data cells, whereas if the headers are included it will have one row of visible cells, the header row.
The code below sets a reference to the whole table, applies the autofilter to it and checks whether there is only one visible row:
Private Sub CommandButton1_Click()
Dim loDataTable1 As ListObject
Set loDataTable1 = Sheets("data").ListObjects("DataTable1")
Sheets("data").Visible = True
Sheets("data").Select
If ComboBox1.Value <> "" Then
loDataTable1.Range.AutoFilter Field:=9, Criteria1:=ComboBox1.Value
End If
If TextBox1.Value <> "" Then
loDataTable1.Range.AutoFilter Field:=8, Criteria1:=">=" & TextBox1.Value '
End If
If TextBox2.Value <> "" Then
loDataTable1.Range.AutoFilter Field:=8, Criteria1:="<=" & TextBox2.Value '
End If
If loDataTable1.ListColumns(1).Range.SpecialCells(xlCellTypeVisible).Cells.Count = 1 Then
'turn off filter
loDataTable1.Range.AutoFilter
loDataTable1.ShowAutoFilter = True
MsgBox "No matching data"
End If
Unload Me
End Sub