I have a file with a few sheets, I need to extract values from each not empty cell into a column on another sheet.
Would be awesome if while doing that duplicates can be removed as well.
The following code infinitely loops. I don't see how to break the loop since all the events are being used in the body of the code.
Range where the cells are being looked for on both sheets are different, that is why I used .End(xlUp) to define the last row with values in cells.
I cannot use empty cells as a trigger for stopping the loop because there are empty cells between cells with values.
Sub updt()
Dim ws As Worksheet, currWs As Worksheet, Lng As Integer, rng As Range
Set ws = wb.Worksheets("Sheet1") 'the source sheet
Set currWs = Sheets("Sheet2") 'the destination sheet
Lng = ws.Cells(Rows.Count, 1).End(xlUp).Row
Set rng = ws.Range("A2:A" & Lng)
For Each c Lng rng
If WorksheetFunction.CountIf(currWs.Range("A:A"), c.Value) = 0 Then
currWs.Range("A" & currWs.Cells(Rows.Count, 1).End(xlUp).Row)(2) = c.Value
End If
Next
End Sub
Update Column With Unique Non-Existing Values From a Column of Another Worksheet Using a Dictionary
To avoid further complications, no arrays are used.
Option Explicit
Sub UpdateWorksheet()
' Reference the workbook.
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Reference the source worksheet, calculate the last row
' and reference the source column range.
Dim sws As Worksheet: Set sws = wb.Worksheets("Sheet1")
Dim slRow As Long: slRow = sws.Cells(sws.Rows.Count, "A").End(xlUp).Row
Dim srg As Range: Set srg = sws.Range("A2:A" & slRow)
' Reference the destination worksheet and calculate the last row.
Dim dws As Worksheet: Set dws = wb.Worksheets("Sheet2")
Dim dlRow As Long: dlRow = dws.Cells(dws.Rows.Count, "A").End(xlUp).Row
' Define a dictionary (object).
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare ' case-insensitive
' Declare variables.
Dim cCell As Range
Dim cKey As Variant
' Write the unique values from the destination column range
' to the dictionary.
If dlRow > 1 Then ' 1 means 'first row - 1' i.e. '2 - 1'
Dim drg As Range: Set drg = dws.Range("A2:A" & dlRow)
For Each cCell In drg.Cells
cKey = cCell.Value
If Not IsError(cKey) Then ' exclude error values
If Len(cKey) > 0 Then ' exclude blanks
dict(cKey) = Empty
End If
End If
Next cCell
End If
' Add the unique values from the source column range
' to the dictionary.
For Each cCell In srg.Cells
cKey = cCell.Value
If Not IsError(cKey) Then ' exclude error values
If Len(cKey) > 0 Then ' exclude blanks
dict(cKey) = Empty
End If
End If
Next cCell
' Check if the dictionary is empty.
If dict.Count = 0 Then
MsgBox "No valid values found.", vbCritical
Exit Sub
End If
' Clear the previous values from the destination first cell to the bottom
' of the worksheet.
Dim dCell As Range: Set dCell = dws.Range("A2")
With dCell
.Resize(dws.Rows.Count - .Row + 1).ClearContents
End With
' Write the unique values from the dictionary to the destination worksheet.
For Each cKey In dict.Keys
dCell.Value = cKey ' write
Set dCell = dCell.Offset(1) ' reference the cell below
Next cKey
' Inform.
MsgBox "Worksheet updated.", vbInformation
End Sub
You might want to use AdvancedFilter:
Option Explicit
Sub Copy_Advanced()
Dim ws As Worksheet, currWs As Worksheet, Lng As Integer, rng As Range
Set ws = Worksheets("Sheet1") 'the source sheet
Set currWs = Sheets("Sheet2") 'the destination sheet
Lng = ws.Cells(Rows.Count, 1).End(xlUp).Row
Set rng = ws.Range("A1:A" & Lng)
ws.Range("D1").Value = ws.Range("A1").Value
ws.Range("D2") = ">0"
ws.Range(rng.Address).AdvancedFilter Action:=xlFilterCopy, _
CriteriaRange:=ws.Range("D1:D2"), _
CopyToRange:=currWs.Range("A1"), _
Unique:=True
End Sub
Related
I want to display the values found in Column 1 of workbook1 that are not in column 1 of workbook2. I am treating the contents as strings for exact match. Some of the account numbers look alike, but are not because they have leading 0's that make it different.
Workbook1: Column I am referencing is "AccountID"
AccountID
001
002
003
4
5
6
7
8
9
10
Workbook 2 column I am referencing is "AccountID"
AccountID
1
2
3
5
6
7
8
Desired Result Unique values from workbook1 that are not in workbook2 put into a new sheet
AccountID
001
002
003
4
9
10
here is my code, but some of the returned values ARE in both workbooks. I want the values from Workbook1, column1 that are unique from values in Workbook2, columnn1.
'Method to show what AccountID is in Client Bill Info but not FDG Accounts.
Sub CompareCols()
'Disabling the screen updating.
Application.ScreenUpdating = False
'Declaring variables
Dim Rng As Range
Dim RngList As Object
Dim WB1 As Worksheet
Dim WB2 As Worksheet
Dim NWS As Worksheet
'Setting values to variables declared
Set WB1 = ThisWorkbook.Sheets("Detailed Bill Info")
Set WB2 = Workbooks("FDG Accounts.xlsx").Sheets("FDG Accounts")
Set RngList = CreateObject("Scripting.Dictionary")
Set NWS = Sheets.Add
'Loop to collect values that are in column A of this workbook
'that are not in column A of WB2
For Each Rng In WB2.Range("A2", WB2.Range("A" & Rows.Count).End(xlUp))
If Not RngList.Exists(CStr(Rng.Value)) Then
RngList.Add CStr(Rng.Value), Nothing
End If
Next
For Each Rng In WB1.Range("A2", WB1.Range("A" & Rows.Count).End(xlUp))
If Not RngList.Exists(CStr(Rng.Value)) Then
WB1.Cells(Rng.Row, 1).Interior.ColorIndex = 6
tmpstr = Rng.Value
NWS.Cells(Rng.Row, 1).Value = tmpstr
End If
Next
Application.ScreenUpdating = True
End Sub
Unique Strings in Dictionaries
Option Explicit
Sub IdentifyNewAccounts()
Const NewName As String = "New Accounts"
' Reference the source column range ('srg').
Dim swb As Workbook: Set swb = Workbooks("FDG Accounts.xlsx")
Dim sws As Worksheet: Set sws = swb.Worksheets("FDG Accounts")
Dim srg As Range
Set srg = sws.Range("A2", sws.Cells(sws.Rows.Count, "A").End(xlUp))
' Write the unique values from the source column range
' to the keys of the source dictionary ('sDict').
Dim sDict As Object: Set sDict = CreateObject("Scripting.Dictionary")
sDict.CompareMode = vbTextCompare ' case-insensitive
Dim sCell As Range
Dim sString As String
For Each sCell In srg.Cells
sString = CStr(sCell.Value)
If Len(sString) > 0 Then ' exclude blanks
sDict(sString) = Empty
End If
Next sCell
' Reference the destination column range ('drg').
Dim dwb As Workbook: Set dwb = ThisWorkbook ' workbook containing this code
Dim dws As Worksheet: Set dws = dwb.Worksheets("Detailed Bill Info")
Dim drg As Range
Set drg = dws.Range("A2", dws.Cells(dws.Rows.Count, "A").End(xlUp))
' Write the unique values from the destination column range,
' that are not in the 'keys' of the source dictionary, to the 'keys'
' of the destination dictionary ('dDict').
Dim dDict As Object: Set dDict = CreateObject("Scripting.Dictionary")
dDict.CompareMode = vbTextCompare ' case-insensitive
Dim durg As Range
Dim dCell As Range
Dim dString As String
For Each dCell In drg.Cells
dString = CStr(dCell.Value)
If Len(dString) > 0 Then ' the cell is not blank
If Not sDict.Exists(dString) Then ' not found in source dictionary
' Write the unique string to the destination dictionary.
dDict(dString) = Empty
' Combine the cells to be highlighted into a range union.
If durg Is Nothing Then ' first cell
Set durg = dCell
Else ' all but the first cell
Set durg = Union(durg, dCell)
End If
'Else ' the string was found in the source dictionary; do nothing
End If
'Else ' the cell is blank; do nothing
End If
Next dCell
Set sDict = Nothing ' the relevant data is in the destination dictionary
' Validate.
If durg Is Nothing Then ' or 'If dDict.Count = 0 Then'
MsgBox "No unique accounts found.", vbExclamation
Exit Sub
End If
' Write the unique strings from the destination dictionary to the
' destination array ('dData'), a 2D one-based one-column string array.
Dim drCount As Long: drCount = dDict.Count
Dim dData() As String: ReDim dData(1 To drCount, 1 To 1)
Dim dKey As Variant
Dim r As Long
For Each dKey In dDict.Keys
r = r + 1
dData(r, 1) = dKey
Next dKey
Set dDict = Nothing ' the relevant data is in the destination array
' Turn off screen updating (so far the worksheets were only read from).
Application.ScreenUpdating = False
' Highlight the cells meeting the criteria in one go.
durg.Interior.Color = vbYellow
' Add a new worksheet, the new destination worksheet ('ndws').
Dim ndws As Worksheet
' Attempt to reference it.
On Error Resume Next
Set ndws = dwb.Worksheets(NewName)
On Error GoTo 0
' Check if it was referenced (if it exists).
If Not ndws Is Nothing Then ' it exists
Application.DisplayAlerts = False ' to delete without confirmation
ndws.Delete
Application.DisplayAlerts = True
'Else ' it doesn't exist; do nothing
End If
' Add and reference it.
Set ndws = dwb.Worksheets.Add(After:=dwb.Sheets(dwb.Sheets.Count))
' Apply changes.
With ndws
.Name = NewName ' rename
.Range("A1").Value = "AccountID" ' write header
End With
' Reference the new destination (one-column) data range ('dnrg').
Dim ndrg As Range: Set ndrg = ndws.Range("A2").Resize(drCount)
' Write the unique strings from the destination array
' to the new destination range.
With ndrg
.NumberFormat = "#" ' format
.Value = dData ' write
.EntireColumn.AutoFit ' more format
End With
' Activate (select) the destination workbook.
If Not dwb Is ActiveWorkbook Then ' it's not active
dwb.Activate
'Else ' it's already active; do nothing
End If
' Save the destination workbook.
'dwb.Save
' Turn on screen updating (to see the changes behind the message box).
Application.ScreenUpdating = True
' Inform.
MsgBox "New accounts identified.", vbInformation
End Sub
Extra Characters
Based on the sample data you provided above it appears that your data contains extra white space on several Account Id's.
To fix this, use the Trim function to remove the white space. Make sure to do this when checking to see if the value exists in your dictionary, as well as when you are capturing the value as well.
Extra notes
tmpstr is a missing variable. It's best to use Option Explicit Statement to find this issue.
NWS.Cells(Rng.Row, 1).Value = tmpstr needs to have the cell formatted to text before applying this. This can be done with NWS.Cells(Rng.Row, 1).NumberFormat = "#"
The fix
Below is your code with the above changes:
' Method to show what AccountID is in Client Bill Info but not FDG Accounts.
Sub CompareCols()
'Disabling the screen updating.
Application.ScreenUpdating = False
'Declaring variables
Dim Rng As Range
Dim RngList As Object
Dim WB1 As Worksheet
Dim WB2 As Worksheet
Dim NWS As Worksheet
'Setting values to variables declared
Set WB1 = ThisWorkbook.Sheets("Detailed Bill Info")
Set WB2 = Workbooks("FDG Accounts.xlsx").Sheets("FDG Accounts")
Set RngList = CreateObject("Scripting.Dictionary")
Set NWS = Sheets.Add
'Loop to collect values that are in column A of this workbook
'that are not in column A of WB2
For Each Rng In WB2.Range("A2", WB2.Range("A" & Rows.Count).End(xlUp))
If Not RngList.Exists(Trim(CStr(Rng.Value))) Then
RngList.Add Trim(CStr(Rng.Value)), Nothing
End If
Next
For Each Rng In WB1.Range("A2", WB1.Range("A" & Rows.Count).End(xlUp))
If Not RngList.Exists(Trim(CStr(Rng.Value))) Then
WB1.Cells(Rng.Row, 1).Interior.ColorIndex = 6
Dim tmpstr As String ' Added Missing variable
tmpstr = Trim(Rng.Value)
' Format will be lost on new cell
' Make sure to update it to text before
' setting the value
NWS.Cells(Rng.Row, 1).NumberFormat = "#"
NWS.Cells(Rng.Row, 1).Value = tmpstr
End If
Next
Application.ScreenUpdating = True
End Sub
I am currently using this code which goes through my worksheet and checks in the range O15:O300 to see if there are any cells that match the current date. If there is then it copies the entire row to worksheet "Today's Actions" then copies the site number (Situated in cell C3) to column AA in "Todays Actions".
I use the below code which works fine for this task for one specific sheet:
Sub rangecheck()
Application.ScreenUpdating = False
For Each cell In Range("O15:O300")
If cell.Value = Date Then
matchRow = cell.Row
Rows(matchRow & ":" & matchRow).Select
Selection.Copy
Sheets("Today's Actions").Range("A" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
ActiveSheet.Range("C3").Copy
Sheets("Today's Actions").Range("AA" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
End If
Next
Application.ScreenUpdating = True
End Sub
However, there are multiple sheets that I need to action this code for. So I use the below code to run this across all sheets:
Sub rangecheck_Set()
Dim ws As Worksheet
Dim starting_ws As Worksheet
Set starting_ws = ActiveSheet
Application.ScreenUpdating = False
For Each ws In ThisWorkbook.Worksheets
ws.Activate
Call rangecheck
Next
starting_ws.Activate 'activate the worksheet that was originally active ("Today's Actions")
Application.ScreenUpdating = True
End Sub
This issue I'm having is that it seems to work fine but randomly whenever there are a lot of dates that match todays date in range O15:O300, it duplicates some lines up to or slightly exceeding 300 rows (So as an example, if there were 15 rows that 'should' be brought back to "Today's action" tab, it would bring them back but then have a few other rows randomly duplicated down to around row 300).
I get this might be due to the range going down to 300 but I even edited the range to go to 'last row' and it still brings back the same issue. Any thoughts? I've been trying to solve this for days now. Any help appreciated
Don't use implicit references to worksheets and ranges. It is most likely that this is the reason for your problem.
Also you don't need to select and copy - another source for unforeseeable errors.
Another reason for your error could be that you don't exclude "Today's Actions"-sheet from the copying routine.
I re-wrote your sub that is copying the data:
Sub copyTodaysRows(wsSource As Worksheet, wsTarget As Worksheet)
If wsSource is wsTarget then Exit Sub 'don't run this for the target sheet
Dim c As Range, wsTargetNewRow As Long
For Each c In wsSource.Range("O15:O300")
If c.Value = Date Then
With wsTarget
wsTargetNewRow = .Range("A" & .Rows.Count).End(xlUp).Row + 1
c.EntireRow.Copy Destination:=.Range("A" & wsTargetNewRow)
.Range("AA" & wsTargetNewRow).Value = wsSource.Range("C3").Value
End With
End If
Next
End Sub
It takes the source sheet and the target sheet as input parameters.
You will call it like this within your "outer" routine:
Sub rangecheck_Set()
Application.ScreenUpdating = False
Dim wsSource as worksheet
Dim wsTarget as worksheet
Set wsTarget = Thisworkbook.Worksheets("Today's Actions")
For Each wsSource In ThisWorkbook.Worksheets
copyTodaysRows wsSource, wsTarget
Next
Application.ScreenUpdating = True
End Sub
Copy Values of Criteria (Dates) Rows From Multiple Worksheets
Option Explicit
Sub RetrieveTodaysActions()
' Calls 'RetrieveTodaysActionsCall'.
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet
For Each sws In ThisWorkbook.Worksheets
RetrieveTodaysActionsCall sws
Next sws
MsgBox "Today's actions retrieved.", vbInformation
End Sub
Sub RetrieveTodaysActionsCall(ByVal sws As Worksheet)
' Define constants.
' Source
Const sCriteriaColumnAddress As String = "O15:O300"
Const sCol1 As String = "A"
Const sCell2Address As String = "C3"
' Destination
Const dName As String = "Today's Actions"
Const dCol1 As String = "A"
Const dCol2 As String = "AA"
' Both
' Write the criteria date to a variable ('CriteriaDate').
Dim CriteriaDate As Date: CriteriaDate = Date ' today
' Exclude the destination worksheet.
If StrComp(sws.Name, dName, vbTextCompare) = 0 Then Exit Sub
' Reference the source criteria column range ('scrg').
Dim scrg As Range: Set scrg = sws.Range(sCriteriaColumnAddress)
' Check the number of matches, the number of rows to be copied
' to the destination worksheet.
If Application.CountIf(scrg, Date) = 0 Then Exit Sub
' Reference the range ('surg'), the range from the first cell
' in the source column ('sCol1') to the last cell of the used range.
Dim surg As Range
With sws.UsedRange
Set surg = sws.Range(sCol1 & 1, .Cells(.Rows.Count, .Columns.Count))
End With
' Reference the source range ('srg').
Dim srg As Range: Set srg = Intersect(scrg.EntireRow, surg)
If srg Is Nothing Then Exit Sub
' Write the number of columns of the source range to a variable (cCount).
Dim cCount As Long: cCount = srg.Columns.Count
' Write the criteria column number to a variable ('CriteriaColumn').
Dim CriteriaColumn As Long: CriteriaColumn = scrg.Column
' Write the values from the source range to an array ('Data').
Dim Data() As Variant: Data = srg.Value
Dim sValue As Variant ' Criteria Value in the Current Source Row
Dim sr As Long ' Current Source Row
Dim c As Long ' Current Source/Destination Column
Dim dr As Long ' Current Destination Row
' Loop through the rows of the array.
For sr = 1 To UBound(Data, 1)
' Write the value in the current row to a variable.
sValue = Data(sr, CriteriaColumn)
' Check if the current value is a date.
If IsDate(sValue) Then
' Check if the current value is equal to the criteria date.
If sValue = CriteriaDate Then
dr = dr + 1
' Write the values from the source row to the destination row.
For c = 1 To cCount
Data(dr, c) = Data(sr, c)
Next c
End If
End If
Next sr
' Reference the destination worksheet ('dws').
Dim dws As Worksheet: Set dws = sws.Parent.Worksheets(dName)
' Reference the destination first cell ('dfCell').
Dim dfCell As Range
Set dfCell = dws.Cells(dws.Rows.Count, dCol1).End(xlUp).Offset(1)
' Reference the destination range ('drg').
Dim drg As Range: Set drg = dfCell.Resize(dr, cCount)
' Write the values from the array to the destination range.
drg.Value = Data
' Reference the destination range 2 ('drg2').
Dim drg2 As Range: Set drg2 = drg.EntireRow.Columns(dCol2)
' Write the source cell 2 value to the destination range 2 ('drg2')
' (the same value to all cells of the range).
drg2.Value = sws.Range(sCell2Address).Value
End Sub
My process was different from the other responses, so I will still post it. I have also added a way of logging that a row has been logged because otherwise I saw that rows could be duplicated to the "Today's Actions" sheet.
Sub rangecheck(ByVal checkedSheet As Worksheet)
'#PARAM checkedSheet is the sheet to iterate through for like dates.
'Instantiate counter variables
Dim matchRow As Integer
matchRow = 0
Dim pasteRow As Integer
pasteRow = 0
Application.ScreenUpdating = False
For Each cell In checkedSheet.Range("O15:O300")
If cell.Value = Date Then
matchRow = cell.Row
'Checks if the row has been logged already (I use column "A" because I
'have no data in it, but this can be amy column in the row)
If checkedSheet.Cells(matchRow, 1) = "Logged" Then
'Do nothing
Else
'Sets value of "pasteRow" to one lower than the lowest used row in
column "AA"
pasteRow = Sheets("Today's Actions").Cells(Rows.Count,
27).End(xlUp).Row + 1
'Copies the values of the matchRow to the pasteRow
Sheets("Today's Actions").Rows(pasteRow).Value =
checkedSheet.Rows(matchRow).Value
'Copies the value of the Site Number to the paste row column "AA"
Sheets("Today's Actions").Cells(pasteRow, 27).Value =
checkedSheet.Cells(3, 3).Value
'Log that a row has been added to the "Today's Actions" sheet
checkedSheet.Cells(matchRow, 1) = "Logged"
End If
End If
Next
Application.ScreenUpdating = True
End Sub
I have also modifed your sub which calls the copying sub to check if it is trying to copy the "Today's Actions" sheet.
Sub rangecheck_Set()
Dim ws As Worksheet
Dim starting_ws As Worksheet
Set starting_ws = Worksheets("Today's Actions")
Application.ScreenUpdating = False
For Each ws In ThisWorkbook.Worksheets
'Check if the ws to check is "Today's Actions"
If ws.Name = "Today's Actions" Then
'Do Nothing
Else
Call rangecheck(ws)
End If
Next
starting_ws.Activate 'activate the worksheet that was originally active
Application.ScreenUpdating = True
End Sub
I am trying to extract data from different sheets in a summary sheet.
The referencing does not work.
Sub Summary_LPI()
Dim wkSht As Worksheet, wsC As Worksheet, rngSearch As Range
Dim shNCell As Range
Set wsC = Sheets("Summary")
Set rngSearch = wsC.Range("A2:A60")
For Each wkSht In ThisWorkbook.Worksheets
'find the sheet name cell in rngSearch:
Set shNCell = rngSearch.Find(what:=wkSht.Name, LookIn:=xlValues, Lookat:=xlWhole,
MatchCase:=False)
'if found:
If Not shNCell Is Nothing Then
'copy the below built array in the necessary place
wkSht.Range("AZ56").Value = wsC.Range(shNCell.Offset(0, 6), shNCell.Offset(1, 6)).Value
End If
Next wkSht
End Sub
Copy Data Into a Summary Worksheet
Adjust the values in the constants section.
The order of the columns in the Summary worksheet needs to be the same as in each individual worksheet.
The number of columns to be pulled is defined by the last non-empty column in the first (header) row of the Summary worksheet.
Option Explicit
Sub Summary_LPI()
' s - Source, d - Destination
Const sfvCol As String = "AY" ' First Value Column
Const dName As String = "Summary"
Const dlCol As String = "A" ' Lookup Column
Const dfvColString As String = "F" ' First Value Column
Const dhRow As Long = 1 ' Header Row
Dim wb As Workbook: Set wb = ThisWorkbook
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dfRow As Long: dfRow = dhRow + 1 ' First Row
Dim dlrow As Long ' Last Row
dlrow = dws.Cells(dws.Rows.Count, dlCol).End(xlUp).Row
If dlrow < dfRow Then Exit Sub ' no data
Dim dlcrg As Range ' Lookup Column Range
Set dlcrg = dws.Range(dws.Cells(dfRow, dlCol), dws.Cells(dlrow, dlCol))
Dim dfvCol As Long: dfvCol = dws.Columns(dfvColString).Column
Dim dlvCol As Long ' Last Value Column
dlvCol = dws.Cells(dhRow, dws.Columns.Count).End(xlToLeft).Column
If dlvCol < dfvCol Then Exit Sub ' no data
Dim vcCount As Long: vcCount = dlvCol - dfvCol + 1 ' Value Columns Count
Application.ScreenUpdating = False
Dim sws As Worksheet
Dim svrrg As Range ' Value Row Range
Dim svRow As Long ' Value Row
Dim dvrrg As Range ' Value Row Range
Dim dlCell As Range ' Lookup Cell
For Each dlCell In dlcrg.Cells
Set dvrrg = dlCell.EntireRow.Columns(dfvCol).Resize(, vcCount)
On Error Resume Next
Set sws = wb.Worksheets(CStr(dlCell.Value))
On Error GoTo 0
If sws Is Nothing Then ' worksheet doesn't exist
dvrrg.ClearContents ' remove if you want to keep the previous
Else ' worksheet exists
svRow = sws.Cells(sws.Rows.Count, sfvCol).End(xlUp).Row
Set svrrg = sws.Cells(svRow, sfvCol).Resize(, vcCount)
dvrrg.Value = svrrg.Value
Set sws = Nothing
End If
Next dlCell
Application.ScreenUpdating = True
MsgBox "Summary updated."
End Sub
I am trying to transfer data from one master sheet to multiple template sheets based on cell name match with sheet name of the template sheets using a specific offset in the master sheet. However the referencing does not seem to work. In my case sheet named "Combine" is the master sheet. The offset value based on match cellname is 6 columns away from the matched cell. I am getting debugging error. Can anyone fix the problem?
Sub Button5_Click()
Dim wkSht As Worksheet, wsC As Worksheet, rngSearch As Range
Dim shNCell As Range
Set wsC = Sheets("Combine")
Set rngSearch = wsC.Range("A4:A800")
For Each wkSht In ThisWorkbook.Worksheets
'find the sheet name cell in rngSearch:
Set shNCell = rngSearch.Find(what:=wkSht.Name, LookIn:=xlValues, Lookat:=xlWhole,
MatchCase:=False)
'if found:
If Not shNCell Is Nothing Then
'copy the below built array in the necessary place
wkSht.Range("F12").Resize(19, 1).Value = wsC.Range(shNCell.Offset(0, 6)).Value
End If
Next wkSht
End Sub
Lookup Values (VBA)
Option Explicit
Sub Button5_Click()
Const ExceptionsList As String = "Combine" ' comma-saparated, no spaces!
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Reference the source column range ('srg').
Dim sws As Worksheet: Set sws = wb.Worksheets("Combine")
Dim slRow As Long: slRow = sws.Cells(sws.Rows.Count, "A").End(xlUp).Row
If slRow < 4 Then Exit Sub
Dim srg As Range
Set srg = sws.Range(sws.Cells(4, "A"), sws.Cells(slRow, "A"))
' Write the names from the list (string) to an array ('Exceptions').
Dim Exceptions() As String: Exceptions = Split(ExceptionsList, ",")
Dim sCell As Range
Dim dws As Worksheet
For Each dws In wb.Worksheets
' Check if not in list.
If IsError(Application.Match(dws.Name, Exceptions, 0)) Then
' '.Cells(.Cells.Count)' ensures the search starts with
' the first cell (irrelevant in this case but good to know).
' Think: After the last cell comes the first cell.
' Using 'xlFormulas' will allow you to find even if the cell
' is in a hidden row or column. The 'formula' and the 'value'
' are the same since 'xlWhole' is used.
' 'False' is the default value of the `MatchCase` argument.
With srg
Set sCell = .Find(What:=dws.Name, After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, LookAt:=xlWhole)
End With
If Not sCell Is Nothing Then
dws.Range("F12").Value = sCell.EntireRow.Columns("G").Value
'or
'dws.Range("F12").Value = sCell.Offset(, 6).Value
'Else ' no cell found; do nothing
End If
'Else ' is in the exceptions list; do nothing
End If
Next dws
End Sub
I want to copy one row of data at a time from one sheet and pasting into another sheet. I need to repeat this 100 times. I also need to modify a couple of column values after pasting them.
My data is not pasting into new sheet correctly.
'Get column numbers which need to be modified
PolicyReference = DataWS.Range("A1", DataWS.Range("IV1").End(xlToLeft)).Find("PolicyReference").Column
InsuredCode = DataWS.Range("A1", DataWS.Range("IV1").End(xlToLeft)).Find("InsuredCode").Column
InsuredDescription = DataWS.Range("A1", DataWS.Range("IV1").End(xlToLeft)).Find("InsuredDescription").Column
For j = 1 To 100
'Worksheets(DataWS).Range("A1:A100").Copy Worksheets(DestinationWS).Range("A1")
'1. Find last used row in the copy range based on data in column A
CopyLastRow = DataWS.Cells(DataWS.Rows.count, "A").End(xlUp).Row
'2. Find first blank row in the destination range based on data in column A
DestLastRow = DestinationWS.Cells(DestinationWS.Rows.count, "A").End(xlUp).Offset(1).Row
'3. Copy & Paste Data
DataWS.Rows(j).EntireRow.Copy DestinationWS.Range("A" & DestLastRow)
DataWS.Range("A1:A100").Copy
DestinationWS.Range("A" & Rows.count).End(xlUp).Offset(2).PasteSpecial Paste:=xlPasteValues
Next j
This code will copy all but the first row from DataWs to DestinationWs. If you want to be more selective in what you copy modifications must be made to the code in the loop, at the bottom.
Private Sub Study()
' 244
Dim DataWs As Worksheet
Dim DestinationWs As Worksheet
Dim PolicyReference As Long
Dim InsuredCode As Long
Dim InsuredDescription As Long
Dim Fnd As Range
Dim CopyLastRow As Long
Dim DestLastRow As Long
Dim R As Long ' loop counter: rows
Set DataWs = Worksheets("Sheet1")
Set DestinationWs = Worksheets("Sheet2")
With DestinationWs
DestLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
'Get column numbers which need to be modified
With DataWs
Set Fnd = .Rows(1).Find("PolicyReference") ' spaces between words are permissible
' make sure the column is found before using it in your further code
If Fnd Is Nothing Then Exit Sub
PolicyReference = Fnd.Column
Set Fnd = .Rows(1).Find("InsuredCode")
If Fnd Is Nothing Then Exit Sub ' perhaps give a message before exiting
InsuredCode = Fnd.Column
Set Fnd = .Rows(1).Find("InsuredDescription")
If Fnd Is Nothing Then Exit Sub ' perhaps give a message before exiting
InsuredDescription = Fnd.Column
CopyLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
Application.ScreenUpdating = False ' speeds up execution
For R = 2 To CopyLastRow ' start in row 2
DestLastRow = DestLastRow + 1
.Rows(R).Copy DestinationWs.Cells(DestLastRow, "A")
Next R
Application.ScreenUpdating = True
End With
End Sub
Columns and Ranges
I am considering these as two problems. Revealing the connection between them might lead to a more suitable solution.
The first part (including the function) illustrates how you can write the column numbers to an array which can later be used to process the data in those columns.
The second part illustrates how to copy values most efficiently. The loop is ignored.
Option Explicit
Sub ColumnsAndRanges()
Const sName As String = "Sheet1"
Const shRow As Long = 1
Const sHeadersList As String _
= "PolicyReference,InsuredCode,InsuredDescription"
Const sFirst As String = "A1"
Const dName As String = "Sheet2"
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
'Part 1: Column Numbers
Dim shrg As Range: Set shrg = sws.Rows(shRow)
' Use the function 'getColumnNumbers'.
Dim sColNums As Variant: sColNums = getColumnNumbers(shrg, sHeadersList)
If IsEmpty(sColNums) Then
MsgBox "Could not find all the headers."
Exit Sub
End If
' Column Numbers Example:
Dim n As Long
For n = 1 To UBound(sColNums)
Debug.Print n, sColNums(n)
Next n
'Part 2: Copy Range Values
' Create a reference to the Source Range.
Dim slCell As Range ' Source Last Cell
Set slCell = sws.Cells(sws.Rows.Count, "A").End(xlUp)
Dim srg As Range
' Note how a cell address (sFirst) or a cell range (slCell) can be used.
Set srg = sws.Range(sFirst, slCell).EntireRow
' Create a reference to the Destination Range.
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dfCell As Range ' Destination First Cell
' When 'EntireRow' is used, only "A" or 1 can be used.
Set dfCell = dws.Cells(dws.Rows.Count, "A").End(xlUp).Offset(1)
Dim drg As Range: Set drg = dfCell.Resize(srg.Rows.Count, srg.Columns.Count)
' Copy by assignment (most efficient when only values are to be copied).
drg.Value = srg.Value
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the column numbers in a one-based array.
' Remarks: The column numbers refer to the columns of the given range,
' not necessarily to the columns of the worksheet.
' If any of the headers cannot be found, 'Empty' is returned.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function getColumnNumbers( _
ByVal RowRange As Range, _
ByVal HeadersList As String, _
Optional ByVal Delimiter As String = ",") _
As Variant
If RowRange Is Nothing Then Exit Function
If Len(HeadersList) = 0 Then Exit Function
Dim Headers() As String: Headers = Split(HeadersList, Delimiter)
Dim ColNums As Variant
ColNums = Application.Match(Headers, RowRange.Rows(1), 0)
If Application.Count(ColNums) = UBound(Headers) + 1 Then
getColumnNumbers = ColNums
End If
End Function
The following one line of code using AdvancedFilter will paste data to the destination sheet.
Sub CopyDataToAnotherSheet()
DataWS.Range("A1").CurrentRegion.AdvancedFilter _
Action:=xlFilterCopy, _
CriteriaRange:=DataWS.Range("A1", _
DataWS.Cells(1, DataWS.Columns.Count).End(xlToLeft)), _
CopyToRange:=DestinationWS.Range("A1")
End Sub