Take non zero values, and adjacent data, from one sheet and create new table in another sheet - VBA loop - excel

I am trying to take the output from a solver model and condense it into a summary report in another sheet. The Solver screen will be lost each time I run it on new data.
My solver screen looks like this
Solver screenshot. The ideal report output will be this table. Notice that January only has two truckloads (TLs) as Solver output (IF(E4:N4=True,Include TL,n/a). So, the new report should skip TLs #3,4,5 (G4:I4) and fill in the table with next valid output (column J). I will always want to associate the unit quantity (E:N) with a product name (D) in the new report.
I am a super novice VBA user. Here is how far I have got in my VBA to accomplish this:
Sub TL_Report()
Dim c As Range
For Each c In ActiveSheet.Range("e5:e30")
If c.Value <> 0 Then
Worksheets("TL_Report").Range("C" & Rows.Count).End(xlUp).Offset(1, 0).Resize(1, 2).Value = Range(c.Offset(0, -1), c).Value
End If
Next c
End Sub
I can figure out how to loop through each column in the solver, but I cannot figure out how to have the new report get reformatted without blanks entries. Any advice on how to write this? Thank you.

According to the data avaiable, i've created this subroutine:
Sub SubReport()
'Declarations.
Dim WksSource As Worksheet
Dim WksReport As Worksheet
Dim WksWorksheet01 As Worksheet
Dim RngMonths As Range
Dim RngTrucks As Range
Dim RngProductList As Range
Dim RngValues As Range
Dim RngTarget As Range
Dim RngRange01 As Range
Dim DblCounter01 As Integer
Dim DblCounter02 As Integer
'Setting WksSource.
Set WksSource = Sheets("TL_Solver")
'Referring to WksSource.
With WksSource
'Setting RngMonths.
Set RngRange01 = .Range("E2")
DblCounter01 = Excel.WorksheetFunction.Min(RngRange01.End(xlToRight).Column, _
.Cells(RngRange01.Row, .Columns.Count).End(xlToLeft).Column _
)
Set RngMonths = .Range( _
RngRange01, _
.Cells(RngRange01.Row, DblCounter01) _
)
'Setting RngTrucks.
Set RngRange01 = .Range("E3")
DblCounter01 = Excel.WorksheetFunction.Min(RngRange01.End(xlToRight).Column, _
.Cells(RngRange01.Row, .Columns.Count).End(xlToLeft).Column _
)
Set RngTrucks = .Range( _
RngRange01, _
.Cells(RngRange01.Row, DblCounter01) _
)
'Setting RngProductList.
Set RngRange01 = RngTrucks.Resize(1, 1).Offset(2, -1)
DblCounter01 = Excel.WorksheetFunction.Min(RngRange01.End(xlDown).Row, _
.Cells(.Rows.Count, RngRange01.Column).End(xlUp).Row _
)
Set RngProductList = .Range( _
RngRange01, _
.Cells(DblCounter01, RngRange01.Column) _
)
'Setting RngValues.
Set RngRange01 = .Cells(RngProductList.Row, RngTrucks.Column)
Set RngValues = RngRange01.Resize(RngProductList.Rows.Count, RngTrucks.Columns.Count)
End With
'Creating a new worksheet for the report.
Set WksReport = ActiveWorkbook.Sheets.Add(After:=WksSource)
'Counting other existing reports if any.
DblCounter01 = 0
For Each WksWorksheet01 In WksReport.Parent.Worksheets()
If Left(WksWorksheet01.Name, 7) = "Report " Then
DblCounter01 = DblCounter01 + 1
End If
Next
'Renaming the current report.
DblCounter02 = DblCounter01
On Error Resume Next
Do Until WksReport.Name = "Report " & DblCounter01
DblCounter01 = DblCounter01 + 1
WksReport.Name = "Report " & DblCounter01
If DblCounter01 - DblCounter02 > 1000 Then GoTo CP_FAILED_RENAMING
Loop
CP_FAILED_RENAMING:
On Error GoTo 0
'Setting RngTarget.
Set RngTarget = WksReport.Range("A1")
'Covering each column in RngValues.
For DblCounter01 = 1 To RngValues.Columns.Count
'Checking if there is any value to report.
If Excel.WorksheetFunction.Sum(RngValues.Columns(DblCounter01).Cells) <> 0 Then
'Inserting the data for the first row of the report's chapter.
With RngTarget
.Offset(0, 1).Value = "Truck #"
.Offset(0, 2).Value = Split(RngTrucks.Cells(1, DblCounter01), "#")(1)
.Offset(0, 3).Value = "Delivery"
If WksSource.Cells(RngMonths.Row, RngTrucks.Columns(DblCounter01).Column).Value = "" Then
.Offset(0, 4).Value = WksSource.Cells(RngMonths.Row, RngTrucks.Columns(DblCounter01).Column).End(xlToLeft).Value
Else
.Offset(0, 4).Value = WksSource.Cells(RngMonths.Row, RngTrucks.Columns(DblCounter01).Column).Value
End If
.Offset(1, 1).Value = "Product"
.Offset(1, 2).Value = "Quantity"
End With
'Offsetting RngTarget by 2 rows in order to enter the data.
Set RngTarget = RngTarget.Offset(2, 0)
'Covering each value in the given column of RngValues.
DblCounter02 = 1
For Each RngRange01 In RngValues.Columns(DblCounter01).Cells
'Checking if the value is not 0.
If RngRange01.Value <> 0 Then
'Inserting the data.
With RngTarget
.Value = DblCounter02
.Offset(0, 1).Value = WksSource.Cells(RngRange01.Row, RngProductList.Column).Value
.Offset(0, 2).Value = RngRange01.Value
End With
DblCounter02 = DblCounter02 + 1
'Offsetting RngTarget to the next row of the report.
Set RngTarget = RngTarget.Offset(1, 0)
End If
Next
'Offsetting RngTarget by 1 row for the next chapter.
Set RngTarget = RngTarget.Offset(1, 0)
End If
Next
'Autofitting the second column of the report.
RngTarget.Offset(0, 1).EntireColumn.AutoFit
End Sub
It dynamically determines the size of the data to process (starting from given cells), it creates a new sheet renamed as "Report n" (based of the n pre-existing sheet already named "Report n") and insert the data as requested.

Related

VBA to copy specific cells from one worksheet to another upon meeting a criteria

My VBA knowledge is very limited, so looking for some help here. Tried some Googling and putting together a code but hasn't met the goal. Appreciate the help here!
I have 2 worksheets:
Data - source worksheet with the data to be copied
Dashboard - Target sheet for pasting
Data sheet - It has multiple columns, the ones I have named are the ones I need to be copied except the column named "Sold?" which is for criteria. The other columns with no names in the image actually have data, to avoid confusion I have removed them here.
This sheet grows and I will add a new row of data when needed.
Dashboard Sheet - When I click "Refresh" button, I want the code to check the "Data" sheet and if a row meets of criteria of Sold? = "N", then only data from column C,G,J,M should be copied and pasted into columns B,C,D,E of "Dashboard" sheet. Additional criteria: if an investment name repeats, the details need to be summed up and shown in Dashboard sheet. I have provided my expected output in the image. (ABC & TY summed up)
I have tried a bit but unable to incorporate all the criteria and this code when run doesn't throw an error but does nothing, no output.
Private Sub Refresh_Click()
Worksheets("Dashboard").Activate
Application.ScreenUpdating = True
a = Worksheets("Data").Cells(Rows.Count, "A").End(xlUp).Row
For i = 12 To a
If Worksheets("Data").Cells(i, 15).Value = "N" Then
Worksheets("Data").Cells(i, 3).Copy
Worksheets("Data").Cells(i, 7).Copy
Worksheets("Data").Cells(i, 13).Copy
Worksheets("Data").Cells(i, 14).Copy
Worksheets("Dashboard").Activate
Worksheets("Dashboard").Range("B6:G25").Select
ActiveSheet.Paste
End If
Next
Application.CutCopyMode = False
End Sub
I strongly suggest a pivot table. Still if you want VBA based solution, you might try this code:
Option Explicit
Private Sub Refresh_Click()
'Declarations.
Dim BlnHiddenColumns() As Boolean
Dim DblFirstRow As Double
Dim DblLastRow As Double
Dim DblCounter01 As Double
Dim DblCounterLimit01 As Double
Dim DblInvestmentNameColumn As Double
Dim DblQuantityColumn As Double
Dim DblAfterChargeColumn As Double
Dim DblCurrentPLColumn As Double
Dim DblSoldColumn As Double
Dim RngData As Range
Dim RngResult As Range
Dim StrAutofilterAddress As String
Dim StrMarker As String
Dim StrInvestmentNameHeader As String
Dim StrQuantityHeader As String
Dim StrAfterChargeHeader As String
Dim StrCurrentPLHeader As String
Dim WksData As Worksheet
Dim WksDashboard As Worksheet
Dim WksPivotTable As Worksheet
Dim PvtPivotTable01 As PivotTable
'Settings.
DblInvestmentNameColumn = 3
DblQuantityColumn = 7
DblAfterChargeColumn = 10
DblCurrentPLColumn = 13
DblSoldColumn = 15
DblFirstRow = 12
DblCounterLimit01 = 1000
StrMarker = "N"
Set WksData = Worksheets("Data")
DblLastRow = WksData.Cells(Rows.Count, "B").End(xlUp).Row
Set RngData = WksData.Range(WksData.Cells(DblFirstRow - 1, Excel.WorksheetFunction.Min(DblInvestmentNameColumn, DblQuantityColumn, DblAfterChargeColumn, DblCurrentPLColumn, DblSoldColumn)), WksData.Cells(DblLastRow, Excel.WorksheetFunction.Max(DblInvestmentNameColumn, DblQuantityColumn, DblAfterChargeColumn, DblCurrentPLColumn, DblSoldColumn)))
ReDim BlnHiddenColumns(1 To RngData.Columns.Count)
Set WksDashboard = Worksheets("Dashboard")
Set RngResult = WksDashboard.Range("B6")
StrInvestmentNameHeader = WksDashboard.Range("B5").Value
StrQuantityHeader = WksDashboard.Range("C5").Value
StrAfterChargeHeader = WksDashboard.Range("D5").Value
StrCurrentPLHeader = WksDashboard.Range("E5").Value
'Turning off screen updating.
Application.ScreenUpdating = False
'Checking for any previous results list.
If Excel.WorksheetFunction.CountBlank(RngResult) <> RngResult.Cells.Count Then
DblCounter01 = 0
'Checking each row of the result list until an entirely blank row is found.
Do Until Excel.WorksheetFunction.CountBlank(RngResult.Offset(DblCounter01, 0)) = RngResult.Cells.Count
DblCounter01 = DblCounter01 + 1
'If the number of rows checked is equal or superior to DblCounterLimit01 the macro is terminated.
If DblCounter01 >= DblCounterLimit01 Then
MsgBox "Please clear the current holdings list manually", vbCritical + vbOKOnly, "Unable to clear the current list"
Exit Sub
End If
Loop
'Clearing the list.
RngResult.Parent.Range(RngResult, RngResult.Offset(DblCounter01 - 1)).ClearContents
End If
'Checking for existing autofilter in WksData.
If WksData.AutoFilterMode = True Then
'Coping the address of the autofilter in WksData.
StrAutofilterAddress = WksData.AutoFilter.Range.Address
End If
'Removing any autofilter in WksData.
WksData.AutoFilterMode = False
'Covering each column of RngData.
For DblCounter01 = 1 To RngData.Columns.Count
'Setting BlnHiddenColumns accordingly to the RngData columns' status (hidden/not hidden).
BlnHiddenColumns(DblCounter01) = RngData.Columns(DblCounter01).Hidden
'Hiding the columns of RngData we won't copy.
Select Case DblCounter01 + RngData.Column - 1
Case Is = DblInvestmentNameColumn, DblQuantityColumn, DblAfterChargeColumn, DblCurrentPLColumn
RngData.Columns(DblCounter01).Hidden = False
Case Else
RngData.Columns(DblCounter01).Hidden = True
End Select
Next
'Filtering RngData.
RngData.AutoFilter Field:=DblSoldColumn - RngData.Column + 1, Criteria1:=StrMarker
'Copying the filtered RngData into RngResult.
RngData.Resize(RngData.Rows.Count - 1, RngData.Columns.Count).Offset(1, 0).SpecialCells(xlCellTypeVisible).Copy RngResult
'Restoring the RngData columns to their previous status (hidden/not hidden).
For DblCounter01 = 1 To RngData.Columns.Count
If BlnHiddenColumns(DblCounter01) Then
RngData.Columns(DblCounter01).Hidden = True
Else
RngData.Columns(DblCounter01).Hidden = False
End If
Next
'Removing any autofilter in WksData.
WksData.AutoFilterMode = False
'Restoring any pre-existing autofilter in WksData.
If StrAutofilterAddress <> "" Then
WksData.Range(StrAutofilterAddress).AutoFilter
End If
'Setting RngResult to cover the imported list (headers included).
Set RngResult = RngResult.Offset(-1, 0)
Set RngResult = WksDashboard.Range(RngResult, RngResult.End(xlDown).End(xlToRight))
'Creating WksPivotTable.
Set WksPivotTable = Sheets.Add
'Creating PvtPivotTable01.
Set PvtPivotTable01 = ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, _
SourceData:=RngResult, _
Version:=7 _
).CreatePivotTable(TableDestination:=WksPivotTable.Cells(1, 1), _
TableName:="Temporary Pivot Table", _
DefaultVersion:=7 _
)
'Setting PvtPivotTable01.
With PvtPivotTable01.PivotFields(StrInvestmentNameHeader)
.Orientation = xlRowField
.Position = 1
End With
With PvtPivotTable01
.AddDataField .PivotFields(StrQuantityHeader), "Sum of " & StrQuantityHeader, xlSum
.AddDataField .PivotFields(StrAfterChargeHeader), "Sum of " & StrAfterChargeHeader, xlSum
.AddDataField .PivotFields(StrCurrentPLHeader), "Sum of " & StrCurrentPLHeader, xlSum
.ColumnGrand = False
End With
'Clearing the data from RngResult.
RngResult.Offset(1, 0).Resize(RngResult.Rows.Count - 1).ClearContents
'Copying the PvtPivotTable01 content to RngResult.
PvtPivotTable01.DataBodyRange.Offset(0, -1).Resize(, PvtPivotTable01.DataFields.Count + 1).Copy RngResult.Cells(2, 1)
'Deleting WksPivotTable.
Application.DisplayAlerts = False
WksPivotTable.Delete
Application.DisplayAlerts = True
'Restoring screen updating.
Application.ScreenUpdating = False
End Sub
I've intentionally made it longer than the necessary, especially by creating many variables to avoid hard coded data. This method might be useful in more complex and/or longer codes.

