VBA Help to find a column based on header value and cupy it to an other worksheet - excel

I have this basic code to find the needed coumns in a table and copy them to an other worksheet. My problem is that every time I want to modify it to not to copy&paste the header it returns error. This is my code:
Sub CopyColumns()
Dim wsSource, wsResult As Worksheet
Dim Name, UniqueId, OperatingStatus As Long
Set wsSource = ThisWorkbook.Sheets("Source")
Set wsResult = ThisWorkbook.Sheets("Result")
Name = wsSource.Rows(1).Find("#BASEDATA#name").Column
UniqueId = wsSource.Rows(1).Find("#BASEDATA#uniqueId").Column
OperatingStatus = wsSource.Rows(1).Find("#BASEDATA#operatingStatus").Column
If Name <> 0 Then
wsSource.Columns(Name).Copy Destination:=wsResult.Columns(3)
End If
If UniqueId <> 0 Then
wsSource.Columns(UniqueId).Copy Destination:=wsResult.Columns(4)
End If
If OperatingStatus <> 0 Then
wsSource.Columns(OperatingStatus).Copy Destination:=wsResult.Columns(1)
End If
End Sub
Any ideas how to solve it?
I tried is to copy like this using offset:
If targetColName <> 0 Then
wsSource.Columns(targetColName).Offset(1, 0).Resize(wsSource.Rows.Count - 1).Copy _ Destination:=wsResult.Columns(3).Offset(1, 0)
It gives Error: Application-defined ot object-defined error
Thanks!
offset and resize not working

You can break out the "copy column if found" into a separate sub:
Sub CopyColumns()
Dim wsSource, wsResult As Worksheet
Set wsSource = ThisWorkbook.Sheets("Source")
Set wsResult = ThisWorkbook.Sheets("Result")
CopyIfExists wsSource.Rows(1), "#BASEDATA#name", wsResult, 3
CopyIfExists wsSource.Rows(1), "#BASEDATA#uniqueId", wsResult, 4
CopyIfExists wsSource.Rows(1), "#BASEDATA#operatingStatus", wsResult, 1
End Sub
'Look for `colName` in `headerRow`, and if found copy the whole
' column to column `destColNum` on `destSheet`
Sub CopyIfExists(headerRow As Range, colName As String, destSheet As Worksheet, destColNum As Long)
Dim f As Range
Set f = headerRow.Find(what:=colName, lookat:=xlWhole) 'or xlPart
If Not f Is Nothing Then
f.EntireColumn.Copy destSheet.Cells(1, destColNum)
End If
End Sub
When using find, you should check you got a match before trying to do anything with the matched cell.

Copy Columns
Option Explicit
Sub CopyColumnsToResult()
Dim sColNames(): sColNames = Array("#BASEDATA#name", _
"#BASEDATA#uniqueId", "#BASEDATA#operatingStatus")
Dim dCols(): dCols = Array(3, 4, 1)
Dim sws As Worksheet: Set sws = ThisWorkbook.Sheets("Source")
Dim shrg As Range: Set shrg = sws.Rows(1)
Dim slCell As Range: Set slCell = shrg.Cells(shrg.Cells.Count) ' last cell
Dim dws As Worksheet: Set dws = ThisWorkbook.Sheets("Result")
Dim shCell As Range, c As Long
For c = LBound(sColNames) To UBound(sColNames)
Set shCell = shrg.Find(sColNames(c), slCell, xlFormulas, xlWhole)
If Not shCell Is Nothing Then ' header cell found
sws.Columns(shCell.Column).Copy dws.Columns(dCols(c))
End If
Next c
MsgBox "Columns copied.", vbInformation
End Sub

Related

How do I get it to select a single row based on the value?

