copy the next cell down and paste on the same cell - excel

I have been using below code to just copy the next cell down and paste on the same cell everytime.
Let me explain it. For Example Sheet1 has for values or more in the Range("L1:L" & lastrow) i will run the code if range("E4") isempty then L1 value will be updated in E4
then again run the code now L2 value will be updated in E4.
then again run the code now L3 value will be updated in E4.
then again run the code now L4 value will be updated in E4.
and so on.
If L4 has last value then exit sub as below code is working.
is there any easiest way to do this. Looking forward to your help.
Dim sht1 As Worksheet
Set sht1 = Sheet1
Dim r As Range
Set r = Range(sht1.Cells(1, 12), sht1.Cells(1, 12).End(xlDown))
Dim offset_row As Variant
If IsEmpty(sht1.Cells(4, 5).Value) Then
offset_row = 0
Else
offset_row = Application.WorksheetFunction.Match(sht1.Cells(4, 5).Value, r, 0)
End If
If Not IsError(offset_row) Then
If offset_row <> r.Rows.Count Then
sht1.Cells(1, 12).Offset(offset_row, 0).copy Destination:=sht1.Cells(4, 5)
End If
End If

Write Next Cell Value
Note that Application.WorksheetFunction.Match or WorksheetFunction.Match is the same and if the value is not found, an error will occur i.e. your testing for an error value has no effect (not gonna happen). It should be handled with On Error.
On the other hand, Application.Match will result in either a number or an error value and can be tested with IsError or IsNumeric.
The Code
Option Explicit
Sub writeNextCellValue()
Const dCellAddress As String = "E4"
Const sFirstCellAddress As String = "L1"
Dim srg As Range ' Source Column Range
Dim dCell As Range ' Destination Cell Range
Dim isSourceColumnRangeValid As Boolean ' Source Column Range Validator
With Sheet1.Range(sFirstCellAddress)
Set srg = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If Not srg Is Nothing Then
Set srg = .Resize(srg.Row - .Row + 1)
isSourceColumnRangeValid = True
Set dCell = .Worksheet.Range(dCellAddress)
End If
End With
If isSourceColumnRangeValid Then
If dCell.Value = "" Then ' Value is "".
dCell.Value = srg.Cells(1).Value
Else ' Value is not "".
Dim cIndex As Variant
cIndex = Application.Match(dCell.Value, srg, 0)
If IsNumeric(cIndex) Then ' Value found.
If cIndex = srg.Rows.Count Then ' Last value found.
'dCell.Value = ""
'dCell.Value = srg.Cells(1).Value
Else ' Not last value found.
dCell.Value = srg.Cells(cIndex + 1)
End If
Else ' Value not found.
'dCell.Value = ""
End If
End If
End If
End Sub

Please try the next code:
Sub FillNextCellVal()
Dim sht1 As Worksheet, lastR As Long, rng As Range
Dim ECell As Range, cExist As Range, i As Long
Set sht1 = Sheet1
Set ECell = sht1.Range("E4")
lastR = sht1.Range("L" & sht1.rows.count).End(xlUp).row
Set rng = sht1.Range("L1:L" & lastR)
If ECell.value = "" Then
ECell.value = rng.SpecialCells(xlCellTypeConstants).Areas(1).value
Else
Set cExist = rng.Find(What:=ECell.value, After:=rng.cells(1), _
LookAt:=xlWhole, LookIn:=xlValues, SearchOrder:=xlByRows)
If Not cExist Is Nothing Then
For i = 1 To lastR - cExist.row
If cExist.Offset(i).value <> "" Then
ECell.value = cExist.Offset(i).value
Exit For
End If
Next i
End If
End If
End Sub

Related

searching for data in a lot of sheets and copying entire row if data is found to a separate work sheet in VBA