Looping vlookup through predefined named range in multiple sheets

So I've been solving this problem of mine for a couple days already.
Basically, I have multiple green sheets (my source sheets) and one main sheet (master sheet), the problem I'm working on has to do with looping through these green sheets in order to pull certain information from them and put it on certain columns in my master sheet.
Here's the layout of one of these green sheets for better understanding:
https://imgur.com/cayZXUA
I'm sorry for the links, cant add images yet
You can see that these green sheets consist of multiple boxes which can differ in size from sheet to sheet. Some of the values I need to retrieve are fixed in the same cell address for all green sheets so I have no problem getting them to the master sheet. But there are some cases like this:
https://imgur.com/nPYyLbM
Assumption box contains information that I need to lookup and pull it to Main sheet. In essence, this box can take up vertically any space so that address for values of payroll, tax and miscellaneous expenditures changes.
I came up with the idea of giving these boxes in all green sheets name "Assumptions" like seen in the image above. So the questions is how do I lookup 3rd column of this named box and pull it to main sheet?
Here's Main sheet structure:
https://imgur.com/CWMpGvH
My code so far:
Sub CombiningSheets()
Dim p_value, cst_value, m_value As Long
Dim p, cst, m As String
p = "payroll"
cst = "consolidated social tax"
m = "miscellaneous expenditures"
With ThisWorkbook.Sheets("Main")
For Each wsheet In ThisWorkbook.Sheets
If wsheet.Name <> "Main" Then
Set nextEntry = .Cells(.Rows.Count, "G").End(xlUp).Offset(1, 0)
Set nextEntry_FTE_quantity = .Cells(.Rows.Count, "K").End(xlUp).Offset(1, 0)
Set nextEntry_nonrecurring_expenses = .Cells(.Rows.Count, "S").End(xlUp).Offset(1, 0)
Set nextEntry_initiative_type = .Cells(.Rows.Count, "Q").End(xlUp).Offset(1, 0)
Set nextEntry_initiative_description = .Cells(.Rows.Count, "E").End(xlUp).Offset(1, 0)
Set nextEntry_economic_benefit = .Cells(.Rows.Count, "AA").End(xlUp).Offset(1, 0)
Set nextEntry_payroll = .Cells(.Rows.Count, "AI").End(xlUp).Offset(1, 0)
Set nextEntry_consolidated_social_tax = .Cells(.Rows.Count, "AJ").End(xlUp).Offset(1, 0)
Set nextEntry_miscellaneous_expenditures = .Cells(.Rows.Count, "AK").End(xlUp).Offset(1, 0)
If IsError(Application.Match(wsheet.Name, .Range("G:G"), 0)) Then
nextEntry.Value = wsheet.Name
nextEntry_initiative_description.Value = wsheet.Range("K6").Value
nextEntry_FTE_quantity.Value = wsheet.Range("BH16").Value
nextEntry_initiative_type.Value = wsheet.Range("K8").Value
nextEntry_nonrecurring_expenses.Value = wsheet.Range("BH17").Value
nextEntry_economic_benefit.Value = wsheet.Range("BH15").Value
End If
End If
Debug.Print wsheet.Name
Next wsheet
End With
End Sub
From your questions it seems that you have defined named ranges. As I'm aware of your question How to copy sheets with certain tab color from one workbook to another? I do believe that you don't have named ranges on your individual sheets.
Below you find some code if you have named ranges (Sub List_NamedRange_Loop).If you don't have named ranges you can create these named ranges on the individual sheets first (Sub Create_NamedRange).
At the end of this post you find a screenshot of the result I got.
Sub List_NamedRange_Loop()
Dim NamedRange As Name
Dim ws As Worksheet
Dim PrDebug As Boolean
Dim iCt As Integer
PrDebug = False ' => Output to Worksheet "Main"
'PrDebug = True ' => Output to Immediate Window (Ctrl-G in VBE)
'List on sheet "main"
If Not (PrDebug) Then
On Error Resume Next
Debug.Print ActiveWorkbook.Name
Sheets("main").Activate
If ActiveSheet.Name <> "main" Then
Worksheets.Add
ActiveSheet.Name = "main"
End If
On Error GoTo 0
Range("A1:D1000").ClearContents
Range("A1").Value = "Sheet Name"
Range("B1").Value = "Named Range"
Range("C1").Value = "RefersTo"
Range("D1").Value = "Value (Direct Reference)"
Range("E1").Value = "Value (Named Reference)"
End If
'We expect all named ranges to be local = defined on the indivdual sheets
'so no need for the below 'workbook loop'
'Loop through each named range in workbook
' For Each namedrange In ActiveWorkbook.Names
' Debug.Print namedrange.Name, namedrange.RefersTo
' Next namedrange
'Loop through each named range scoped to a specific worksheet
iCt = 0
For Each ws In Worksheets
iCt = iCt + 1
If ws.Names.Count > 0 Then
If PrDebug Then
Debug.Print
Debug.Print ws.Name
Else
End If
For Each NamedRange In ws.Names 'Worksheets("Sheet1").Names
If PrDebug Then
Debug.Print ws.Name, NamedRange.Name, NamedRange.RefersTo
Else
iCt = iCt + 1
Range("A1").Offset(iCt, 0).Value = ws.Name
' Range("B1").Offset(iCt, 0).Value = Replace(NamedRange.Name, ws.Name & "!", "")
If InStr(1, NamedRange.Name, "'") Then
Range("B1").Offset(iCt, 0).Value = Replace(NamedRange.Name, "'" & ws.Name & "'!", "")
Else
Range("B1").Offset(iCt, 0).Value = Replace(NamedRange.Name, ws.Name & "!", "")
End If
Range("C1").Offset(iCt, 0).Value = "'" & NamedRange.RefersTo
Range("D1").Offset(iCt, 0).Value = NamedRange.RefersTo
Range("E1").Offset(iCt, 0).Formula = "=" & NamedRange.Name
Range("E1").Offset(iCt, 0).Calculate
End If
Next NamedRange
Else
' iCt = iCt + 1
' Range("A1").Offset(iCt, 0).Value = ws.Name
' Range("B1").Offset(iCt, 0).Value = "NO NAMES DEFINED!"
End If
Next ws
End Sub
If you don't have named ranges you might create them with the code similar to the following:
Sub Create_NamedRange()
Dim ws As Worksheet
Dim foundRange As Range
For Each ws In Worksheets
If ws.Name <> "main" Then
Debug.Print ws.Name
Set foundRange = ws.Cells.Find(What:="payroll", After:=ActiveCell, LookIn:=xlValues, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
True, SearchFormat:=False)
If Not (foundRange Is Nothing) Then
Debug.Print "Found: "; ws.Name
'offset between AR and BH: 16 columns (https://imgur.com/nPYyLbM)
ws.Names.Add Name:="payroll", RefersTo:=foundRange.Offset(0, 16)
ws.Names.Add Name:="consolidated_social_tax", RefersTo:=foundRange.Offset(1, 16)
ws.Names.Add Name:="miscellaneous_expenditures", RefersTo:=foundRange.Offset(2, 16)
End If
End If
Next ws
End Sub
I would use Range.Find to locate the cells by keywords and return the values adjacent to them.
Sub TestFind()
Dim colOffset As Long
Dim wsheet As Worksheet
colOffset = Columns("BH").Column - Columns("AR").Column - 2 'Two Extra Cells in Merged Range Adjustment
For Each wsheet In ThisWorkbook.Worksheets
If wsheet.Name <> "Main" Then
Debug.Print FindValueRelativeToSearch(wsheet.Columns("AR"), "payroll", 0, colOffset)
Debug.Print FindValueRelativeToSearch(wsheet.Columns("AR"), "social tax", 0, colOffset)
Debug.Print FindValueRelativeToSearch(wsheet.Columns("AR:AT"), "miscellaneous expenditures", 0, colOffset)
End If
Next
End Sub
Function FindValueRelativeToSearch(SearchRange As Range, search As String, rowOffset As Long, colOffset As Long) As Variant
Dim cell As Range
Application.FindFormat.MergeCells = True
With SearchRange
Set cell = .Find(What:=search, After:=.Cells(1, 1), _
LookIn:=xlFormulas, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=True)
End With
cell.Offset(rowOffset, colOffset).Activate
If cell Is Nothing Then
Debug.Print "Search not found: FindValueRelativeToSearch()", SearchRange.Address(0, 0, xlA1, True), search
Else
FindValueRelativeToSearch = cell.Offset(rowOffset, colOffset).Value
End If
End Function