I'm working on simplifying an excel worksheet and I want information in the rows to be transferred based on the value. If the value = "done", I want it transferred to Carc. If the value = "On-going", I want it transferred to Ccon (haven't typed this up yet). This have been written-up in VBA but I'm open to trying other things if it would make things easier.
Main thing is that I'm trying to find a way to make the code already made, simpler and more practical. Only thing I haven't figured out is how to have it select 1 individual row, instead of all rows.
Sub MoveBasedOnValue2()
Dim TakeCell As Range
Dim DestCell As Range
Dim Status As Range
Dim Cjob As Worksheet
Dim CArc As Worksheet
Dim Contact As Range, Subject As Range, JobNo As Range, QuoteNo As Range
Dim Dateofcommision As Range, Ddate As Range
Set Cjob = Sheet4
Set CArc = Sheet1
If Cjob.Range("G2") = "Done" Then
Set Contact = Cjob.Range("A2")
Set Subject = Cjob.Range("B2")
Set QuoteNo = Cjob.Range("C2")
Set JobNo = Cjob.Range("D2")
Set Dateofcommision = Cjob.Range("E2")
Set Ddate = Cjob.Range("F2")
Status.Select
Contact.Select
Subject.Select
QuoteNo.Select
JobNo.Select
Dateofcommision.Select
Ddate.Select
If CArc.Range("A2") = "" Then
Set DestCell = CArc.Range("A2")
Else
Set DestCell = CArc.Range("A1").End(xlDown).Offset(1, 0)
End If
Contact.Copy DestCell
Subject.Copy DestCell.Offset(0, 1)
QuoteNo.Copy DestCell.Offset(0, 2)
JobNo.Copy DestCell.Offset(0, 3)
Dateofcommision.Copy DestCell.Offset(0, 4)
Ddate.Copy DestCell.Offset(0, 5)
Status.ClearContents
Contact.ClearContents
Subject.ClearContents
QuoteNo.ClearContents
JobNo.ClearContents
Dateofcommision.ClearContents
Ddate.ClearContents
End If
You can do something like this:
Sub MoveBasedOnValue2()
Dim cStatus As Range, wsDest As Worksheet
Set cStatus = Sheet4.Range("G2") 'first cell to check status
Do While Len(cStatus.Value) > 0
Select Case LCase(cStatus.Value)
Case "done": Set wsDest = Sheet1
Case "on-going": Set wsDest = Sheet2 'for example
Case Else: Set wsDest = Nothing 'no move to make
End Select
If Not wsDest Is Nothing Then 'got a destination sheet?
'here Range("A1:F2") is *relative* to the whole row...
cStatus.EntireRow.Range("A1:F2").Cut _
Destination:=wsDest.Cells(Rows.Count, "A").End(xlUp).Offset(1)
End If
Set cStatus = cStatus.Offset(1, 0) 'next source row
Loop
End Sub

Autofilter VBA, How i can check if the criteria doesn't exist?

I have 2 sheets is the CriteriaSheet and DataSheet that need to filter, which use the data in CriteriaSheet to fill in the Criterial argument of AutoFilter for filter data in DataSheet.
DataSheet
CriteriaSheet
My problem is, I want to know which Criteria is cannot find in the DataSheet. How do I know if the "Ant"(Criteria) doesn't exist in the DataSheet?
Function Filter_Function()
Dim Data_sh As Worksheet
Dim Filter_Criteria_Sh As Worksheet
Dim Output_sh As Worksheet
Set Data_sh = ThisWorkbook.Sheets("DataSheet")
Set Filter_Criteria_Sh = ThisWorkbook.Sheets("CriteriaSheet")
Set Output_sh = ThisWorkbook.Sheets("Output")
Output_sh.UsedRange.Clear
Data_sh.AutoFilterMode = False
Dim Emp_list() As String
Dim n, i As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria_Sh.Range("A:A")) - 2
ReDim Emp_list(n) As String
Dim R As String
For i = 0 To n
Emp_list(i) = Filter_Criteria_Sh.Range("A" & i + 2)
Next i
With Range("A1")
''get range before filter
Dim rngBefore As Range
Set rngBefore = Range(.Offset(1, 0), .Offset(1, 0).End(xlDown))
''filter
Data_sh.UsedRange.AutoFilter 2, Emp_list(), xlFilterValues
''get range after filter
On Error Resume Next
Dim rngAfter As Range
Set rngAfter = rngBefore.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
''check whether any cells matching criteria were found
If rngAfter Is Nothing Then
MsgBox "No found " & Emp_list()
'Exit Function
Else
Data_sh.UsedRange.Copy Output_sh.Range("A1")
End If
End With
Data_sh.AutoFilterMode = False
End Function
Could you please suggest? Thanks

Insert new row under a specific text using vba