Hi I'm relatively new to VBA and programing and im having an "overflow" issue with my code
I'm trying to to go through the first 31 work sheets search for the term "Power On" in column C and when it find a match copy the entire row and paste it into Sheet33 it was working at one point for just a single sheet but now i cant get it to work after modifying it for the first 31 sheets
any help would be greatly appreciated!
Sub test()
Dim LSearchRow As Integer
Dim LCopyToRow As Integer
Dim ws1 As Worksheet
Dim I As Integer
LCopyToRow = 1
For I = 1 To 31
Set ws1 = ActiveSheet
LSearchRow = 1
While Len(Range("A" & CStr(LSearchRow)).Value) > 0
'If value in column C = "Power On", copy entire row to Sheet33
If Range("C" & CStr(LSearchRow)).Value = "Power On" Then
'Select row in ws1 to copy
Rows(CStr(LSearchRow) & ":" & CStr(LSearchRow)).Select
Selection.Copy
'Paste row into Sheet33 in next row
Sheets("Sheet33").Select
Rows(CStr(LCopyToRow) & ":" & CStr(LCopyToRow)).Select
ActiveSheet.Paste
LCopyToRow = LCopyToRow + 1
'Go back to ws1
Sheets(ws1).Select
End If
LSearchRow = LSearchRow + 1
Wend
Exit Sub
Next I
End Sub
'Overflow' error happens when your declared data variable of a certain datatype can no longer hold the SIZE of the value you are putting in it.
Based on your code, LSearchRow and LCopyToRow are declared as INTEGER which can hold up to 32767 (rows). to fix this declare it as LONG instead of INTEGER:
Dim LSearchRow As Long
Dim LCopyToRow As Long
Here's an update to my answer. I made an alternative version of your code:
Sub GetPowerOn()
Dim ws As Worksheet
Dim wsResult As Worksheet
Dim nrow As Long
Dim actvCell As Range
Dim actvLrow As Long
Set wsResult = ThisWorkbook.Worksheets("Sheet33")
Application.ScreenUpdating = False
For Each ws In ThisWorkbook.Worksheets '~Loop through the sheets of the workbook
If Not ws.Name = "Sheet33" Then '~As long as the sheet is not Sheet33, fire the search,copy,paste function below
actvLrow = ws.Range("A" & Rows.Count).End(xlUp).Row '~ Set the lastrow of the active sheet
For Each actvCell In ws.Range("C1:C" & actvLrow) '~ Loop through the cells of column C
If actvCell.Value = "Power On" Then '~Look for criteria
ws.Rows(actvCell.Row & ":" & actvCell.Row).Copy '~Copy the row that matches the criteria
nrow = wsResult.Range("A" & Rows.Count).End(xlUp).Offset(1).Row '~Get the lastrow empty row of the output sheet
wsResult.Range("A" & nrow).PasteSpecial xlPasteValuesAndNumberFormats '~Paste to the next empty row
Application.CutCopyMode = False
End If
Next actvCell
End If
Next ws
Application.ScreenUpdating = True
End Sub
' The reason you are getting the same sheet is you are setting WS1 to ActiveSheet
' 31 times in a row -- not getting the first 31 sheets.
' ActiveSheet is whatever sheet you last happened to have in focus. Unless you
' know you want that (almost never), you should not use it.
' You want to avoids things like copy / paste / select. These are slow.
' You also want to avoid processing things row by row.
' Here is an example that should do what you want.
Sub ThirtyOneFlavors()
Const PowerColNum = 3 ' if you are sure it will always be column 3
Dim WS1 As Worksheet, WS33 As Worksheet
Dim PowerColumn As Range, PowerCell As Range, FirstCell As Range, R As Long
Set WS33 = ThisWorkbook.Sheets("Sheet33") ' Maybe this could use a clever name
WS33.Cells.Delete ' only if you want this
' using ThisWorkbook avoids accidentally getting some other open workbook
For Each WS1 In ThisWorkbook.Sheets
' here, put the names of any sheets you don't want to process
If WS1.Name <> WS33.Name Then
Set PowerColumn = WS1.UsedRange.Columns(PowerColNum)
' I am assuming Power On is the whole column
Set PowerCell = PowerColumn.Find("Power On", LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False)
If Not PowerCell Is Nothing Then ' if you found something
' we need to keep track of the first one found,
' otherwise Excel will keep finding the same one repeatedly
Set FirstCell = PowerCell
End If
While Not PowerCell Is Nothing ' if you keep finding cells
R = R + 1 ' next row
'.Value will hold all of the values in a range (no need to paste)
WS33.Cells(R, 1).EntireRow.Value = PowerCell.EntireRow.Value
' get the next one
Set PowerCell = PowerColumn.Find("Power On", after:=PowerCell, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False)
If PowerCell.Address = FirstCell.Address Then
' if we found the first one again, kill the loop
Set PowerCell = Nothing
End If
Wend
End If
Next WS1
End Sub
'Consolidate' Data
Option Explicit
Sub ConsolidateData()
' Source
Const sfIndex As Long = 1
Const slIndex As Long = 31
Const sFirstCell As String = "C2"
Const sCriteria As String = "Power On"
' Destination
Const dIndex As Long = 33
Const dFirstCell As String = "A2" ' has to be column 'A' ('EntireRow')
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Create a reference to the initial destination cell.
Dim dws As Worksheet: Set dws = wb.Worksheets(dIndex)
Dim dfCell As Range: Set dfCell = dws.Range(dFirstCell)
Dim dCell As Range: Set dCell = RefLastCellInColumn(dfCell)
If dCell Is Nothing Then ' no data found
Set dCell = dfCell
Else ' data found
Set dCell = dCell.Offset(1)
End If
Dim sws As Worksheet
Dim srg As Range
Dim scrg As Range
Dim sCell As Range
Dim n As Long
Application.ScreenUpdating = False
' Process each source worksheet...
For n = sfIndex To slIndex
Set sws = wb.Worksheets(n)
Set scrg = RefColumn(sws.Range(sFirstCell))
' Test for data...
If Not scrg Is Nothing Then ' data in column found
' Process each cell in source column range...
For Each sCell In scrg.Cells
' Check current cell agains criteria. To ignore case,
' i.e. 'POWER ON = power on', 'vbTextCompare' is used.
If StrComp(CStr(sCell.Value), sCriteria, vbTextCompare) = 0 Then
' Combine current cell into current source range.
' The combining is restricted to per worksheet ('Union').
Set srg = RefCombinedRange(srg, sCell)
End If
Next sCell
' Test for matches...
If Not srg Is Nothing Then ' match found
' Copy. This will work only if all source cells contain values.
' If some of them contain formulas, the results may be mixed
' (some rows containing the formulas, some only values) due to
' the source range being non-contiguous.
' This is prevented by either not combining the cells or
' by using 'PasteSpecial'.
srg.EntireRow.Copy dCell
' Create a reference to the next destination cell.
Set dCell = dCell.Offset(srg.Cells.Count)
' Unreference source range (before processing next worksheet).
Set srg = Nothing
'Else ' no match found
End If
'Else ' no data in column found
End If
Next n
' Activate destination worksheet.
'If Not dws Is ActiveSheet Then dws.Activate
' Save workbook.
'wb.Save
Application.ScreenUpdating = True
MsgBox "Data consolidated.", vbInformation, "Consolidate Data"
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Creates a reference to the bottom-most non-empty cell
' in the one-column range from the first cell ('FirstCell')
' through the bottom-most cell of the worksheet column.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefLastCellInColumn( _
ByVal FirstCell As Range) _
As Range
If FirstCell Is Nothing Then Exit Function
With FirstCell.Cells(1)
Dim lCell As Range
Set RefLastCellInColumn = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
End With
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Creates a reference to the one-column range from the first cell
' of a range ('FirstCell') to the bottom-most non-empty cell
' of the first cell's worksheet column.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefColumn( _
ByVal FirstCell As Range) _
As Range
If FirstCell Is Nothing Then Exit Function
With FirstCell.Cells(1)
Dim lCell As Range
Set lCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then Exit Function
Set RefColumn = .Resize(lCell.Row - .Row + 1)
End With
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Creates a reference to a range combined from two ranges.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefCombinedRange( _
ByVal CombinedRange As Range, _
ByVal AddRange As Range) _
As Range
If CombinedRange Is Nothing Then
Set RefCombinedRange = AddRange
Else
Set RefCombinedRange = Union(CombinedRange, AddRange)
End If
End Function
An alternative method using Find and `FindNext'
Option Explicit
Sub test()
Const MAX_SHT = 3
Const PASTE_SHT = 4
Const TERM = "Power On"
Const COL = "C"
Dim wb As Workbook, ws As Worksheet
Dim n As Integer, LastRow As Long, count As Long
Dim rngFound As Range, rngTarget As Range, sFirst As String
Set wb = ThisWorkbook
' check number of sheets
If wb.Sheets.count < MAX_SHT Then
MsgBox "Too few sheets", vbCritical
Exit Sub
End If
' copy destination
With wb.Sheets(PASTE_SHT)
LastRow = .Cells(Rows.count, COL).End(xlUp).Row
Set rngTarget = .Cells(LastRow + 1, "A")
End With
' first 31 sheets
For n = 1 To MAX_SHT
Set ws = wb.Sheets(n)
LastRow = ws.Cells(Rows.count, COL).End(xlUp).Row
With ws.Range("C1:C" & LastRow)
' search for term
Set rngFound = .Find(TERM, lookin:=xlValues, LookAt:=xlWhole)
If Not rngFound Is Nothing Then
sFirst = rngFound.Address
Do
ws.Rows(rngFound.Row).EntireRow.Copy rngTarget
Set rngTarget = rngTarget.Offset(1)
Set rngFound = .FindNext(rngFound)
count = count + 1
Loop While rngFound.Address <> sFirst
End If
End With
Next
MsgBox count & " rows copied", vbInformation
End Sub
ok just try the following code
many fixes are made and speedUps
Sub test()
' in a x64 environement better forget Integers and go for Longs
Dim LSearchRow As Long
Dim LCopyToRow As Long
Dim ws1 As Worksheet
Dim I As Long
Dim vldRng As Range
Dim maxRw As Long
Dim maxClmn As Long
Dim rngDest As Range
'2 Lines to speed code Immensly. Don't use them while debugging
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
LCopyToRow = 1
Set rngDest = ThisWorkbook.Sheets("Sheet33").Cells(1, 1)
'Set rngDest = ThisWorkbook.Sheets(33).Range("A1") 'Alternative 01
'Set rngDest = Sheets(33).Range("A1") 'Alternative 02
For I = 1 To 31
Set ws1 = ThisWorkbook.Sheets(I)
Set vldRng = ws1.UsedRange ' Get range used instead of searching entire Sheet
maxRw = vldRng.Rows.Count
maxClmn = vldRng.Columns.Count
For LSearchRow = 1 To maxRw
'If value in column C = "Power On", copy entire row to Sheet33
If vldRng.Cells(LSearchRow, 3).Value = "Power On" Then
'Select row in ws1 to copy
vldRng.Cells(LSearchRow, 1).Resize(1, maxClmn).Copy
'Paste row into Sheet33 in next row
rngDest.Offset(LCopyToRow - 1, 0).PasteSpecial xlPasteValues
LCopyToRow = LCopyToRow + 1
End If
Next LSearchRow
Next I
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub

What is the fastest way to pull data from one sheet to another using VBA

I have and excel file with 2 tabs, one is 166K rows and the other is 400K rows. Previously we were manually performing vlookups to pull data from the 400k row tab into the 166k row tab. I want to automate this some using VBA but am having issues with speed.
I tried an IF formula but that ran for over 30 minutes before I killed the process
For i = 2 To Assign.UsedRange.Rows.Count
For x = 2 To HR.UsedRange.Rows.Count
If Assign.Cells(i, 1 ) = HR.Cells(x,1) Then
Assign.Cells(i, 9) = HR.Cells(x, 3)
End If
Next x
Next i
and now I'm trying a vlookup for VBA but that also is taking a long time.
For x = 2 To Assign.UsedRange.Rows.Count
On Error Resume Next
Worksheets("Assignments").Cells(x, 9).Value =
Application.WorksheetFunction.VLookup(Worksheets("Assignments").Cells(x, 5).Value,
Worksheets("Workforce").Range("A:AX"), 5, 0)
On Error GoTo 0
Next x
any suggestions on how to speed this up? I tried using Access but the files were too big.
A VBA Lookup
This took roughly 13 seconds on my machine (Windows 10, Office 2019 64bit) for 400k vs 160k of integers.
An optimized (using arrays and Application.Match applied to the lookup column range) Match version took the same amount of time for 10 times fewer data.
Since your data probably isn't integers, your feedback is highly appreciated.
Adjust the values in the constants section.
Option Explicit
Sub VBALookup()
Const sName As String = "Workforce" ' Source Worksheet Name
Const slFirst As String = "E2" ' Source Lookup Column First Cell Address
Const svCol As String = "I" ' Source Value Column
Const dName As String = "Assignments" ' Destination Worksheet Name
Const dlFirst As String = "E2" ' Destination Lookup First Cell Address
Const dvCol As String = "I" ' Destination Value Column
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Create references to the Source Ranges.
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim sfCell As Range: Set sfCell = sws.Range(slFirst)
Dim slrg As Range: Set slrg = RefColumn(sfCell) ' lookup range
If slrg Is Nothing Then Exit Sub
Dim svrg As Range: Set svrg = slrg.EntireRow.Columns(svCol) ' read value
' Create references to the Destination Ranges.
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dfCell As Range: Set dfCell = dws.Range(dlFirst)
Dim dlrg As Range: Set dlrg = RefColumn(dfCell) ' lookup value
If dlrg Is Nothing Then Exit Sub
Dim dvrg As Range: Set dvrg = dlrg.EntireRow.Columns(dvCol) ' write value
' Write the 'INDEX/MATCH' formula to a variable.
Dim dFormula As String
dFormula = "=IFERROR(INDEX('" & sName & "'!" & svrg.Address(, 0) _
& ",MATCH(" & dfCell.Address(0, 0) _
& ",'" & sName & "'!" & slrg.Address(, 0) & ",0)),"""")"
' Take a look in the Immediate window ('Ctrl + G')
'Debug.Print "Source", slrg.Address(, 0), svrg.Address(, 0)
'Debug.Print "Destination", dlrg.Address(, 0), dvrg.Address(, 0)
'Debug.Print "Formula", dFormula
' Write the formula to the Destination Value Range
' and convert the formulas to values.
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
dvrg.Formula = dFormula
dvrg.Value = dvrg.Value
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Creates a reference to the range from the first cell
' of a range ('rg') through the bottom-most non-empty cell
' of the range's column.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefColumn( _
ByVal rg As Range) _
As Range
If rg Is Nothing Then Exit Function
With rg.Cells(1)
Dim lCell As Range
Set lCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then Exit Function
Set RefColumn = .Resize(lCell.Row - .Row + 1)
End With
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the values of a range ('rg') in a 2D one-based array.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetRange( _
ByVal rg As Range) _
As Variant
If rg Is Nothing Then Exit Function
Dim rData As Variant
If rg.Rows.Count + rg.Columns.Count = 2 Then
ReDim rData(1 To 1, 1 To 1): rData(1, 1) = rg.Value
Else
rData = rg.Value
End If
GetRange = rData
End Function
I would try with the find method instead of an inner loop. You have just to customize your file references and ranges.
Sub FindMatches()
Dim shtOld As Worksheet, shtNew As Worksheet
Dim oldRow As Integer
Dim newRow As Integer
Dim i As Integer, id, f As Range
i = 1
Set shtOld = ThisWorkbook.Sheets("Assign")
Set shtNew = ThisWorkbook.Sheets("HR")
For oldRow = 2 To shtOld.UsedRange.Rows.Count
id = shtOld.Cells(oldRow, 1)
Set f = shtNew.Range("A1:A1000").Find(id, , xlValues, xlWhole)
If Not f Is Nothing Then
With shtOld.Rows(i)
.Cells(1).Value = shtOld.Cells(oldRow, 1)
End With
i = i + 1
End If
Next oldRow
End Sub

Combine data from multiple worksheets to one sheet on key word from column

im sorry for making similar question but im run into a problem, bcs i don t know very good VBA coding...
I found many similar questions, and i found a code that i can apply to my needs.
I found code here But i don't know how to edit that code so that he can work in my Workbook. I have workbook with 35 worksheets, all with same format, values are in columns "A:F", in column "E" i have text "On Stock" and "Sent", i want all rows from all worksheets that have "On Stock" value in column "E" to be copied into one worksheet named "Blanko List". I tried to edit code myself, but it can t run, nothing happens. Thanks in advance.
Edited code
Sub CommandButton4_Click()
Dim wM As Worksheet, ws As Worksheet
Dim r As Long, lr As Long, nr As Long, y As Long
Dim c As Range, firstaddress As String
Application.ScreenUpdating = False
Set wM = Sheets("Blanko List")
lr = wM.Cells.Find("*", , xlValues, xlWhole, xlByRows, xlPrevious, False).Row
If lr > 1 Then wM.Range("A2:G" & lr).ClearContents
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> "Blanko List" Then
y = 0
On Error Resume Next
y = Application.CountIf(ws.Columns(7), "On Stock")
On Error GoTo 0
If y > 1 Then
firstaddress = ""
With ws.Columns(7)
Set c = .Find("On Stock", LookIn:=xlValues, LookAt:=xlWhole)
If Not c Is Nothing Then
firstaddress = c.Address
Do
nr = wM.Range("G" & Rows.Count).End(xlUp).Offset(1).Row
ws.Range("A" & c.Row & ":G" & c.Row).Copy wM.Range("A" & nr)
Application.CutCopyMode = False
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstaddress
End If
End With
End If
End If
Next ws
wM.Activate
Application.ScreenUpdating = True
''''
Original code:
Option Explicit
Sub GetYes()
Dim wM As Worksheet, ws As Worksheet
Dim r As Long, lr As Long, nr As Long, y As Long
Dim c As Range, firstaddress As String
Application.ScreenUpdating = False
Set wM = Sheets("Master")
lr = wM.Cells.Find("*", , xlValues, xlWhole, xlByRows, xlPrevious, False).Row
If lr > 1 Then wM.Range("A2:G" & lr).ClearContents
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> "Master" Then
y = 0
On Error Resume Next
y = Application.CountIf(ws.Columns(7), "Yes")
On Error GoTo 0
If y > 1 Then
firstaddress = ""
With ws.Columns(7)
Set c = .Find("Yes", LookIn:=xlValues, LookAt:=xlWhole)
If Not c Is Nothing Then
firstaddress = c.Address
Do
nr = wM.Range("G" & Rows.Count).End(xlUp).Offset(1).Row
ws.Range("A" & c.Row & ":G" & c.Row).Copy wM.Range("A" & nr)
Application.CutCopyMode = False
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstaddress
End If
End With
End If
End If
Next ws
wM.Activate
Application.ScreenUpdating = True
End Sub
Copy Criteria Rows
Option Explicit
Sub CopyCriteriaRows()
' Source
Const sCols As String = "A:F"
Const sfRow As Long = 2
Const scCol As Long = 5
Const sCriteria As String = "On Stock"
' Destination
Const dName As String = "Blanco List"
Const dFirst As String = "A2"
' Exceptions
Const ExceptionsList As String = "Blanco List" ' add more
Const ListSeparator As String = ","
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Write the names of the worksheets to be 'processed' to an array.
Dim wsNames As Variant
wsNames = ArrWorksheetNames(wb, ExceptionsList, ListSeparator)
If IsEmpty(wsNames) Then Exit Sub ' no worksheet found
' Create a reference to the first destination row range.
' Note that the number of columns is equal in source and destination.
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim cCount As Long: cCount = dws.Columns(sCols).Columns.Count
Dim drrg As Range: Set drrg = dws.Range(dFirst).Resize(, cCount)
Dim sws As Worksheet ' Source Worksheet
Dim srg As Range ' Source Range
Dim sfrrg As Range ' Source First Row Range
Dim drg As Range ' Destination Range
Dim Data As Variant ' Data Array
Dim cValue As Variant ' Current Value
Dim dr As Long ' Destination Row Counter
Dim sr As Long ' Source Row Counter
Dim c As Long ' Column Counter
For Each sws In wb.Worksheets(wsNames)
' Create a reference to the current Source First Row Range.
Set sfrrg = sws.Columns(sCols).Rows(sfRow)
Set srg = Nothing
' Create a reference to the current Source Range.
Set srg = RefColumns(sfrrg)
If Not srg Is Nothing Then ' the current Source Range is not empty
' Write the values from the current Source Range to the Data Array.
Data = GetRange(srg)
' Write the matches to the top of the Data Array. The size
' of the array stays the same but 'dr' is used: to track
' the number of, to move, and finally, to write (to the worksheet)
' the 'destination' values.
dr = 0
For sr = 1 To UBound(Data, 1)
cValue = Data(sr, scCol)
If StrComp(CStr(cValue), sCriteria, vbTextCompare) = 0 Then
dr = dr + 1
For c = 1 To cCount
Data(dr, c) = Data(sr, c)
Next c
End If
Next sr
If dr > 0 Then ' there have been matches
' Create a reference to the Destination Range.
Set drg = drrg.Resize(dr)
' Write only the 'destination' values (dr) from
' the Data Array to the Destination Range.
drg.Value = Data
' Create a reference to the next Destination First Row Range.
Set drrg = drrg.Offset(dr)
End If
End If
Next sws
' The 'Clear Range' is the range spanning
' from the last 'Destination First Row Range'
' (which was referenced, but was not written to)
' to the bottom-most row range of the worksheet.
Dim crg As Range
Set crg = drrg.Resize(dws.Rows.Count - drrg.Row + 1)
crg.ClearContents
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the names of the worksheets of a workbook ('wb'),
' that are not included in a list ('ExceptionsList'),
' in an array.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function ArrWorksheetNames( _
ByVal wb As Workbook, _
Optional ByVal ExceptionsList As String = "", _
Optional ByVal ListSeparator As String = ",", _
Optional ByVal FirstIndex As Long = 0) _
As Variant
If wb Is Nothing Then Exit Function
Dim wsCount As Long: wsCount = wb.Worksheets.Count
If wsCount = 0 Then Exit Function ' There could e.g. only be charts.
Dim IndexDiff As Long: IndexDiff = FirstIndex - 1
Dim LastIndex As Long: LastIndex = wsCount + IndexDiff
Dim Arr() As String: ReDim Arr(FirstIndex To LastIndex)
Dim n As Long: n = IndexDiff
Dim ws As Worksheet
If Len(ExceptionsList) = 0 Then
For Each ws In wb.Worksheets
n = n + 1
Arr(n) = ws.Name
Next ws
Else
Dim Exceptions() As String
Exceptions = Split(ExceptionsList, ListSeparator)
For Each ws In wb.Worksheets
If IsError(Application.Match(ws.Name, Exceptions, 0)) Then
n = n + 1
Arr(n) = ws.Name
End If
Next ws
End If
Select Case n
Case IndexDiff
Exit Function
Case Is < LastIndex
ReDim Preserve Arr(FirstIndex To n)
End Select
ArrWorksheetNames = Arr
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Creates a reference to the range spanning from the first row
' of a given range ('rg') to the row containing the bottom-most
' non-empty cell of the given range's columns.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefColumns( _
ByVal rg As Range) _
As Range
If rg Is Nothing Then Exit Function
With rg.Rows(1)
Dim lCell As Range
Set lCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , xlByRows, xlPrevious)
If lCell Is Nothing Then Exit Function ' empty range
Set RefColumns = .Resize(lCell.Row - .Row + 1)
End With
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the values of a range in a 2D one-based array.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetRange( _
ByVal rg As Range) _
As Variant
If rg Is Nothing Then Exit Function
Dim rData As Variant
If rg.Rows.Count + rg.Columns.Count = 2 Then ' one cell only
ReDim rData(1 To 1, 1 To 1): rData(1, 1) = rg.Value
Else
rData = rg.Value
End If
GetRange = rData
End Function
' Irrelevant to the Question,
' but for a better understanding of `ArrWorksheetNames`.
Sub ArrWorksheetNamesTEST()
Const ExceptionsList As String = "Sheet1,Sheet2,Sheet3,Sheet4"
Const ListSeparator As String = ","
Const FirstIndex As Long = 4
Dim wb As Workbook: Set wb = ThisWorkbook
Dim wsNames As Variant
wsNames = ArrWorksheetNames(wb, ExceptionsList, ListSeparator, FirstIndex)
If IsEmpty(wsNames) Then
Debug.Print "No worksheets."
Else
Debug.Print "[" & LBound(wsNames) & "," & UBound(wsNames) & "]" _
& vbLf & Join(wsNames, vbLf)
End If
End Sub
You can use this to develop an array of values and then dump them into some collection sheet.
Sub grabAllSheets()
Const exclude_Sheet = "Result" ' name of sheet to drop data
Const tangoText = "On Stock"
Dim ws As Worksheet, aCell As Range
ReDim allvalues(1 To 6, 1 To 1)
Dim i As Long, c As Long
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> exclude_Sheet Then
For Each aCell In Intersect(ws.Range("E:E"), ws.UsedRange).Cells
If aCell.Value = tangoText Then
i = i + 1
ReDim Preserve allvalues(1 To 6, 1 To i)
For c = 1 To Range("F:F").Column
allvalues(c, i) = ws.Cells(aCell.Row, c).Value
Next c
End If
Next aCell
End If
Next ws
Dim theRow As Long
With Sheets(exclude_Sheet)
theRow = .Cells(.Rows.Count, 1).End(xlUp).Row
.Cells(IIf(theRow = 1, 1, theRow + 1), 1).Resize(i, 6).Value = _
Application.WorksheetFunction.Transpose(allvalues)
End With
End Sub

Macro/VBA: highlight rows based on 2 conditions

Objective is to highlight rows that meet two different conditions:
If column A is equal to the previous workday (taking into consideration of holidays mentioned in the Reference sheet)
If column B is not equal to "AA"
I have the following code, but am unable to get appropriate rows highlighted (no rows get highlighted due to condition #1 not being met):
Sub code()
Dim lrow As Long
lrow = Cells(Rows.Count, "A").End(xlUp).Row
For i = 2 To lrow
If Cells(i, "A").Value = "=WORKDAY(today(),-1,Reference!$A$2:$A$12)" And Cells(i, "B").Value <> "AA" Then Cells(i, 1).EntireRow.Interior.ColorIndex = 6
Next i
End Sub
You could try this:
Option Explicit
Sub code()
Dim i As Long, lrow As Long
Dim objRangeHolidays As Range
Set objRangeHolidays = Worksheets("Reference").Range("$A$2", "$A$12")
lrow = Cells(rows.Count, "A").End(xlUp).row
For i = 2 To lrow
If CDate(Cells(i, "A").Value) = CDate(Application.WorksheetFunction.WorkDay(Date, -1, objRangeHolidays)) And Cells(i, "B").Value <> "AA" Then
Cells(i, 1).EntireRow.Interior.ColorIndex = 6
End If
Next i
Set objRangeHolidays = Nothing
End Sub
Your original code does not work as "=WORKDAY(today(),-1,Reference!$A$2:$A$12)" is a literal string on VBA, not a function call.
We use CDate() function to make our cell values comparable with WorksheetFunction.Workday() function.
WorksheetFunction.Today() is the same as Date() in VBA.
objRangeHolidays holds holidays defined in Reference sheet.
This is my test result:
Highlight Entire Rows
Adjust the values in the constants section.
Option Explicit
Sub highlightPreviousWorkday()
' Source
Const sName As String = "Sheet1"
Const sFirst As String = "A2"
Const sCritCol As String = "B"
Const sCriteria As String = "AA"
Const sColorIndex As Long = 6
' Holiday
Const hName As String = "Reference"
Const hFirst As String = "A2"
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
wb.Activate ' `Evaluate` will fail if not active.
' Source
Dim srg As Range
With wb.Worksheets(sName).Range(sFirst)
Dim slCell As Range
Set slCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If slCell Is Nothing Then Exit Sub
Set srg = .Resize(slCell.Row - .Row + 1)
End With
' Holiday
Dim Holiday As String
With wb.Worksheets(hName).Range(hFirst)
Dim hlCell As Range
Set hlCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If Not hlCell Is Nothing Then
Holiday = ",'" & hName & "'!" _
& .Resize(hlCell.Row - .Row + 1).Address
End If
End With
' Evaluation
Dim evDate As Variant
evDate = Evaluate("WORKDAY(TODAY(),-1" & Holiday & ")")
' Combine
Dim drg As Range
If VarType(evDate) = vbDouble Then
Dim sCell As Range
Dim sValue As Variant
Dim sString As String
For Each sCell In srg.Cells
sValue = sCell.Value
If VarType(sValue) = vbDate Then
If CDbl(sValue) = evDate Then
sString = CStr(sCell.EntireRow.Columns(sCritCol).Value)
If sString <> sCriteria Then
Set drg = getCombinedRange(drg, sCell)
End If
End If
End If
Next sCell
End If
' Color
Application.ScreenUpdating = False
srg.EntireRow.Interior.ColorIndex = xlNone
If Not drg Is Nothing Then
drg.EntireRow.Interior.ColorIndex = sColorIndex
End If
Application.ScreenUpdating = True
End Sub
Function getCombinedRange( _
ByVal BuiltRange As Range, _
ByVal AddRange As Range) _
As Range
If BuiltRange Is Nothing Then
Set getCombinedRange = AddRange
Else
Set getCombinedRange = Union(BuiltRange, AddRange)
End If
End Function

How to replace values using Arrays & Ranges?

I can replace values by mentioning them one by one.
I want to replace (oldarray) with (newarray) where both of them are derived from ranges.
i.e. oldarray = ("a2:a5") and newarray = ("b2:b5") instead of writing them one by one.
and also I need to replace each old value with adjacent cell value
i.e. a2 replaced by b2, and a3 replaced by b3.
is that possible?
Sub ReplaceValues()
Dim NewValues() As String
Dim NewValues() As String
OldValues = Split("BMV,MERCE", ",")
NewValues = Split("Jack,Sally", ",")
For i = 0 To UBound(OldValues)
With sheets("destination").Columns("Z:Z")
.Replace What:=OldValues(i), Replacement:=NewValues(i), LookAt:=xlWhole, SearchOrder:=xlByRows, MatchCase:=True
End With
Next
End Sub
Something like this should work:
Sub ReplaceValues()
Dim OldValues, NewValues, ws As Worksheet
Set ws = Thisworkbook.worksheets("Config") 'or whichever sheet...
OldValues = ws.Range("A2:A5").Value 'this gives a 2d array
NewValues = ws.Range("B2:B5").Value 'this too
For i = 1 To UBound(OldValues, 1)
With sheets("destination").Columns("Z:Z")
.Replace What:=OldValues(i, 1), Replacement:=NewValues(i, 1), _
LookAt:=xlWhole, SearchOrder:=xlByRows, MatchCase:=True
End With
Next
End Sub
Search and Replace (Application.Match)
If a value in the Destination column (Z) is found in the Search column (A), it will be replaced with the value in the same row of the Replace column (B).
This search (Application.Match) is not case-sensitive i.e. A = a.
Adjust the values in the constants section.
Only run replaceValues; the rest is being called by it.
The Code
Option Explicit
Sub replaceValues()
' Define constants.
' Source
Const srcName As String = "Sheet1"
Const sFirst As String = "A2"
Const rFirst As String = "B2"
' Destination
Const dstName As String = "Sheet2"
Const dFirst As String = "Z2"
' Workbook
Dim wb As Workbook
Set wb = ThisWorkbook
' Write from worksheets to arrays.
Dim ws As Worksheet ' Each Worksheet
Dim rng As Range ' Each Column Range
' Source
Dim sData As Variant ' Search Data Array
Dim rData As Variant ' Replace Data Array
Dim ColOffset As Long ' Search and Replace Column Offset
Set ws = wb.Worksheets(srcName)
Set rng = getColumnRange(getCellRange(ws, sFirst))
ColOffset = getCellRange(ws, rFirst).Column - rng.Column
sData = getColumn(rng)
rData = getColumn(rng.Offset(, ColOffset))
' Destination
Dim dData As Variant ' Destination Array
Set ws = wb.Worksheets(dstName)
Set rng = getColumnRange(getCellRange(ws, dFirst))
dData = getColumn(rng)
' Search and replace (in arrays).
Dim mData As Variant ' Match Data Array
mData = Application.Match(dData, sData, 0)
Dim cMatch As Variant
Dim i As Long
For i = 1 To UBound(dData, 1) ' or 'UBound(mData, 1)'
cMatch = mData(i, 1)
If IsNumeric(cMatch) Then
dData(i, 1) = rData(cMatch, 1)
End If
Next i
' Write from Destination Array to Destination Range.
rng.Value = dData
End Sub
Function getCellRange( _
ws As Worksheet, _
ByVal CellAddress As String) _
As Range
On Error Resume Next
Set getCellRange = ws.Range(CellAddress)
On Error GoTo 0
End Function
Function getColumnRange( _
FirstCell As Range) _
As Range
If Not FirstCell Is Nothing Then
With FirstCell
Dim rng As Range
Set rng = .Resize(.Worksheet.Rows.Count - .Row + 1)
Set rng = rng.Find("*", , xlFormulas, , , xlPrevious)
If Not rng Is Nothing Then
Set getColumnRange = .Resize(rng.Row - .Row + 1)
End If
End With
End If
End Function
Function getColumn( _
rng As Range) _
As Variant
If Not rng Is Nothing Then
If InStr(rng.Address, ":") > 0 Then
getColumn = rng.Value
Else
Dim Data As Variant
ReDim Data(1 To 1, 1 To 1)
Data(1, 1) = rng.Value
getColumn = Data
End If
End If
End Function
The code below reads the data from A2:A6 into an array SrcArr and the data from B2:B6 into another array I called ModArr. Then it creates a third array (OutArr) of the same size as the source and writes data from SrcArr into it modified according to the data in ModArr. Finally, the OutArr is written to column D. This is the setup and the result.
And here is the code that did it.
Sub ReplaceArray()
' 138
Dim SrcArr As Variant ' Source
Dim ModArr As Variant ' Modifier
Dim OutArr As Variant ' Output
Dim R As Long ' loop counter: rows
With ActiveSheet
SrcArr = .Range("A2:A6").Value
ModArr = .Range("B2:B6").Value
ReDim OutArr(1 To UBound(SrcArr), 1 To UBound(SrcArr, 2))
For R = 1 To UBound(SrcArr)
If ModArr(R, 1) = True Then
OutArr(R, 1) = SrcArr(R, 1) * 12
Else
If IsEmpty(ModArr(R, 1)) Then
OutArr(R, 1) = "No data"
Else
OutArr(R, 1) = 0
End If
End If
Next R
.Cells(2, "D").Resize(UBound(SrcArr), UBound(SrcArr, 2)).Value = OutArr
End With
End Sub

Resources