How to do a nested search based on multiple tab dependent values?

I have three worksheets, Accounts, JEExtracts & Detail Extracts.
I would like to search based on values from Accounts WS which is unique and find all matches from JEExtracts, then based on all matching values found, take values of another cell corresponding to that row and search all instances from Detail extracts WS.
When I do that, the first iteration works. In the second iteration the searchstring loses its value. It ends in error object not defined.
Sub FilterAccount()
Dim c As Range
Dim searchRng As Range
Dim searchRng2 As Range
Dim LastAcc As Long
Dim LastRowJE As Long
Dim LastRowDE As Long
Dim fAddress
Dim fAddress2
LastAcc = Sheets("Accounts").Cells(2, 1).End(xlDown).Row
LastRowJE = Sheets("JournalExtract").Cells(2, 2).End(xlDown).Row
LastRowDE = Sheets("DetailExtract").Cells(2, 10).End(xlDown).Row
LastAcc = LastAcc - 1
LastRowJE = LastRowJE - 1
LastRowDE = LastRowDE - 1
ACRow = 2
ACCol = 1
JERow = 2
JECol = 7
DERow = 2
DECol = 10
Worksheets("Accounts").Activate
Application.ScreenUpdating = False
'Loop through cells to do the lookup based on value on a particular column of worksheet Accounts
For Each c In Sheets("Accounts").Range(Cells(ACRow, ACCol), Cells(LastAcc, ACCol))
'MsgBox (c.Value)
If IsEmpty(c) = True Then Exit For 'If there is no value found in the cell then exit from the process
If IsEmpty(c) = False Then 'If there is value found in the cell then search the same value in JournalExtract
Worksheets("JournalExtract").Activate
With Sheets("JournalExtract").Range(Cells(JERow, JECol), Cells(LastRowJE, JECol)) 'Using the cells looking up resource name in pivot tab
Set searchRng = .Find(What:=c.Value) 'Find it
If Not searchRng Is Nothing Then 'If we find a value
fAddress = searchRng.Address 'Set the address to compare
Do
searchRng.Offset(0, 0).Cells.Interior.Color = RGB(255, 0, 0)
Worksheets("DetailExtract").Activate
'Using the value from worksheet JournalExtract looking up value in DetailExtract
With Sheets("DetailExtract").Range(Cells(DERow, DECol), Cells(LastRowDE, DECol))
Set searchRng2 = .Find(What:=searchRng.Offset(0, 4)) 'Find it
If Not searchRng2 Is Nothing Then
fAddress2 = searchRng2.Address
Do
searchRng2.Offset(0, 0).Cells.Interior.Color = RGB(255, 255, 0)
Set searchRng2 = .FindNext(searchRng2)
Loop While Not searchRng2 Is Nothing And searchRng2.Address <> fAddress2
End If
Set searchRng2 = Nothing
End With
Worksheets("JournalExtract").Activate
Set searchRng = .FindNext(searchRng) 'Doesn't get value in 2nd iteration
Loop While Not searchRng Is Nothing And searchRng.Address <> fAddress 'Here error is thrown - Object value not set.
End If
End With
End If
Set searchRng = Nothing
Next
Application.ScreenUpdating = True
End Sub
A Find/FindNext pair can only be used one at a time. If you try a nested Find/FindNext using the value from the first Find/FindNext, the first is removed and replaced by the second. You need an alternative method of location for the nested lookup or you can isolate each process.
This is hopefully closer to what you need but I didn't fully test it. It builds a union from the results of the first Find/FindNext pair then cycles through that union of ranges to process the second Find/FindNext pair.
Option Explicit
Sub FilterAccount()
Dim c As Range, s As Range
Dim searchRng As Range, foundRng As Range
Dim searchRng2 As Range
Dim LastAcc As Long, LastRowJE As Long, LastRowDE As Long
Dim ACRow As Long, ACCol As Long, JERow As Long, JECol As Long, DERow As Long, DECol As Long
Dim fAddress As String, fAddress2 As String
LastAcc = Worksheets("Accounts").Cells(Rows.Count, "A").End(xlUp).Row - 1
LastRowJE = Worksheets("JournalExtract").Cells(Rows.Count, "B").End(xlUp).Row - 1
LastRowDE = Worksheets("DetailExtract").Cells(Rows.Count, "J").End(xlUp).Row - 1
ACRow = 2
ACCol = 1
JERow = 2
JECol = 7
DERow = 2
DECol = 10
With Worksheets("Accounts")
'Loop through cells to do the lookup based on value on a particular column of worksheet Accounts
For Each c In .Range(.Cells(ACRow, ACCol), .Cells(LastAcc, ACCol))
'If there is no value found in the cell then exit from the process
If IsEmpty(c) Then
Exit For
Else
With Worksheets("JournalExtract")
'Using the cells looking up resource name in pivot tab
With .Range(.Cells(JERow, JECol), .Cells(LastRowJE, JECol))
Set searchRng = .Find(What:=c.Value) 'Find it
'If we find a value
If Not searchRng Is Nothing Then
fAddress = searchRng.Address 'Set the address to compare
Set foundRng = searchRng
'collect all the searchRngs into a union
Do
Set foundRng = Union(foundRng, searchRng)
Set searchRng = .FindNext(after:=searchRng)
Loop While searchRng.Address <> fAddress
foundRng.Cells.Interior.Color = RGB(255, 0, 0)
'now on to the second search
'cycle through the union
For Each s In foundRng
With Worksheets("DetailExtract")
'Using the value from worksheet JournalExtract looking up value in DetailExtract
With .Range(.Cells(DERow, DECol), .Cells(LastRowDE, DECol))
Set searchRng2 = .Find(What:=c.Offset(0, 4)) 'Find it
If Not searchRng2 Is Nothing Then
fAddress2 = searchRng2.Address
Do
searchRng2.Offset(0, 0).Cells.Interior.Color = RGB(255, 255, 0)
Set searchRng2 = .FindNext(searchRng2)
Loop While searchRng2.Address <> fAddress2
End If
End With
End With
Next s
End If
End With
End With
End If
Next c
End With
End Sub
You can use SQL to query your data. Note that I changed Accounts to Account. Sample workbook.
Sub FindValues()
Dim c%, sql$, conn_string$
Dim rs As Object
Dim wksOutput As Worksheet
conn_string = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & ThisWorkbook.FullName & ";" & _
"Extended Properties=""Excel 12.0"";"
Set rs = CreateObject("ADODB.Recordset")
rs.CursorLocation = adUseClient
sql$ = "SELECT A.Account, J.[Link ID], DE.[Values] " & _
"FROM ([Accounts$] AS A " & _
"INNER JOIN [JEExtracts$] AS J " & _
"ON A.Account = J.Account) " & _
"INNER JOIN ['Detail Extracts$'] AS DE " & _
"ON J.[Link ID] = DE.[Link ID];"
rs.Open sql, conn_string, adOpenForwardOnly, adLockReadOnly
If rs.RecordCount > 0 Then
Set wksOutput = Sheets.Add(After:=Sheets(Sheets.Count))
wksOutput.Name = "output"
With wksOutput
'// Output headers
For c = 0 To rs.Fields.Count - 1
.Cells(1, c + 1) = rs.Fields(c).Name
Next
.Range("A2").CopyFromRecordset rs
End With
Else
MsgBox "No records were found.", vbExclamation
End If
rs.Close
Set rs = Nothing
End Sub

