I have been trying to make a function where it matches 2 separate strings with two column then copy corresponding columns data and paste into separate sheet.
I am stuck on that thing how to make 2 matches like For Each cell In myDataRng & myDataRng2.
your help will be appreciated
Sub Tester()
Dim myDataRng, myDataRng2 As Range
Dim cell As Range, wsSrc As Worksheet, wsDest As Worksheet
Dim destRow As Range
Dim FindValue As String
Dim FindValue2 As String
Set wsSrc = Worksheets("Sheet1") 'source sheet
Set wsDest = Worksheets("Sheet2") 'destination sheet
FindValue = wsDest.Range("A2").Value
FindValue2 = wsDest.Range("B2").Value
Set myDataRng = wsSrc.Range("F2:F" & wsSrc.Cells(Rows.Count, "F").End(xlUp).Row)
Set myDataRng2 = wsSrc.Range("A2:A" & wsSrc.Cells(Rows.Count, "A").End(xlUp).Row)
Set destRow = wsDest.Rows(2) 'first destination row
For Each cell In myDataRng
If InStr(1, cell.Value, FindValue) > 0 Then
With cell.EntireRow 'the whole matching row
destRow.Cells(5).Value = .Cells(2).Value
destRow.Cells(6).Value = .Cells(3).Value
destRow.Cells(7).Value = .Cells(4).Value
destRow.Cells(8).Value = .Cells(5).Value
End With
Set destRow = destRow.Offset(1, 0) 'next destination row
End If
Next cell
End Sub
Other Condition
Sub find()
Dim foundRng As Range
Dim mValue As String
Set shData = Worksheets("Sheet1")
Set shSummary = Worksheets("Sheet2")
mValue = shSummary.Range("C2")
Set foundRng = shData.Range("G1:Z1").find(mValue)
'If matches then copy macthed Column and paste into Sheet2 Col"I" (as above code psting the data into Sheet2)
End Sub
Several options:
If Instr(1, cell.Offset(,-5).Value, FindValue2) > 0 Then
If InStr(1, wsSrc.Range("A" & cell.Row), FindValue2) > 0 Then
and others.
I like using rows for loops like this because it makes it very easy to read the code and understand what is happening. By breaking the search range into a series of rows, everything becomes simple to write and read.
Sub Tester()
Dim myDataRng, myDataRng2 As Range
Dim rRow As Range, wsSrc As Worksheet, wsDest As Worksheet
Dim destRow As Range
Dim FindValue As String
Dim FindValue2 As String
Set wsSrc = Worksheets("Sheet1") 'source sheet
Set wsDest = Worksheets("Sheet2") 'destination sheet
FindValue = wsDest.Range("A2").Value
FindValue2 = wsDest.Range("B2").Value
Set myDataRng = wsSrc.Range("F2:F" & wsSrc.Cells(Rows.Count, "F").End(xlUp).Row)
'Set myDataRng2 = wsSrc.Range("A2:A" & wsSrc.Cells(Rows.Count, "A").End(xlUp).Row)
Set destRow = wsDest.Rows(2) 'first destination row
For Each rRow In myDataRng.Rows.EntireRow
If InStr(1, rRow.Columns("F").Value, FindValue) > 0 _
And InStr(1, rRow.Columns("A").Value, FindValue2) > 0 Then
With rRow.EntireRow 'the whole matching row
destRow.Cells(5).Value = .Cells(2).Value
destRow.Cells(6).Value = .Cells(3).Value
destRow.Cells(7).Value = .Cells(4).Value
destRow.Cells(8).Value = .Cells(5).Value
End With
Set destRow = destRow.Offset(1, 0) 'next destination row
End If
Next rRow
End Sub
Set wsSrc = Worksheets("Sheet1") 'source sheet
Set wsDest = Worksheets("Sheet2") 'destination sheet
FindValue = wsDest.Range("A2").Value
FindValue2 = wsDest.Range("B2").Value
Set myDataRng = wsSrc.Range("F2:F" & wsSrc.Cells(Rows.Count, "F").End(xlUp).Row)
'Set myDataRng2 = wsSrc.Range("A2:A" & wsSrc.Cells(Rows.Count, "A").End(xlUp).Row)
Set destRow = wsDest.Rows(2) 'first destination row
For Each rRow In myDataRng.Rows
If InStr(1, rRow.Columns("F").Value, FindValue) > 0 _
And InStr(1, rRow.Columns("A").Value, FindValue2) > 0 Then
With rRow.EntireRow 'the whole matching row
destRow.Cells(5).Value = .Cells(2).Value
destRow.Cells(6).Value = .Cells(3).Value
destRow.Cells(7).Value = .Cells(4).Value
destRow.Cells(8).Value = .Cells(5).Value
End With
Set destRow = destRow.Offset(1, 0) 'next destination row
End If
Next rRow
End Sub
Related
How to autosum column using column header in vba code? I am trying to autosum few columns in excel sheet but column position is changing every time.
Dim Rng As Range
Dim c As Range
Set Rng = Range("F1:F" & Range("F1").End(xlDown).Row)
Set c = Range("F1").End(xlDown).Offset(1, 0)
c.Formula = "=SUM(" & Rng.Address(False, False) & ")"
Set Rng = Range("G1:G" & Range("G1").End(xlDown).Row)
Set c = Range("G1").End(xlDown).Offset(1, 0)
c.Formula = "=SUM(" & Rng.Address(False, False) & ")"
Set Rng = Range("H1:H" & Range("H1").End(xlDown).Row)
Set c = Range("H1").End(xlDown).Offset(1, 0)
c.Formula = "=SUM(" & Rng.Address(False, False) & ")"
Find Headers to Insert Autosum (Application.Match)
It is assumed that the headers are in the first row of the worksheet's used range.
Sub InsertAutosum()
Dim Headers(): Headers = Array("Sales 2020", "Sales 2021", "Sales 2022")
Dim ws As Worksheet: Set ws = ActiveSheet
Dim trg As Range ' Table Range
With ws.UsedRange
Dim lCell As Range
Set lCell = .Find("*", , xlFormulas, , xlByRows, xlPrevious)
If lCell Is Nothing Then Exit Sub
Set trg = .Resize(lCell.Row - .Row + 1)
End With
Dim hrg As Range: Set hrg = trg.Rows(1) ' Header Range
Dim trCount As Long: trCount = trg.Rows.Count
Dim srg As Range: Set srg = trg.Resize(trCount - 1).Offset(1) ' Sum Range
Dim Header, cIndex, sFormula As String
For Each Header In Headers
cIndex = Application.Match(Header, hrg, 0)
If IsNumeric(cIndex) Then
sFormula = "=SUM(" & srg.Columns(cIndex).Address(, 0) & ")"
hrg.Offset(trCount).Cells(cIndex).Formula = sFormula
End If
Next Header
End Sub
how to autosum column using column header in vba code
If you know the column header, then it becomes very easy. Here is an example. Let's say the header of the column is SOME-HEADER and we are not sure which column it is in but the headers are in row 1. If they are not in row 1 then you will have to tweak the code accordingly.
I have commented the code but if you still have a question then simply ask.
Option Explicit
Sub Sample()
Dim Ws As Worksheet
Dim HeaderText As String
Dim HeaderRow As Long
Dim HeaderColumn As Long
Dim LastRow As Long
Dim LastColumn As Long
Dim rng As Range
Dim i As Long
'~~> Change this to the relevant worksheet
Set Ws = Sheet1
'~~> Column Header text. Change as applicable
HeaderText = "SOME-HEADER"
'~~> Headers are in row 1. Change as applicable
HeaderRow = 1
With Ws
'~~> Check if there is data in the worksheet
If Application.WorksheetFunction.CountA(.Cells) = 0 Then
MsgBox "There is no data in thw worksheet"
Exit Sub
End If
'~~> Find last column
LastColumn = .Cells(HeaderRow, .Columns.Count).End(xlToLeft).Column
'~~> We can use .Find to find the header row but it may be an overkill
'~~> So we use a simple loop
For i = 1 To LastColumn
'~~> Checking for an exact match.
If UCase(Trim(.Cells(HeaderRow, i).Value)) = UCase(Trim(HeaderText)) Then
HeaderColumn = i
Exit For
End If
Next i
'~~> Check if we found the column
If HeaderColumn = 0 Then
MsgBox "Unable to find the column"
Exit Sub
End If
'~~> Find the last row in the column
LastRow = .Cells(.Rows.Count, HeaderColumn).End(xlUp).Row
'~~> This is the range
Set rng = .Range(.Cells(2, HeaderColumn), .Cells(LastRow, HeaderColumn))
'~~> Insert Sum Formula
.Cells(LastRow + 1, HeaderColumn).Formula = "=Sum(" & _
rng.Address(False, False) & _
")"
End With
End Sub
SCREENSHOT
I am trying to copy data from multiple sheets and paste it into Sheet1. The result paste it into Sheet1 but the same row each time and not the next row of previous copied data. Here is my code. Any help is really appreciate. Thank you!
Sub LoopCopySheetsData()
Dim i As Integer
Dim wb As Workbook
Dim totalWS As Long
Set wb = ActiveWorkbook
'totalWS = wb.Sheets.Count
totalWS = 4
For i = 2 To totalWS 'Start of the VBA loop
If i < totalWS + 1 Then
Sheets(i).Select
With wb.Sheets(i)
Set findHeadRow = .Range("A:A").Find(What:="Data", LookIn:=xlValues)
End With
headRow = findHeadRow.Row
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
Sheets(i).Range("A" & headRow + 1 & ":A" & lastRow).Copy
Range("A1").Activate
With wb.Sheets("Sheet1")
lastRowMaster = Cells(Rows.Count, "D").End(xlUp).Row
Sheets("Sheet1").Range("D" & lastRowMaster + 1).PasteSpecial xlPasteValues
End With
End If
Next i
End Sub
Copy Columns From Multiple Worksheets
If the header cell (Data) contains a formula, you will have to use xlValues instead of xlFormulas (first occurrence).
Adjust the values in the constants section.
Option Explicit
Sub LoopCopySheetsData()
' Source
Const sCol As String = "A"
Const sHeader As String = "Data"
' Destination
Const dName As String = "Sheet1"
Const dCol As String = "D"
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dfCell As Range
Set dfCell = dws.Cells(dws.Rows.Count, dCol).End(xlUp).Offset(1)
Dim sws As Worksheet
Dim srg As Range ' Range
Dim shCell As Range ' Header Cell
Dim slCell As Range ' Last Cell
Dim rCount As Long ' Source/Destination Rows Count
For Each sws In wb.Worksheets
If StrComp(sws.Name, dName, vbTextCompare) <> 0 Then ' exclude 'dws'
' Find header cell and last cell.
With sws.Columns(sCol)
Set shCell = _
.Find(sHeader, .Cells(.Cells.Count), xlFormulas, xlWhole)
Set slCell = .Find("*", , xlFormulas, , , xlPrevious)
End With
If Not shCell Is Nothing Then
If Not slCell Is Nothing Then
rCount = slCell.Row - shCell.Row ' without header
If rCount > 0 Then
Set srg = shCell.Offset(1).Resize(rCount)
dfCell.Resize(rCount).Value = srg.Value ' copy
Set dfCell = dfCell.Offset(rCount) ' next
End If
End If
End If
End If
Next sws
MsgBox "Done.", vbInformation
End Sub
Please heed this post: How to avoid using Select in Excel VBA. As second answer mentions, avoid any use of ActiveWorkbook, Activate, and Select for efficiency, maintenance, and readability.
Instead, explicitly qualify all Workbook, Worksheet, Cells, Range, and other objects. In fact, consider range assignment and avoid the need of copy and paste:
Sub LoopCopySheetsData()
Dim i As Integer, totalWS As Integer
Dim headRow As Long, lastRow As Long, headRowMaster As Long, lastRowMaster As Long
'totalWS = ThisWorkbook.Sheets.Count
totalWS = 4
For i = 2 To totalWS
If i < (totalWS + 1) Then
With ThisWorkbook.Sheets(i)
headRow = .Range("A:A").Find(What:="Data", LookIn:=xlValues).Row
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
With ThisWorkbook.Sheets("Sheet1")
headRowMaster = .Cells(.Rows.Count, "D").End(xlUp).Row
lastRowMaster = headRowMaster + (lastRow - headRow)
' ASSIGN VALUES BY RANGE
.Range("D" & headRowMaster + 1 & ":D" & lastRowMaster).Value = _
ThisWorkbook.Sheets(i).Range("A" & headRow + 1 & ":A" & lastRow).Value
End With
End If
Next i
End Sub
For each value in Sheet("Scrap2") Column A.
find all matching instances of this value in column A of Sheet("VA_Data"). copy entire row and paste to first empty Row on sheet("List")
My code right now basically only copys the first instance it comes to of the match and then moves to the next value in Sheet("Scrap2").
If there are 10 cells in col A of sheet "VA_Data" that match the first value of Scrap2, then those 10 rows need to copy entire row and paste to first empty rows on sheet "List".
any help is appreciated.
Option Explicit
Public Sub Loop_VA_Data()
Dim wsa As Worksheet
Dim wsb As Worksheet
Dim wsc As Worksheet
Dim a As Integer
Dim b As Integer
Dim c As Integer
Dim stra As String
Dim rng As Range
On Error GoTo errLoop_VA_Data
Application.ScreenUpdating = False
Set wsa = ThisWorkbook.Worksheets("Scrap2")
Set wsb = ThisWorkbook.Worksheets("VA_Data")
Set wsc = ThisWorkbook.Worksheets("List")
wsa.Range("B:B").Clear
wsc.Rows("2:" & wsc.Range("A1").CurrentRegion.Rows.Count + 1).Clear
a = 2
Do
If Trim(wsa.Cells(a, 1).Value) = "" Then
Exit Do
End If
stra = Trim(wsa.Cells(a, 1).Value)
Set rng = wsb.Range("A:A").Find(What:=stra, LookIn:=xlValues, LookAt:=xlWhole)
If Not (rng Is Nothing) Then
b = rng.Row
c = wsc.Range("A1").CurrentRegion.Rows.Count + 1
wsb.Rows(b).Copy wsc.Rows(c)
wsa.Cells(a, 2).Value = "Found on row " & b
Else
wsa.Cells(a, 2).Value = "Not Found"
End If
If Not (rng Is Nothing) Then
Set rng = Nothing
End If
a = a + 1
Loop
MsgBox "Complete!", vbInformation
GoTo closeout
Exit Sub
errLoop_VA_Data:
MsgBox "Err Number is: " & Err.Number & " / Err Desc is: " & Err.Description & " in sub Loop_VA_Data!", vbCritical
closeout:
If Not (wsa Is Nothing) Then
Set wsa = Nothing
End If
If Not (wsb Is Nothing) Then
Set wsb = Nothing
End If
If Not (wsc Is Nothing) Then
Set wsc = Nothing
End If
If Not (rng Is Nothing) Then
Set rng = Nothing
End If
Exit Sub
End Sub
I think #urdearboy has the right idea - using a filter & copying en masse. The following code assumes the data on your VA_Data sheet is contiguous. Let me know how you go with it.
Option Explicit
Sub Filter_Copy()
Application.ScreenUpdating = False
Dim c As Range
Dim LastRow As Long, PasteRow As Long
Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As Worksheet
Set ws1 = Sheets("Scrap2")
Set ws2 = Sheets("VA_Data")
Set ws3 = Sheets("List")
LastRow = ws1.Cells(Rows.Count, 1).End(xlUp).Row
For Each c In ws1.Range(ws1.Cells(1, 1), ws1.Cells(LastRow, 1))
With ws2.Cells(1, 1).CurrentRegion
.AutoFilter 1, c.Value
PasteRow = ws3.Cells(Rows.Count, 1).End(xlUp).Row + 1
.Offset(1).Resize(.Rows.Count - 1).Copy ws3.Range("A" & PasteRow)
.AutoFilter
End With
Next c
End Sub
I have two worksheets
Source(ThisWorkbook) - contains multiple worksheets
Destination(WBD) - contains 1 worksheet
This is the process:
Compare each cell from a range in WBD (B2:B6) to all worksheet names in ThisWorkbook
If a match is found, from a range in WBD (C2:C7) and look for it in the matched worksheet
(this is where I'm having troubles)How do I get the value of the avg price cell? Do I need another loop?
*the distance between type and price is consistent.
Here's what I got so far:
For Each cel In WBD.Worksheets(1).Range("B2:B6")
For Each ws In ThisWorkbook.Worksheets
ws.Activate
If ws.Name = cel.Value Then
'find C2:C7 , offset, copy avg price, paste
Next ws
Next cel
Source - ThisWorkbook
Destination - WBD
A Lookup by Worksheets
An Application.Match Approach
Option Explicit
Sub lookupValues()
Const dFirst As Long = 2
Const sFirst As Long = 2
Dim swb As Workbook: Set swb = ThisWorkbook
'Dim WBD As Workbook: Set WBD = ThisWorkbook
Dim drg As Range
Dim dLast As Long
With WBD.Worksheets(1)
dLast = .Cells(.Rows.Count, "C").End(xlUp).Row ' because merged in 'B'
Set drg = .Cells(dFirst, "B").Resize(dLast - dFirst + 1)
End With
Dim src As Worksheet
Dim srg As Range
Dim cel As Range
Dim dMatch As Variant
Dim sMatch As Variant
Dim sLast As Long
For Each src In swb.Worksheets
sLast = src.Cells(src.Rows.Count, "C").End(xlUp).Row
Set srg = Nothing
On Error Resume Next
Set srg = src.Cells(sFirst, "B").Resize(sLast - sFirst + 1)
On Error GoTo 0
If Not srg Is Nothing Then
dMatch = Application.Match(src.Name, drg, 0)
If IsNumeric(dMatch) Then
Set cel = drg.Cells(dMatch)
Do
sMatch = Application.Match(cel.Offset(, 1).Value, srg, 0)
If IsNumeric(sMatch) Then
cel.Offset(, 2).Value _
= srg.Cells(sMatch).Offset(3, 2).Value
End If
Set cel = cel.Offset(, 1).Offset(1, -1) ' because merged
Loop Until Len(cel.Value) > 0 Or cel.Row > dLast
End If
End If
Next src
'WBD.Save
'swb.Close SaveChanges:=False
End Sub
Sub m1()
For Each cel In ThisWorkbook.Worksheets(1).Range("B2:B6")
If cel.MergeCells Then
shname = cel.MergeArea.Cells(1, 1).Value ' if cells merged, only first cell contains value
Else
shname = cel.Value
End If
For Each ws In ThisWorkbook.Worksheets
If ws.Name = shname Then
Set f = ws.Columns("B").Find(cel.Offset(0, 1).Value, lookat:=xlWhole)
If Not f Is Nothing Then ' its found
Set f = ws.Cells.Find("avg price", after:=f.Offset(0, 1))
If Not f Is Nothing Then ' its found
cel.Offset(0, 2).Value = f.Offset(0, 1).Value
End If
End If
End If
Next ws
Next cel
End Sub
The workbook contains three sheets:
Item-style (contains in colA the item no., colB the style of the item)
Style (List of styles we want)
Style template (List of items within the styles specified in the cols)
I need a macro that does three things:
Copy the list of styles from the Style sheet and paste & transpose in Style template starting from row 2. Row 1 of all columns needs to be left blank.
The macro needs to select each style in style template one by one, which is now in different columns. These will be the search criteria.
On the basis of style selected in step 2, the macro needs to do a search in item-style sheet and select all the items that have the selected style and paste all these items beneath the corresponding style in style-template sheet. If there are no items corresponding to the selected style, then it should mention "No items" beneath the corresponding style.
Here's a link to the workbook for easy understanding
StyleProject
Though the workbook mentions only three styles the macro should have the capability of working with more than 50 styles.
Here's the code I have:
Sub StyleProject()
Application.ScreenUpdating = False
Dim ws As Worksheet
Dim ws2 As Worksheet
Dim ws3 As Worksheet
Set ws = Sheets("Item-Style")
Set ws2 = Sheets("Style")
Set ws3 = Sheets("Style Template")
Dim rng As Range, secRng As Range
Dim i, j, k
Sheets("Style Template").Activate
finalcol = Cells(2, 50).End(x1toleft).Column
For i = 2 To finalcol
j = Cells(2, i).Value
lr = ws.Range("A" & Rows.Count).End(xlUp).Row
For k = 2 To lr
Set rng = ws.Range("B" & i)
If StrComp(CStr(rng.Text), j, 1) = 0 Then
ws.Rows(k & ":" & k).Copy
nxtRow = ws3.Range(i & Rows.Count).End(xlUp).Row + 1
ws2.Rows(nxtRow & ":" & nxtRow).PasteSpecial Paste:=xlPasteValues, _
Operation:=xlNone, SkipBlanks:=False, Transpose:=False
Set rng = Nothing
End If
Next k
Next i
Application.ScreenUpdating = True
End Sub
It ends up in error trying to figure out nextrng I believe.
Sub StyleProject()
Dim wsStyle As Worksheet
Dim wsData As Worksheet
Dim wsTemplate As Worksheet
Dim StyleCell As Range
Dim rngFound As Range
Dim arrResults() As Variant
Dim strFirst As String
Dim ResultIndex As Long
Dim StyleIndex As Long
Set wsStyle = Sheets("Style")
Set wsData = Sheets("Item Data")
Set wsTemplate = Sheets("Style Template")
With wsStyle.Range("A2", wsStyle.Cells(Rows.Count, "A").End(xlUp))
If .Row < 2 Then Exit Sub 'No data
ReDim arrResults(1 To 1 + Evaluate("MAX(COUNTIF(" & wsData.Columns("B").Address(External:=True) & "," & .Address(External:=True) & "))"), 1 To .Cells.Count)
For Each StyleCell In .Cells
StyleIndex = StyleIndex + 1
ResultIndex = 1
arrResults(ResultIndex, StyleIndex) = StyleCell.Text
Set rngFound = wsData.Columns("B").Find(StyleCell.Text, wsData.Cells(Rows.Count, "B"), xlValues, xlWhole)
If Not rngFound Is Nothing Then
strFirst = rngFound.Address
Do
ResultIndex = ResultIndex + 1
arrResults(ResultIndex, StyleIndex) = wsData.Cells(rngFound.Row, "A").Text
Set rngFound = wsData.Columns("B").Find(StyleCell.Text, rngFound, xlValues, xlWhole)
Loop While rngFound.Address <> strFirst
End If
Next StyleCell
End With
If UBound(arrResults, 1) > 1 Then
wsTemplate.Range("B2", wsTemplate.Cells(Rows.Count, Columns.Count)).Clear
wsTemplate.Range("B2").Resize(UBound(arrResults, 1), UBound(arrResults, 2)).Value = arrResults
With wsTemplate.Range("B2").Resize(, UBound(arrResults, 2))
.Font.Bold = True
.Borders(xlEdgeBottom).LineStyle = xlContinuous
.EntireColumn.AutoFit
End With
End If
Set wsStyle = Nothing
Set wsData = Nothing
Set wsTemplate = Nothing
Set StyleCell = Nothing
Set rngFound = Nothing
Erase arrResults
End Sub