I am trying to insert new row under a particular text(selected from userform) with the new text but getting an error "Object variable or With block variable not set" in the line "fvalue.Value = Me.txtremark.Value".
Please help me to find where exactly the mistake I did in the Code. I was trying to find many ways but failed.
Excel Table:
Required Output:
Private Sub cmdadd_Click()
Dim fvalue As Range
Dim wks As Worksheet
Set wks = ThisWorkbook.Worksheets("Sheet1")
wks.Activate
Set fvalue = wks.Range("B:B").Find(What:=Me.txtremark.Value, LookIn:=xlFormulas, LookAt:=xlWhole)
fvalue.Value = Me.txtremark.Value
fvalue.Offset(1).EntireRow.Insert Shift:=xlDown
fvalue.Offset(0, 1).Value = Me.txtplace.Value
End Sub
Try:
Option Explicit
Sub test()
Dim Position As Range, rngToSearch As Range
Dim strToFound As String
'Change strToFound value
strToFound = "Test"
With ThisWorkbook.Worksheets("Sheet1")
Set rngToSearch = .Range("B:B")
Set Position = rngToSearch.Find(strToFound, LookIn:=xlValues, Lookat:=xlWhole)
If Not Position Is Nothing Then
Debug.Print Position.Row
.Rows(Position.Row).Offset(1).EntireRow.Insert
End If
End With
End Sub
Try using a separate variable to pass values to the worksheet, or just refer to the textbox.
Additionally, activating (and selecting) is not necessary and will hurt your macro's speed and is prone to errors.
Option Explicit
Private Sub cmdadd_Click()
Dim fvalue As Range
Dim wks As Worksheet
Set wks = ThisWorkbook.Worksheets("Sheet1")
Set fvalue = wks.Range("B:B").Find(What:=Me.txtremark.Value, LookIn:=xlFormulas, LookAt:=xlWhole)
If Not fvalue Is Nothing Then
wks.Rows(fvalue.Row + 1).EntireRow.Insert
wks.Cells(fvalue.Row + 1, fvalue.Column + 1).Value = Me.txtremark.Value
End If
End Sub
I have taken the liberty to check if the value is found in the first place

How to loop through several regions of a worksheet?

I'm looking for some VBA that will allow me to loop through several different REGIONS on a worksheet. Not individual cells, necessarily, but to jump from "currentregion" to the next "currentregion". And once the region is located, it should be selected and copied.
I've tried setting a StartCell (via Cells.Find(What:="*") and then using that cell to select the corresponding 'currentregion'. The issue is how to move to the next 'currentregion', until all 'currentregions' on the worksheet have been copied/pasted.
My results are inconsistent so far, where sometimes all the necessary regions are copied/pasted, but other times some of the regions are ignored (same exact worksheet, same exact data).
Set StartCell = Cells.Find(What:="*", _
After:=Cells(Rows.Count, Columns.Count), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)Do
'Select Range and copy it
If StartCell <> "" Then
StartCell.currentregion.CopyPicture
'Select a cell to paste the picture in
Range("A16").PasteSpecial
'Move to next range to be copied
Set StartCell = StartCell.End(xlToRight).End(xlToRight)
StartCell.Select
End If
Loop Until StartCell = ""
Something like that should work
Option Explicit
Public Sub ProcessEachRegion()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1") 'define your sheet
Dim StartCell As Range
Set StartCell = ws.Range("A1") 'define start cell
Do Until StartCell.Column = ws.Columns.Count 'loop until end of columns
With StartCell.CurrentRegion
'do all your copy stuff here!
'.Copy
'Destination.Paste
Set StartCell = .Resize(1, 1).Offset(ColumnOffset:=.Columns.Count - 1).End(xlToRight)
End With
Loop
End Sub
It looks for the next region right to the previous one (regions 1 to 5 in the example below).
The main sub (I named it tgr) will call a function named GetAllPopulatedCells which defines a range for all populated cells in a worksheet. The .Areas property will let your loop through each region. It will then copy each Area/Region as a picture (still not sure why you want this) and put it in the destination cell, and then adjust the destination cell as needed so that all of the pasted images are stacked on top of each other.
Sub tgr()
Dim ws As Worksheet
Dim rAllRegions As Range
Dim rRegion As Range
Dim rDest As Range
Set ws = ActiveWorkbook.ActiveSheet
Set rAllRegions = GetAllPopulatedCells(ws)
Set rDest = ws.Range("A16")
If rAllRegions Is Nothing Then
MsgBox "No populated cells found in '" & ws.Name & "'. Exiting Macro.", , "Error"
Exit Sub
End If
For Each rRegion In rAllRegions.Areas
rRegion.CopyPicture
rDest.PasteSpecial
Set rDest = rDest.Offset(rRegion.Rows.Count)
Next rRegion
End Sub
Public Function GetAllPopulatedCells(Optional ByRef arg_ws As Worksheet) As Range
Dim ws As Worksheet
Dim rConstants As Range
Dim rFormulas As Range
If arg_ws Is Nothing Then Set ws = ActiveWorkbook.ActiveSheet Else Set ws = arg_ws
On Error Resume Next
Set rConstants = ws.Cells.SpecialCells(xlCellTypeConstants)
Set rFormulas = ws.Cells.SpecialCells(xlCellTypeFormulas)
On Error GoTo 0
Select Case Abs(rConstants Is Nothing) + 2 * Abs(rFormulas Is Nothing)
Case 0: Set GetAllPopulatedCells = Union(rConstants, rFormulas)
Case 1: Set GetAllPopulatedCells = rFormulas
Case 2: Set GetAllPopulatedCells = rConstants
Case 3: Set GetAllPopulatedCells = Nothing
End Select
Set ws = Nothing
Set rConstants = Nothing
Set rFormulas = Nothing
End Function

