Data Extraction over multiple worksheets - vba

I'm totally new to VBA and I'm trying to script an excel module to extract a specific section on each sheet of a workbook and format them and output together to 1 sheet on a new workbook.
So far I have this;
Public Sub extractCol()
' Find FF&E Section, Add 3 rows and Identify relevant columns.
Dim rFind As Range
With Range("A:A")
Set rFind = .Find(What:="FF&E", LookAt:=xlWhole, MatchCase:=False,
SearchFormat:=False)
If Not rFind Is Nothing Then
NumRange = rFind.Row + 3 ' Find FF&E line and add three
CRange = "C" & NumRange & ":" & "C" & NumRange + 100 ' Define First 100
Lines in Column C
ERange = "E" & NumRange & ":" & "E" & NumRange + 100 ' Define First 100
Lines in Column E
KRange = "K" & NumRange & ":" & "K" & NumRange + 100 ' Define First 100
Lines in Column K
MRange = "M" & NumRange & ":" & "M" & NumRange + 100 ' Define First 100
Lines in Column M
Set range1 = Union(Range(CRange), Range(ERange), Range(KRange),
Range(MRange)) ' Combine individual column ranges in to one selection
range1.Copy ' Copy new combined range
Set NewBook = Workbooks.Add ' Open new Workbook
ActiveCell.PasteSpecial Paste:=xlPasteValues ' Paste to new Workbook
End If
End With
End Sub
This is great as it extracts the bits that I wan't correctly but it only does the current sheet. How do I loop this to do all sheets?
Secondly I would like to paste all the results to the same sheet under one another?
And finally I have the below script that extracts the sheet name and formats it. Ideally I would like to add a column to the output above which would display this data depending on which sheet it came from.
Function FindRoom()
shtName = ActiveSheet.Name
Dim arr() As String
arr = VBA.Split(shtName, " ")
xCount = UBound(arr)
If xCount < 1 Then
FindRoom = ""
Else
FindRoom = arr(xCount)
End If
End Function
Sorry, I know this is not a simple one answer question but any help would be grateful even if it's just pointing me in the right direction.

Try this. I've added a worksheet variable ws. This puts the sheet name in column A of the new workbook, and the data in col B onwards. I have also added declarations for all the variables.
Public Sub extractCol()
'Find FF&E Section, Add 3 rows and Identify relevant columns.
Dim rFind As Range, CRange As String, ERange As String, KRange As String, MRange As String
Dim ws As Worksheet
Dim NewBook As Workbook
Dim NumRange As Long
Set NewBook = Workbooks.Add ' Open new Workbook
For Each ws In ThisWorkbook.Worksheets
With ws
Set rFind = .Range("A:A").Find(What:="FF&E", LookAt:=xlWhole, MatchCase:=False, SearchFormat:=False)
If Not rFind Is Nothing Then
NumRange = rFind.Row + 3 ' Find FF&E line and add three
CRange = "C" & NumRange & ":" & "C" & NumRange + 100 ' Define First 100 Lines in Column C
ERange = "E" & NumRange & ":" & "E" & NumRange + 100 ' Define First 100 Lines in Column E
KRange = "K" & NumRange & ":" & "K" & NumRange + 100 ' Define First 100 Lines in Column K
MRange = "M" & NumRange & ":" & "M" & NumRange + 100 ' Define First 100 Lines in Column M
Set range1 = Union(.Range(CRange), .Range(ERange), .Range(KRange), .Range(MRange)) ' Combine individual column ranges in to one selection
range1.Copy ' Copy new combined range
NewBook.Sheets(1).Range("B" & Rows.Count).End(xlUp)(2).PasteSpecial Paste:=xlPasteValues ' Paste to new Workbook
NewBook.Sheets(1).Range("A" & Rows.Count).End(xlUp)(2).Resize(range1.Rows.Count).Value = FindRoom(ws)
End If
End With
Next ws
End Sub
Function FindRoom(ws As Worksheet)
shtName = ws.Name
Dim arr() As String
arr = VBA.Split(shtName, " ")
xCount = UBound(arr)
If xCount < 1 Then
FindRoom = ""
Else
FindRoom = arr(xCount)
End If
End Function