Deducing column from User defined range in Excel VBA

Edit: #TimWilliams I edited the code as follows but it it doesn't run at all now. ANy thoughts?
Sub Item_Fix()
Dim rng As Range, col As Range, arr
Dim sht As Worksheet, c As Range, tmp
On Error Resume Next 'in case user cancels
Set rng = Application.InputBox( _
Prompt:="Please select the Items to update. " & _
" (e.g. Column A or Column B)", _
Title:="Select Range", Type:=8)
On Error GoTo 0
' Set hdr = Application.InputBox( _
' Prompt:="Does your selection contain headers?", _
' Title:="Header Option")
hdr = MsgBox("Does your selection contain a header?", vbYesNo + vbQuestion, "Header Option")
If rng Is Nothing Then Exit Sub
If rng.Columns.Count > 1 Then
MsgBox "Please select only a single column!", vbExclamation
Exit Sub
End If
Set sht = rng.Parent
Set col = sht.Range(sht.Cells(2, rng.Column), _
sht.Cells(sht.Rows.Count, rng.Column).End(xlUp))
Application.ScreenUpdating = False
If hdr = vbYes Then
For Each c In col.Cells
tmp = Trim(c.Value)
If Len(tmp) > 0 And Len(tmp) < 9 And Row > 1 Then
c.NumberFormat = "#"
c.Value = Right("000000000" & tmp, 9)
End If
Next c
End If
If hdr = vbNo Then
For Each c In col.Cells
tmp = Trim(c.Value)
If Len(tmp) > 0 And Len(tmp) < 9 Then
c.NumberFormat = "#"
c.Value = Right("000000000" & tmp, 9)
End If
Next c
Application.ScreenUpdating = True
End If
End Sub
I'm trying to write a function that will insert leading zeroes into a column that a user specifies. Honestly, I would love for this to be like the Excel Menu Data > Remove Duplicates option. I want to click on a menu button and then select my range and let it do the magic, unfortunately I keep getting errors when trying to deduce the column that has been selected. Other than that issue, it should work fine. My code is below. Any help would be greatly appreciated!
Sub Item_Fix()
'Set Item = Application.InputBox("Select the range that contains the Items").Column
Set IC = Application.InputBox(Prompt:= _
"Please select the Range of Items. (e.g. Column A or Column B)", _
Title:="SPECIFY RANGE", Type:=8).Column
'Set Items = vRange.Column
Set Items = IC.Column
Lastrow = Worksheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
Items.EntireColumn.Offset(0, 1).Insert
For i = 2 To Lastrow
Cells(i, Items + 1).Formula = "=Text(" & Cells(i, Items) & ",""000000000"")"
Next i
NewColumn = Items + 1
NewColumn.EntireColumn.Copy
Items.PasteSpecial xlPasteValues
NewColumn.EntireColumn.Delete
End Sub
#Jeeped has the right approach I think, but since you asked for a version of your original...
Sub Item_Fix()
Dim rng As Range, col As Range, arr
Dim sht As Worksheet, c As Range, tmp
On Error Resume Next 'in case user cancels
Set rng = Application.InputBox( _
Prompt:="Please select the Items to update. " & _
" (e.g. Column A or Column B)", _
Title:="Select Range", Type:=8)
On Error GoTo 0
If rng Is Nothing Then Exit Sub
If rng.Columns.Count > 1 Then
MsgBox "Please select only a single column!", vbExclamation
Exit Sub
End If
Set sht = rng.Parent
Set col = sht.Range(sht.Cells(2, rng.Column), _
sht.Cells(sht.Rows.Count, rng.Column).End(xlUp))
Application.ScreenUpdating = False
For Each c In col.Cells
tmp = Trim(c.Value)
If Len(tmp) > 0 And Len(tmp) < 9 Then
c.NumberFormat = "#"
c.Value = Right("000000000" & tmp, 9)
End If
Next c
Application.ScreenUpdating = True
End Sub
Let the user select a group of cells to receive the procedure. An InputBox method seems like one extra step and an impediment to the workflow.
Sub make_DUNS_number()
Dim duns As Range, tmp As String
For Each duns In Selection
'possible error control on non-numeric values
'if isnumeric(duns.value2) then
tmp = Right("000000000" & Format(duns.Value2, "000000000;#"), 9)
duns.NumberFormat = "#"
duns.Value2 = tmp
'end if
Next duns
End Sub
With that in place, you should have no trouble adding it to the QAT. See Add Buttons to the Quick Access Toolbar and Customize Button Images for more information.
Selection = Evaluate("index(text(" & Selection.Address & ",""'000000000""),,1)")

How to merge data from multiple sheets?

Update: sample file sample workbook
Problem: I would like to have something that can automatically
1. Search for the part number and revision. After the cell which contains "PART NUMBER" and "REVISION" is found, I need to get the values of below two cell (offset 1 column).
2. It will continue to look for the summary table
3. Put the summary table to a result sheet
4. Continue searching and repeat the process
There are:
Possible of multiple parts number on the same sheet or just 1
Only searching for the Sheet with starting name: "Search"
First Picture shows the structure of the file and the Second Picture shows the result
This will help a lot if it is doable. Please help me.
Update 1:
Logic as I think:
1. Write a module to search for all sheets starting with name "SEARCH"
Go to each sheet resulted from step 1 - to search .NEXT for PART NUMBER and REVISION to get all part number name and revision (addressing by offset(0,1))
Start to search for the summary table ==> It gets to complicated point
Wow, this takes me back to the days when I had to do this nasty stuff a lot!
Anyway, I wrote some code that gets what you want. I may have taken a different approach than you may have thought, but I think it's kind of similar.
Assumptions
PART NUMBER is always in Column B
REVISION is always in Column F
Double check all other references against your original data. I could not access your workbook (due to my work office security), so I made my own book up based on your screenshots).
Option Explicit
Sub wowzer()
Dim wks As Worksheet, wksResult As Worksheet
'set up results sheet
Set wksResult = Worksheets.Add(After:=Worksheets(Worksheets.Count))
With wksResult
.Name = "Results"
.Range("A1:F1") = Array("Part", "Rev", "Name", "Category", "TotalA", "TotalB")
End With
'loop through sheets to get data over
For Each wks In Worksheets
If Left(wks.Name, 6) = "Search" Then ' does sheet start with search?
With wks
Dim rngFindPart As Range, rngFindName As Range
Set rngFindPart = .Columns(2).Find("PART NUMBER", lookat:=xlWhole, After:=.Range("B" & .Rows.Count))
Set rngFindName = .Columns(2).Find("NAME", lookat:=xlWhole, After:=.Range("B" & .Rows.Count))
Dim strFrstAdd As String
strFrstAdd = rngFindPart.Address 'used as a check for when we loop back and find first "PART NUMBER" again
If Not rngFindPart Is Nothing Or Not rngFindName Is Nothing Then
'not going to do anything if no PART NUMBER or NAME found
Do
Dim rngMove As Range
'copy table and place it in result sheet
Set rngMove = .Range(rngFindName.Offset(1).Address, rngFindName.End(xlToRight).End(xlDown))
rngMove.Copy wksResult.Range("C" & wksResult.Rows.Count).End(xlUp).Offset(1)
'place part and revision, aligned with table (will de-duplicate later)
With wksResult
.Range(.Range("A" & .Rows.Count).End(xlUp).Offset(1), .Range("A" & .Rows.Count).End(xlUp).Offset(2)) = rngFindPart.Offset(1)
.Range(.Range("B" & .Rows.Count).End(xlUp).Offset(1), .Range("B" & .Rows.Count).End(xlUp).Offset(2)) = rngFindPart.Offset(1, 4)
End With
'find next instance of "PART NUMBER" and "NAME"
Set rngFindPart = .Columns(2).Find("PART NUMBER", lookat:=xlWhole, After:=rngFindPart)
Set rngFindName = .Columns(2).Find("NAME", lookat:=xlWhole, After:=rngFindPart)
'done when no part number exists or it's the first instance we found
Loop Until rngFindPart Is Nothing Or rngFindPart.Address = strFrstAdd
End If
End With
End If
Next
'de-duplicate results sheet
With wksResult
'if sheet is empty do nothing
If .Cells(2, 1) <> vbNullString Then
.UsedRange.RemoveDuplicates Columns:=Array(1, 2, 3, 4, 5, 6), Header:=xlYes
End If
End With
End Sub
Is this what you are trying?
CODE
Option Explicit
Const SearchString As String = "PART NUMBER"
Dim wsO As Worksheet, WsI1 As Worksheet, WsI2 As Worksheet
Dim lRow As Long
Sub Sample()
Set wsO = Sheets("Result")
Set WsI1 = Sheets("SEARCH PAGE1")
Set WsI2 = Sheets("SEARCH PAGE2")
lRow = 2
PopulateFrom WsI1
PopulateFrom WsI2
End Sub
Sub PopulateFrom(ws As Worksheet)
Dim aCell As Range, bCell As Range, cCell As Range, nmRng As Range, cl As Range
Dim i As Long
Dim ExitLoop As Boolean
With ws
Set aCell = .Cells.Find(What:=SearchString, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
Set bCell = aCell
wsO.Range("A" & lRow).Value = aCell.Offset(1).Value
wsO.Range("B" & lRow).Value = aCell.Offset(1, 3).Value
i = 1
Do
If aCell.Offset(i) = "NAME" Then
Set nmRng = .Range(aCell.Offset(i), aCell.Offset(i).End(xlDown))
For Each cl In nmRng
If cl.Value <> "NAME" Then
If wsO.Range("A" & lRow).Value = "" Then
wsO.Range("A" & lRow).Value = wsO.Range("A" & lRow - 1).Value
wsO.Range("B" & lRow).Value = wsO.Range("B" & lRow - 1).Value
End If
wsO.Range("C" & lRow).Value = cl.Value
wsO.Range("D" & lRow).Value = cl.Offset(, 1).Value
wsO.Range("E" & lRow).Value = cl.Offset(, 2).Value
wsO.Range("F" & lRow).Value = cl.Offset(, 3).Value
lRow = lRow + 1
End If
Next
Exit Do
End If
i = i + 1
Loop
Do While ExitLoop = False
Set aCell = .Cells.FindNext(After:=aCell)
If Not aCell Is Nothing Then
If aCell.Address = bCell.Address Then Exit Do
wsO.Range("A" & lRow).Value = aCell.Offset(1).Value
wsO.Range("B" & lRow).Value = aCell.Offset(1, 3).Value
i = 1
Do
If aCell.Offset(i) = "NAME" Then
Set nmRng = .Range(aCell.Offset(i), aCell.Offset(i).End(xlDown))
For Each cl In nmRng
If cl.Value <> "NAME" Then
If wsO.Range("A" & lRow).Value = "" Then
wsO.Range("A" & lRow).Value = wsO.Range("A" & lRow - 1).Value
wsO.Range("B" & lRow).Value = wsO.Range("B" & lRow - 1).Value
End If
wsO.Range("C" & lRow).Value = cl.Value
wsO.Range("D" & lRow).Value = cl.Offset(, 1).Value
wsO.Range("E" & lRow).Value = cl.Offset(, 2).Value
wsO.Range("F" & lRow).Value = cl.Offset(, 3).Value
lRow = lRow + 1
End If
Next
Exit Do
End If
i = i + 1
Loop
Else
ExitLoop = True
End If
Loop
End If
End With
End Sub
SAMPLE FILE
i.minus.com/1338702873/20yZJWFxgv9Egtd4klNOBg/dtst1Y4ckv86f/Sample.xlsm

Resources