VBA in Excel: Runtime Error 1004

I am trying to do the following. I have several spreadsheets that are named something like "ITT_198763" where the ITT part stays the same but the number changes. I also have one tab called program where the 6 digit number is imported on row 40 (hence the RngToSearch below). I need the program to 1) find the "ITT" sheet for a certain 6 digit number, 2) identify the corresponding row in the "Program" tab, and copy information from the "ITT" tab to row 41 of the identified column. I will be copying more information from the ITT sheet to the specified column, but for now I am just trying to get it to work once.
From the MsgBox, I know it identifies the correct prjNumber (the 6 digit number), but I get the runtime 1004 error on the line Set RngDest. Any help will be appreciated!
Sub Summary_Table()
Dim wks As Worksheet
Dim RngToSearch As Range, RngDest As Range
Dim foundColumn As Variant
Dim prjNumber
For Each wks In ActiveWorkbook.Worksheets
If ((Left(wks.Name, 3) = "ITT")) Then
prjNumber = Right(wks.Name, 6)
MsgBox (prjNumber)
Set RngToSearch = Sheets("Program").Range("C40:q40")
foundColumn = Sheets("Program").Application.Match(prjNumber, RngToSearch, False)
With Sheets("Program")
Set RngDest = .Range(1, foundColumn) 'Project Name
End With
If Not IsError(foundColumn) Then
wks.Range("E2").Copy RngDest
End If
End If
Next wks
End Sub
I tried the .cell instead with the following code (all else is the same) and now get runtime error 13 on the Set RngDest line:
Set RngToSearch = Sheets("Program").Range("C40:q48")
foundColumn = Sheets("Program").Application.Match(prjNumber, RngToSearch.Rows(1), False)
With Sheets("Program")
Set RngDest = RngToSearch.Cells(1, foundColumn) 'Project Name
End With
Yuo are getting that error because foundColumn has an invalid value. Step through the code and see what is the value of foundColumn
Here is an example which works.
Sub Sample()
Dim RngDest As Range, RngToSearch As Range
foundColumn = 1
Set RngToSearch = Sheets("Program").Range("C40:q40")
Set RngDest = RngToSearch.Cells(1, foundColumn)
Debug.Print RngDest.Address
End Sub
Add MsgBox foundColumn before the line Set RngDest = RngToSearch.Cells(1, foundColumn) and see what value do you get. I guess the line
foundColumn = Sheets("Program").Application.Match(prjNumber, RngToSearch, False)
is not giving you the desired value. Here is the way to reproduce the error.
EDIT (Solution)
You need to handle the situation when no match is found. Try something like this
Sub Sample()
Dim RngDest As Range, RngToSearch As Range
Set RngToSearch = Sheets("Program").Range("C40:q40")
foundcolumn = Sheets("Program").Application.Match(1, RngToSearch, False)
If CVErr(foundcolumn) = CVErr(2042) Then
MsgBox "Match Not Found"
Else
Set RngDest = RngToSearch.Cells(1, foundcolumn)
'
'~~> Rest of the code
'
End If
End Sub
You are looking for the Cells function, which has the prototype .Cells([RowIndex], [ColumnIndex]). The Range function takes either a string with a range name (like "A1", or a named range), or other range references.
I figured it out! Found column was the problem. Combining that with the help from the other commenters, the following works:
Sub Summary_Table()
Dim wks As Worksheet
Dim RngToSearch As Range, RngDest As Range
Dim foundColumn As Variant
Dim prjNumber
For Each wks In ActiveWorkbook.Worksheets
If ((Left(wks.Name, 3) = "ITT")) Then
prjNumber = Right(wks.Name, 6)
MsgBox (prjNumber)
Set RngToSearch = Sheets("Program").Range("a40:q48")
foundColumn = Sheets("Program").Rows(40).Find(what:=prjNumber, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False).Column
MsgBox (foundColumn)
With Sheets("Program")
Set RngDest = RngToSearch.Cells(2, foundColumn) 'Project Name
Debug.Print RngDest.Address
End With
If Not IsError(foundColumn) Then
wks.Range("E3").Copy RngDest
End If
End If
Next wks
End Sub

Resources