Related

VBA matching and update existing and add new field into Next Empty Cell on next row

I'm new here and I am trying to do a VBA Vlookup function.
My objectives is to VLookup Table1 from Sheet 1 to Table2 from Sheet2 using Column A and update column B and C if A is existing.
If A is not existing to add into Table1 next blank row with column B and C also included.
Please refer below image - expectation of Sheet1 with updated results.
Thank you in advance.
Currently able to code only to update existing fields but not sure how to add fields that does not match into next blank row of Sheet1.
Sub getOpenExcel()
' Your daily report has a date in it's name
' to select an open workbook we must first know it's name
' AND - it must be already open
' Your examples are 2017-03-11-18875, 2017-03-12-18875, 2017-03-13-18875
' If the name is the current date then this would work to get the filename
Dim fileName As String, monthNum As String, dayNum As String, wb1 As Workbook, wb2 As Workbook
Dim ws1 As Worksheet, ws2 As Worksheet, rng1 As Range, rng2 As Range
' this adds a ZERO to the front of month numbers less than 10
If Month(Date) < 10 Then
monthNum = "0" & CStr(Month(Date))
Else
monthNum = CStr(Month(Date))
End If
' You may or may not need this section
' it adds a ZERO to the front of day numbers less than 10
If Day(Date) < 10 Then
dayNum = "0" & CStr(Day(Date))
Else
dayNum = CStr(Day(Date))
End If
' many cases the daily report will come from the previous day
' If your file has yesterday's date, then comment out the above code and
'uncomment the following code
'
'If Day(DateAdd("d", -1, Date)) < 10 Then
' dayNum = "0" & Day(DateAdd("d", -1, Date))
'Else
' dayNum = Day(DateAdd("d", -1, Date))
'End If
fileName = "GREENBILL_RECON_DETAILED_REPORT_" & CStr(Year(Date)) & monthNum & dayNum
' if today's date is 3/14/17 then "fileNem" = "2017-03-12-18875"
' If your daily report is an excel book, then we need to add the proper extension.
' It could be one of many, "xls", ".xlsx" , ".xlsm", etc....
' If your daily report is open - look at the top. It should have the file name and extension.'
' Replace the below extension with the correct one.
fileName = fileName & ".csv"
' Again, if today's date is 3/14/17 then "fileNem" = "2017-03-12-18875.xlsx"
' This is where we set both workbooks to variables
'
Set wb1 = ThisWorkbook
Set ws1 = wb1.Worksheets("Sheet1")
On Error GoTo notOpen
Set wb2 = Workbooks(fileName) ' This is your daily report
On Error GoTo 0
Set ws2 = wb2.Worksheets("GREENBILL_RECON_DETAILED_REPORT")
ws1.Activate
'*************************************************************************************
' If successful this is the area where you put your code to copy and paste automatically '
' If you need this pasted to the first empty row at bottom of page then 'put code here to find the first empty row and use that varaible
' with range("a" & firstUnusedRow) intstead of A1 ...
wb2.Activate
Range("A1:Z500").Copy _
Destination:=wb1.Worksheets("Sheet1").Range("A1") 'change A1 to A &
firstUnusedRow
'*************************************************************************************
' This is the clean up and exit code
Set wb1 = Nothing
Set wb2 = Nothing
Exit Sub
notOpen:
On Error GoTo 0
Set wb1 = Nothing
MsgBox "The file " & fileName & " is not open"
Exit Sub
End Sub
Sub Rectangle3_Click()
On Error Resume Next
Dim Dept_Row As Long ' To Change to Billing_Acc
Dim Dept_Clm As Long ' To Change to Org_Seqno
Table1 = Sheet1.Range("A1:A10") ' Input file name
Table2 = Sheet2.Range("A1:B10") ' Range of table
Dept_Row = Sheet1.Range("B1").Row
Dept_Clm = Sheet1.Range("B1").Column
For Each cl In Table1
Sheet1.Cells(Dept_Row, Dept_Clm) = Application.WorksheetFunction.VLookup(cl, Table2, 2, False)
Dept_Row = Dept_Row + 1
Next cl
MsgBox "Done"
End Sub
Below code is done iterating thru all the rows by matching column A of both sheets. If not found this will add a new line in sheet1.
Dim lngRow1, lngRow2 As Long
lngRow1 = Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
lngRow2 = Sheets("Sheet2").Cells(Rows.Count, 1).End(xlUp).Row
Dim isFound As Boolean
Dim lastRow As Long
Dim i, j As Long
lastRow = lngRow1
For i = 1 To lngRow2
isFound = False
For j = 1 To lngRow1
If Sheets("Sheet1").Range("A" & i) = Sheets("Sheet2").Range("A" & j) Then
Sheets("Sheet1").Range("B" & i) = Sheets("Sheet2").Range("B" & j)
Sheets("Sheet1").Range("C" & i) = Sheets("Sheet2").Range("C" & j)
isFound = True
End If
Next j
If Not isFound Then
lastRow = lastRow + 1
Sheets("Sheet1").Range("A" & lastRow) = Sheets("Sheet2").Range("A" & i)
Sheets("Sheet1").Range("B" & lastRow) = Sheets("Sheet2").Range("B" & i)
Sheets("Sheet1").Range("C" & lastRow) = Sheets("Sheet2").Range("C" & i)
End If
Next i
Code written considering the above example image. if number of columns are different than the example please modify the code accordingly.

Excel VBA - Evaluate Function Returning #NAME? Error

So I wrote a VBA code to calculate the number of blanks, non-blanks and total entries under each header for some input data. I want to add a code that copies and pastes the values from one sheet to another, dedupes the values, gives me the unique list of values under each header, number of unique values, and the number of times those unique values are occurring under the header.
Blanks: I used the countblank function earlier, but it would skip certain empty fields, so I changed it to sumproduct(len(Range)=0)*1).
Non-Blanks: I wrote a similar function and tried to calculate the above.
It turns out VBA is unable to process the Sumproduct function. Here are the approaches I have tried:
1. Application.WorksheetFunction.Sumproduct(...)
2. ..Number.. = "=Sumproduct(...)"
3. ..Number.. = Evaluate("Sumproduct(...)")
4. ..Number.. = Worksheet.Evaluate("Sumproduct(...)")
Below is the code for the macro, I am writing the code on the Input_File, i.e., the Input worksheet.
Sub Dedupe()
ThisWorkbook.Worksheets("Control_Totals").Cells.ClearContents
Dim lRow As Long
Dim lCol As Long
Dim i As Long
Dim j As Long
Dim Input_File As Worksheet
Dim Output_File As Worksheet
Dim Dedup_File As Worksheet
Dim Col_Let As String
Dim Rng As String
Dim blank As String
Dim non_blank As String
Set Input_File = ThisWorkbook.Worksheets("Input")
Set Output_File = ThisWorkbook.Worksheets("Control_Totals")
Set Dedup_File = ThisWorkbook.Worksheets("Deduped")
With Output_File
.Cells(1, 1) = "Field_Name"
.Cells(1, 2) = "Blanks"
.Cells(1, 3) = "Non-Blanks"
.Cells(1, 4) = "Total"
End With
'Finding the last row among all entries, including the blank ones
lRow = Input_File.Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
MsgBox "Last Row: " & lRow
'Finding the last column header/field
lCol = Input_File.Cells.Find(What:="*", _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
MsgBox "Last Column: " & lCol
i = 1
'Finding the number of blank and non-blank entries for all the fields
Do While i < lCol + 1
Col_Let = ColumnLetter(i)
Rng = "Input!" & "Col_Let" & "2" & ":" & "lRow"
Output_File.Cells(i + 1, 1) = Input_File.Cells(1, i)
blank = "SumProduct((Len(Rng) = 0) * 1)"
non_blank = "SumProduct((Len(Rng) > 0) * 1)"
Output_File.Cells(i + 1, 2).Value = Evaluate(blank)
Output_File.Cells(i + 1, 3).Value = Evaluate(non_blank)
Output_File.Cells(i + 1, 4) = lRow - 1
'Deduping the data under the headers
j = 0
For j = 1 To lRow
Dedup_File.Cells(j, i).Value = Input_File.Cells(j, i).Value
j = j + 1
Next
Dedup_File.Range(Cells(1, i), Cells(lRow, i)).RemoveDuplicates Columns:=1, _
Header:=xlYes
i = i + 1
Loop
End Sub
These lines don't do what you think they do
Col_Let = ColumnLetter(i)
Rng = "Input!" & "Col_Let" & "2" & ":" & "lRow"
Rng is always a string containing "Input!Col_Let2:lRow"
What you meant was: (I think)
Rng = "Input!" & Col_Let & "2" & ":" & Col_Let & lRow
Secondly Rng exists only within this vba routine - it doesn't mean anything to Excel so you can't use it in an Excel Formula. You need
blank = "SumProduct((Len(" & Rng.address & ") = 0) * 1)"
and finally SumProduct doesn't like those sort of tricks in VBA (It relies on excel expanding the 1 into an array automatically). A better solution:
Dim cBlank as long
Dim cNonBlank as long
Dim r as range
For each r in rng
if r.text = "" then
cBlank= cBlank+1
else
cNonBlank = cNonBlank +1
end if
next r
I want to add a code that copies and pastes the values from one sheet to another, dedupes the values, gives me the unique list of values under each header, number of unique values, and the number of times those unique values are occurring under the header.
What you have just described there is a PivotTable, with the field of interest in both the Rows area and in the Values area as a Count.

Clear content of a range if cell contains a specific text

I want to make a macro that clears the content of the cells in the blue border (~40.000 Rows) when the cells in the red border (column AX) contain the text "NoBO" (=No Backorder) without losing the formulas in the columns AP:AX.
Sub clear_ranges()
Dim ws As Worksheet
Dim x As Integer
Dim clearRng As Range
Application.ScreenUpdating = False
Set ws = ThisWorkbook.Sheets("Input")
For x = 6 To ws.Range("B" & Rows.Count).End(xlUp).Row
If (ws.Range("AX6" & x).Value = "NoBO") Then
If clearRng Is Nothing Then
Set clearRng = ws.Range("B6" & x & ":" & "AN6" & x)
Else
Set clearRng = Application.Union(clearRng, ws.Range("B6" & x & ":" & "AN6" & x))
End If
End If
Next x
clearRng.Clear
End Sub
And for some reason:
For x = 6 To ws.Range("B" & Rows.Count).End(xlUp).Row
gives me a error "Overflow". After searching I know what this error means but I can't find a solution for this.
tl;dr - I want to delete the range B6:B##### (till last row) to AN6:AN####*(till last row) if cell AX##### containts NoBO
It is too easy to get an overflow using Integer. Replace:
Dim x As Integer
with:
Dim x As Long
Try:
Sub clear_ranges()
Dim ws As Worksheet
Dim x As Integer
Dim clearRng As Range
Application.ScreenUpdating = False
Set ws = ThisWorkbook.Sheets("Input")
For x = 6 To ws.Range("B" & Rows.Count).End(xlUp).Row
If ws.Range("AX" & x).Value = "NoBO" Then
ws.Range("B" & x & ":" & "AN" & x).Clear
End If
Next x
Application.ScreenUpdating = True
End Sub
I think the Union function can only store up to 30 ranges so it might not suit your needs.
Hi if you are Deleting Rows it's the Best to use a For Each Loop or start from the bottom of the column and work up.
'Loop through cells A6:Axxx and delete cells that contain an "x."
For Each c In Range("AX6:A" & ws.Range("B" & Rows.Count).End(xlUp).Row)
If c.Value2 = "NoBo" Then
Set clearRng = ws.Range("B" & c.Row & ":" & "AN" & c.Row)
End If
clearRng.Clear
Next
try this
Option Explicit
Sub clear_ranges()
Dim ws As Worksheet
Application.ScreenUpdating = False
Set ws = ThisWorkbook.Sheets("Input")
With ws
With .Range("B5:AX" & .Cells(.Rows.Count, "B").End(xlUp).Row) 'take all data
.AutoFilter Field:=49, Criteria1:="NoBO" 'filter to keep only rows with "NoBO" in column "AX" (which is the 49th column from column "B"
With .Offset(1).Resize(.Rows.Count - 1) 'offset from headers
If Application.WorksheetFunction.Subtotal(103, .Columns(1)) > 0 Then Intersect(.SpecialCells(xlCellTypeVisible), ws.Columns("B:AN")).ClearContents 'clear cells in columns "B:AN" of filtered rows
End With
.AutoFilter 'remove autofilter
End With
End With
Application.ScreenUpdating = True
End Sub
You can try
Assuming that there are no blank cells in AX Column
Sub clr_cell()
For i = 6 To ActiveSheet.Range("AX6", ActiveSheet.Range("AX6").End(xlDown)).Rows.Count
'counts the no. of rows in AX and loops through all
If ActiveSheet.Cells(i, 50).Value = "NoBo" Then
ActiveSheet.Range(Cells(i, 2), Cells(i, 40)).ClearContents
'clears range B to AN
End If
Next i
End Sub
I tested this on 40k rows and it worked fine. It takes a while to execute due to the no. of rows maybe.

Need to transpose rows while transferring data from one excel sheet to another

My original question was posted here.
Basically I needed some help transferring data from one sheet to another based on values in the first sheet. I am using a modified bit of code provided by user keong kenshih.
I added an additional check against another row to the IF statement, and I have this for my code:
Option Explicit
Dim MyWorkbook As Workbook
Dim MyWorksheet As Worksheet
Dim MyOutputWorksheet As Worksheet
So I need to output only certain columns. Also I need them to import to certain rows and columns on the second sheet, the CONTRACT sheet. Column A on the MAIN sheet goes to column A starting at row 17 on the CONTRACT sheet. B to B , E to D, F to E, all starting at row 17 on the CONTRACT sheet.
Rows 17-42 on the CONTRACT sheet will contain data.
Sub PullData()
Set MyWorkbook = Workbooks(ActiveWorkbook.Name)
Set MyWorksheet = MyWorkbook.Sheets("MAIN")
Set MyOutputWorksheet = MyWorkbook.Sheets("CONTRACT")
Dim myValue As Long
Dim RowPointer As Long
For RowPointer = 6 To MyWorksheet.Cells(Rows.Count, "B").End(xlUp).Row
If MyWorksheet.Range("A" & RowPointer).V alue > 0 And
MyWorksheet.Range("A" & RowPointer).Value <> ""
MyWorksheet.Range("F" & RowPointer).Value > 0 And
MyWorksheet.Range("F" & RowPointer).Value <> ""Then
If MyOutputWorksheet.Cells(Rows.Count, "B").End(xlUp).Row > 15
Then
Exit Sub
End If
MyWorksheet.Range(("A" & RowPointer) & ":C" & RowPointer).Copy
Destination:=MyOutputWorksheet.Range("A" &
MyOutputWorksheet.Cells(Rows.Count, "B").End(xlUp).Row + 1)
End If
Next RowPointer
End Sub
Give this a try :
Sub PullData()
Dim wRow As Long, _
RowPointer As Long, _
MyWorkbook As Workbook, _
Ws As Worksheet, _
OutWs As Worksheet
Set MyWorkbook = Workbooks(ActiveWorkbook.Name)
Set Ws = MyWorkbook.Sheets("MAIN")
Set OutWs = MyWorkbook.Sheets("CONTRACT")
With Ws
For RowPointer = 6 To .Cells(.Rows.Count, "B").End(xlUp).Row
If .Range("A" & RowPointer).Value > 0 And _
.Range("A" & RowPointer).Value <> "" And _
.Range("F" & RowPointer).Value > 0 And _
.Range("F" & RowPointer).Value <> "" Then
'This line would get you out of the loop after the first copy because _
'You first paste on line 17 and then the below left part will be equal to 18
'If OutWs.Cells(OutWs.Rows.Count, "B").End(xlUp).Row > 15 Then Exit Sub
wRow = OutWs.Rows(OutWs.Rows.Count).End(xlUp).Row + 1
'Always start copy after (or at) line 17
If wRow <= 17 Then wRow = 17
'More efficient way to copy data between ranges
OutWs.Range("A" & wRow).Value = Ws.Range("A" & RowPointer)
OutWs.Range("B" & wRow).Value = Ws.Range("B" & RowPointer)
OutWs.Range("D" & wRow).Value = Ws.Range("E" & RowPointer)
OutWs.Range("E" & wRow).Value = Ws.Range("F" & RowPointer)
End If
Next RowPointer
End With
Set MyWorkbook = Nothing
Set Ws = Nothing
Set OutWs = Nothing
End Sub

How to get back formula after applied vlookup using vba?

I have vba script which apply vlookup from one excel file another file. Its work fine but the output excel file only contains output data but i need formula for that vlookup output because i need apply same thing next by using copy paste so i need a help to get formula also in output excel. This my vba script
Sub vloo()
Dim rng As Range
Dim user As String
Dim file As String
Dim n As Integer
Dim m As Integer
Dim c As Integer
Dim a As Variant
n = 1
m = 0
c = 2
file = "E:\output8.xls"
Workbooks.Add
ActiveWorkbook.SaveAs file
Set nb = Application.Workbooks.Open(file)
Set ns = nb.Worksheets("Sheet1")
'get workbook path
filename = Application.GetOpenFilename(FileFilter:="Excel Files (*.xlsx), *.xls", Title:="Please select a file")
'set our workbook and open it
Set wb = Application.Workbooks.Open(filename)
'set our worksheet
Set ws = wb.Worksheets("Sheet1")
'set the range for vlookup
Set rng = ws.Range("F:AI")
For y = 6 To 20
m = m + 1
user = MsgBox(ws.Cells(1, y), 4, "You Want This")
If user = 6 Then
ns.Cells(1, n) = ws.Cells(1, y)
ns.Cells(1, n).Interior.Color = vbYellow
n = n + 1
If m > 1 Then
ns.Cells(2, c).Value = Application.VLookup(ns.Cells(2, 2).Value, rng, m, False)
c = c + 1
End If
End If
Next
End Sub
After execute this script and went to output excel ns and press F2 in each entry but getting only data dont know why formula are not there so i need formula also.
Simply alter your script so that it writes the formula rather than the evaluation thereof:
Change this:
ns.Cells(2, c).Value = Application.VLookup(ns.Cells(2, 2).Value, rng, m, False)
To this (revised to fully qualify the range used in the VLookup's third argument):
ns.Cells(2, c).Formula = "=VLookup(" _
& ns.Cells(2, 2).Value & "," _
& "[" & rng.Parent.Parent.Name & "]'" & rng.Parent.Name & "'!" & rng.Address & "," _
& m & "," _
& "False)"
If you debug, assuming I did not make any typos, then the that should have the third argument like so:
[Mains_heet_for_SEA_October14.xlsx]'Sheet1'!$F$2:$M$2
Note this may slow down the macro since formulas may re-evaluate during runtime. There are ways around this using the Application.Calculation property if that becomes an issue.
UPDATE
You can verify this works in principle with a Debug statement. Put it in it's own procedure if you'd like, and test it out like so:
Sub foo()
Dim rng As Range
Dim m As Integer
m = 3
Set rng = Range("A1:D10")
Debug.Print "=VLookup(" _
& "some_value" & "," _
& "[" & rng.Parent.Parent.Name & "]'" & rng.Parent.Name & "'!" & rng.Address & "," _
& m & "," _
& "False)"
End Sub
Here is proof that this works: