Search multiple headings(Columns) in other sheet, copy data and paste in main file - excel

I need a VBA code for a button which when clicked browse for other excel file, search for specific sheet named “Farmer History” in it. In this sheet it looks for A1 complete row and search heading “Crop Area” and copy this column data to main file(where button embedded) in sheet named “Berkhund” at F Column below last cell is used.
The same to be done for other 2 columns too i.e
Looks for “Target Qty” in first row at same sheet “Farmer History” and paste in main file sheet “Berkhund” at R Column below last cell is used
Looks for “Commulative Sold” in first row at same sheet “Farmer History” and paste in main file sheet “Berkhund” at S Column below last cell is used.Code which i tried is given below but it cannot BROWSE for file, search and paste back in main file:
Sub copycroparea()
Dim sh As Worksheet, fn As Range
Set sh = Sheets("Farmer History")
Set fn = sh.Rows(1).Find(" Crop Area", , xlValues, xlWhole)
If Not fn Is Nothing Then
fn.Offset(1).Resize(sh.Cells(Rows.Count, fn.Column).End(xlUp).Row, 1).Copy
Sheets("Berkhund").Range("F13")
Else
MsgBox "Crop area Not Found!"
Exit Sub
End If
End Sub

Define an array with the 3 search terms and target coumns and use them in a loop.
Option Explicit
Sub copycroparea()
Const RESULT = "Sheet2" '"Berkhund"
Const SOURCE = "Farmer History"
Dim term(3) As Variant
term(1) = Array("Crop Area", 6) 'F
term(2) = Array("Target Qty", 18) 'R
term(3) = Array("Commulative Sold", 19) 'S
Dim wb As Workbook, ws As Worksheet
Dim wbSearch As Workbook, wsSearch As Worksheet
Dim iTargetRow As Long, iLastRow As Long, sFilename As String
' search for file
sFilename = Application.GetOpenFilename("Excel Files (*.xlsx; *.xlsm),*.xlsx;*.xlsm")
If Len(sFilename) = 0 Or sFilename = "False" Then
MsgBox "No file selected ", vbCritical
End If
'Debug.Print sFilename
Set wb = ThisWorkbook
Set ws = wb.Sheets(RESULT)
Set wbSearch = Workbooks.Open(sFilename, False, True) ' no links update, read only
Set wsSearch = wbSearch.Sheets(SOURCE)
Dim i As Integer, sTerm As String, iCol As Integer, msg As String
Dim rng As Range, rngTarget As Range
For i = 1 To UBound(term)
sTerm = term(i)(0)
iCol = term(i)(1)
'Debug.Print i, sTerm, iCol
Set rng = wsSearch.Rows(1).Find(sTerm, , xlValues, xlPart)
If Not rng Is Nothing Then
' Destination for copy on main file
Set rngTarget = ws.Cells(Rows.Count, iCol).End(xlUp).Offset(1, 0)
' find extent of data
iLastRow = wsSearch.Cells(Rows.Count, rng.Column).End(xlUp).Row
'Debug.Print rngTarget.Address, iLastRow
' copy
rng.Offset(1, 0).Resize(iLastRow, 1).Copy rngTarget
msg = msg & sTerm & " found at " & rng.Address & vbCr
Else
msg = msg & sTerm & "not found" & vbCr
End If
Next
wbSearch.Close False
MsgBox msg, vbInformation
End Sub

Related

Every Time Copy From ActiveSheet Only

i have used this vba in my workbook
To copy entire row data based on given value from inputbox from active sheet to another existing sheet by given value in first inputbox of this strName = Trim(InputBox("What are you looking for?")) and second inputbox will ask to enter Sheet Name Set OutputWs = Worksheets(InputBox("Enter Sheet Name")) and third inputbox will ask to enter column name OutputWs.Cells(Rows.count, InputBox("Enter Column Name")).End(xlUp).Row and then matched given value data from that column of entire row should be copy from active sheet to another sheet given sheet name by inputbox in workbook & here it’s successfully copying
Here what the problem i have getting when i using this vba it’s copying matched data by given value from another sheets also
Here what i want this vba will copy matched data by given value from active sheet only not from another sheets & if I copy matched data by given value from another sheet i mean another active sheet to same OutputWs Sheet then first copied data should not be over written it will look for next blank cell or row to copy the data from there
And one more thing when i enter new sheet name in inputbox then i have getting this Error
Run-time error ‘9’:
Subscript out of range
On this line
Set OutputWs = Worksheets(InputBox("Enter Sheet Name"))
Sub SearchAll()
Dim ws As Worksheet, OutputWs As Worksheet
Dim rFound As Range, IsValueFound As Boolean, IsValueNotFound As Boolean
Dim strName As String
Dim count As Long, LastRow As Long
strName = Trim(InputBox("What are you looking for?"))
If strName = "" Then Exit Sub
Set OutputWs = Worksheets(InputBox("Enter Sheet Name"))
LastRow = OutputWs.Cells(Rows.count, InputBox("Enter Column Name")).End(xlUp).Row
For Each ws In Worksheets
If ws.Name <> OutputWs.Name Then
Debug.Print "Checking " & ws.Name
Set rFound = FindAll(ws.UsedRange, strName)
If Not rFound Is Nothing Then
Set rFound = rFound.EntireRow
count = rFound.Cells.count / Columns.count 'how many matched rows?
Debug.Print "Found " & count & " rows"
rFound.Copy OutputWs.Cells(LastRow + 1, 1)
LastRow = LastRow + count
IsValueFound = True
IsValueNotFound = True
End If
End If
Next ws
If IsValueFound Then
OutputWs.Select
MsgBox "Results pasted to " & "(" & OutputWs.Name & ")" & " Sheet"
Else
If IsValueNotFound Then
OutputWs = MsgBox("Sheet " & OutputWs.Name & " Not Found in WorkBook Do you want Create a New Sheet with Given Name Then Click Yes", vbQuestion + vbYesNo)
If OutputWs = vbYes Then
Worksheets.Add OutputWs.Name
End If
End If
End If
End Sub
Public Function FindAll(rng As Range, val As String) As Range
Dim rv As Range, f As Range
Dim addr As String
Set f = rng.Find(what:=val, After:=rng.Cells(rng.Cells.count), _
LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False)
If Not f Is Nothing Then addr = f.Address()
Do Until f Is Nothing
If rv Is Nothing Then
Set rv = f
Else
Set rv = Application.Union(rv, f)
End If
Set f = rng.FindNext(After:=f)
If f.Address() = addr Then Exit Do
Loop
Set FindAll = rv
End Function

VBA Create table for each filter data in another sheet

I need to make a table for each unique value of a column. I used autofilter to select each filter to then copy and paste to another sheet. Due to the amount of data (large) i would like to automate and maybe do a for each cycle where each filter is select individually and copied to a differente sheet. It´s this even possible? Does anyone knows how to maybe simplify this problem ?
Option Explicit
Sub CreateTables()
Const COL_FILTER = 1 ' A
Const SHT_NAME = "Sheet1" ' data sheet
Dim wb As Workbook, ws As Worksheet
Dim rng As Range, iLastRow As Long, i As Long
Set wb = ThisWorkbook
Set ws = wb.Sheets(SHT_NAME)
' get list as unique values
Dim dict, key, ar
Set dict = CreateObject("Scripting.Dictionary")
iLastRow = ws.Cells(Rows.Count, COL_FILTER).End(xlUp).Row
ar = ws.Cells(1, COL_FILTER).Resize(iLastRow, 1)
For i = 2 To iLastRow
dict(ar(i, 1)) = 1
Next
' confirm
If MsgBox(dict.Count & " sheets will be created," & _
" continue ? ", vbYesNo) = vbNo Then
Exit Sub
End If
' apply autofilter in turn
' copy to new sheet
Set rng = ws.UsedRange
ws.AutoFilterMode = False
For Each key In dict
With wb.Sheets.Add(After:=wb.Sheets(wb.Sheets.Count))
.Name = CStr(key)
rng.AutoFilter COL_FILTER, CStr(key)
rng.SpecialCells(xlCellTypeVisible).Copy .Range("A1")
.ListObjects.Add(xlSrcRange, .UsedRange, , xlYes) _
.Name = "Table " & key
End With
MsgBox "Created sheet " & key
Next
MsgBox dict.Count & " sheets created"
End Sub

Copy columns between worksheets using arrays

I want to copy some columns in the active sheet to a workbook which is opened during run time. The attached code is working well, EXCEPT that I want to READ the column headers in the source sheet rather than have them be hard-defined, because they might not be always the same. The lines I want to transfer start at row 2 on the source and should be pasted also on row 2 on the paste file. Thank you!
Here is the code:
Option Explicit
Sub CopyPvtToTemplate()
' Copies the columns in the Source file and pastes them into template
' The Source file is the Active Sheet
Const LastRowColumnS As Long = 2
Const FirstRowS = 2
Const FirstRowP = 2
Dim HeadSource As Variant
Dim HeadPaste As Variant
Dim LastRow As Long
HeadSource = Array("Header Column I", "Header Column E", "Header Column F", "Header Column G", "Header Column H", "Header Column B", "Header Column J")
HeadPaste = Array("Header Column A", "Header Column B", "Header Column C", "Header Column D", "Header Column E", "Header Column F", "Header Column H")
Dim rng As Range
Dim PasteFile As Variant
Dim wsS As Worksheet
Dim wsP As Worksheet
Dim CurColS As Long
Dim CurColP As Long
Dim NumberOfRows As Long
Dim Count As Long
Dim i As Long
' Define Source Worksheet and Last Row in Source file
Set wsS = ActiveSheet
With ActiveSheet
LastRow = .Range("B" & .Rows.Count).End(xlUp).Row
End With
' Open Paste file
Set PasteFile = Workbooks.Open("C:\Users\ etc.xlsx")
' Define Source Worksheet
Set wsP = PasteFile.Worksheets(2)
' Define last cell with data in Last Row Column of Source Sheet
Set rng = wsS.Columns(LastRowColumnS).Find(what:="*", LookIn:=xlFormulas, Searchdirection:=xlPrevious)
If rng Is Nothing Then
MsgBox "No data in column"
Exit Sub
End If
NumberOfRows = rng.Row - FirstRowS + 1
For i = 0 To UBound(HeadSource)
' Define column of current header in Source Sheet
Set rng = wsS.Cells.Find(what:=HeadSource(i), after:=wsS.Cells(wsS.Rows.Count, wsS.Columns.Count), _
LookIn:=xlFormulas, lookat:=xlWhole, searchorder:=xlByRows)
If Not rng Is Nothing Then
CurColS = rng.Column
' Define column of Current Header in Paste sheet
Set rng = wsP.Cells.Find(what:=HeadPaste(i), after:=wsP.Cells(wsP.Rows.Count, wsP.Columns.Count), _
LookIn:=xlFormulas, lookat:=xlWhole, searchorder:=xlByRows)
If Not rng Is Nothing Then
CurColP = rng.Column
' Write data from Source Sheet to Paste Sheet
wsP.Cells(FirstRowP, CurColP).Resize(NumberOfRows).Value = _
wsS.Cells(FirstRowS, CurColS).Resize(NumberOfRows).Value
' Count the transfer
Count = Count + 1
End If
End If
Next i
MsgBox "Transferred data from '" & Count & "'Columns."
End Sub
I think you're setting yourself up for potential errors by taking this approach - i.e. if you don't type the exact text of the column headings / number of headings within your code etc. Also, using ActiveSheet can be fraught with danger. Having said that, the code below should give you what you want - just change the names to the actual headers, as well as the target file. Let me know how you go.
EDIT
Code edited following clarification from OP.
Option Explicit
Sub CopyPvtToTemplate()
On Error GoTo GetOut
Application.EnableEvents = False
Dim LastRow As Long
Dim wb As Workbook, wsS As Worksheet, wsP As Worksheet
Dim sArray, pArray, i As Integer, j As Integer
Dim Scol As Integer, Pcol As Integer
Set wsS = ActiveSheet
Set wb = Workbooks.Open(ThisWorkbook.Path & "\etc.xlsx") '<~~ change to suit
Set wsP = wb.Sheets(2)
LastRow = wsS.Cells(Rows.Count, 2).End(xlUp).Row
sArray = Array(9, 5, 6, 7, 8, 2, 10)
pArray = Array(1, 2, 3, 4, 5, 6, 8)
For i = 0 To UBound(sArray)
Scol = sArray(i)
For j = 0 To UBound(pArray)
Pcol = pArray(i)
wsS.Range(wsS.Cells(2, Scol), wsS.Cells(LastRow, Scol)).Copy wsP.Cells(2, Pcol)
Next j
Next i
MsgBox "Transferred data from " & i & " columns"
Continue:
Application.EnableEvents = True
Exit Sub
GetOut:
MsgBox Err.Description
Resume Continue
End Sub

Excel VBA Multiple Sheet Search using Data from one Column

I am trying to search for values listed in a column from multiple sheets in my excel workbook. If excel finds a match I would like it to return sheet names of the tabs that had the value.
Here is what i have done so far. I decided to start off by using one keyword to search multiple tabs, copy and paste the sheet name. The code below only paste the first resulting sheet name when there are other sheets containing the same keyword. I would like to know how i can pull the other sheet names that contain the same keyword.
I would also like to know how i can set up the keyword to use information in Column A of the Field List.
Sub FinalAppendVar()
Dim ws As Worksheet
Dim arr() As String
Keyword = "adj_veh_smart_tech_disc"
Totalsheets = Worksheets.Count
For i = 1 To Totalsheets
If Worksheets(i).Name <> "Main" Or InStr(1, Worksheets(i).Name, " Checks") Or Worksheets(i).Name
<>_ "Field Lists" Then
lastrow = Worksheets(i).Cells(Rows.Count, 4).End(xlUp).Row
For j = 2 To lastrow
If Worksheets(i).Cells(1, 3).Value = Keyword Then
Worksheets("Field Lists").Activate
lastrow = Worksheets("Field Lists").Cells(Rows.Count, 4).End(xlUp).Row
Worksheets("Field Lists").Cells(lastrow + 1, 5).Value = Worksheets(i).Name
Worksheets("Field Lists").Cells(lastrow + 2, 5).Value = Worksheets(i).Name
End If
Next
End If
Next
End Sub
The following code should work for what you described.
A couple feedback items:
Tabbing out loops and if statements significantly improves code readability
Never reuse variable names (i.e. lastrow), it makes it hard to read and can cause issues that are difficult to find later on
Follow all Next with the loop variable (i.e. Next i), this improves readability and helps you keep track of the ends of loops
.Activate and .Select are generally never required in vba, its better to be explicit in what you are referencing
Sub FinalAppendVar()
Dim searchSheet As Excel.Worksheet
Dim pasteSheet As Excel.Worksheet
Dim keyword As String
Dim lastSearchRow As Integer
Dim lastPasteRow As Integer
' set the worksheet to paste to
Set pasteSheet = ThisWorkbook.Worksheets("Field Lists")
' set keyword to look for
keyword = "adj_veh_smart_tech_disc" '<-- manual entry
'keyword = pasteSheet.Range("A1").Value '<-- use value in cell A1 on the defined pasteSheet
' loop through all sheets in the workbook
For i = 1 To ThisWorkbook.Worksheets.Count
' set the current worksheet we are looking at
Set searchSheet = ThisWorkbook.Worksheets(i)
' check if the current sheet is one we want to search in
If searchSheet.Name <> "Main" Or InStr(1, searchSheet.Name, " Checks") Or searchSheet.Name <> "Field Lists" Then
' current worksheet is one we want to search in
' find the last row of data in column D of the current sheet
lastSearchRow = searchSheet.Cells(1048576, 4).End(xlUp).Row
' loop through all rows of the current sheet, looking for the keyword
For j = 2 To lastSearchRow
If searchSheet.Cells(j, 3).Value = keyword Then
' found the keyword in row j of column C in the current sheet
' find the last row of column D in the paste sheet
'lastPasteRow = pasteSheet.Cells(1048576, 4).End(xlUp).Row
lastPasteRow = pasteSheet.Cells(1048576, 5).End(xlUp).Row '<-- update based on OPs comment
' paste the name of the current search sheet to the last empty cell in column E
pasteSheet.Cells(lastPasteRow + 1, 5).Value = searchSheet.Name
' not sure if the next line is needed, looks like it pastes again immediately below the previous
pasteSheet.Cells(lastPasteRow + 2, 5).Value = searchSheet.Name
' to save time consider exiting the search in the current sheet since the keyword was just found
' this will move to the next sheet immediately and not loop through the rest of the rows on the current
' search sheet. This may not align with the usecase so it is currently commented out.
'Exit For '<--uncomment this to move to the next sheet after finding the first instance of the keyword
Else
' the keyoword was not in row j of column C
' do nothing
End If
Next j
Else
' current sheet is one we don't want to search in
' do nothing
End If
Next i
End Sub
Please try this variant (Don't worry that the code is so long - the longer the programmer thought and the more wrote, the better the program works ... usually it is):
Option Explicit
Sub collectLinks()
Const LIST_SHEET_NAME As String = "Field Lists"
Dim wsTarget As Worksheet
Dim wsEach As Worksheet
Dim keywordCell As Range
Dim sKeyword As String
Dim linkCell As Range
Dim aFound As Range
Dim aCell As Range
On Error Resume Next
Set wsTarget = ActiveWorkbook.Worksheets(LIST_SHEET_NAME)
On Error GoTo 0
If wsTarget Is Nothing Then
MsgBox "'" & LIST_SHEET_NAME & "' not exists in active workbook", vbCritical, "Wrong book or sheet name"
Exit Sub
End If
Rem Clear all previous results (from column B to end of data)
wsTarget.UsedRange.Offset(0, 1).ClearContents
Rem Repeat for each cell of column A in UsedRange:
For Each keywordCell In Application.Intersect(wsTarget.UsedRange, wsTarget.Columns("A")) ' It can be changed to "D", "AZ" or any other column
sKeyword = keywordCell.Text
If Trim(sKeyword) <> vbNullString Then
Application.StatusBar = "Processed '" & sKeyword & "'"
Set linkCell = keywordCell
For Each wsEach In ActiveWorkbook.Worksheets
If wsEach.Name <> LIST_SHEET_NAME Then
Application.StatusBar = "Processed '" & sKeyword & "' Search in '" & wsEach.Name & "'"
Set aFound = FindAll(wsEach.UsedRange, sKeyword)
If Not aFound Is Nothing Then
For Each aCell In aFound
Set linkCell = linkCell.Offset(0, 1) ' Shift to rught, to the next column
linkCell.Formula2 = "=HYPERLINK(""#" & aCell.Address(False, False, xlA1, True) & """,""" & _
aCell.Worksheet.Name & " in cell " & aCell.Address(False, False, xlA1, False) & """)"
Next aCell
End If
End If
Next wsEach
End If
Next keywordCell
Application.StatusBar = False
Rem Column width
wsTarget.UsedRange.Columns.AutoFit
End Sub
Function FindAll(SearchRange As Range, FindWhat As Variant) As Range
Dim FoundCell As Range
Dim FirstFound As Range
Dim LastCell As Range
Dim ResultRange As Range
Dim Area As Range
Dim MaxRow As Long
Dim MaxCol As Long
For Each Area In SearchRange.Areas
With Area
If .Cells(.Cells.Count).Row > MaxRow Then
MaxRow = .Cells(.Cells.Count).Row
End If
If .Cells(.Cells.Count).Column > MaxCol Then
MaxCol = .Cells(.Cells.Count).Column
End If
End With
Next Area
Set LastCell = SearchRange.Worksheet.Cells(MaxRow, MaxCol)
Rem If your keyword can be a part of cell then change parameter xlWhole to xlPart:
Set FoundCell = SearchRange.Find(FindWhat, LastCell, xlValues, xlWhole, xlByRows)
If Not FoundCell Is Nothing Then
Set FirstFound = FoundCell
Do Until False ' Loop forever. We'll "Exit Do" when necessary.
If ResultRange Is Nothing Then
Set ResultRange = FoundCell
Else
Set ResultRange = Application.Union(ResultRange, FoundCell)
End If
Set FoundCell = SearchRange.FindNext(after:=FoundCell)
If (FoundCell Is Nothing) Then
Exit Do
End If
If (FoundCell.Address = FirstFound.Address) Then
Exit Do
End If
Loop
End If
Set FindAll = ResultRange
End Function
You can see how it works in this demo workbook - Create Links To Keywords.xlsm
EDIT By the way, the second part of this code, the FindAll() function, is a slightly shortened version of the Chip Pearson macro. Keep this link for yourself, there are many useful things to help you in future development.

Copy/Paste columns

I have several columns with headers in one excel workbook, I want to copy some of these columns into another workbook.
Let’s say I have my origin workbook:
Ident|Name|Code|Part|Desc|U|Total
These are the headers of the columns with some data below them.
And I want to copy only the data in the columns Ident, Code and Part in another workbook that has the same headers but in a different order with the exception that one header has a different name:
Code|Ident|Piece
It is blank and Piece corresponds to Part.
So I want a code that takes the data from the origin workbook and copy it to the destination workbook. Also if possible I’d like that you can choose the original workbook from a file as I have different excel files to choose from.
Thank you for your answers. I have never used VBA and I’m trying to learn.
I have the following code that lets you choose the data you want manually but I want something similar that does it automatically after recognizing the headers.
Sub ImportDatafromotherworksheet()
Dim wkbCrntWorkBook As Workbook
Dim wkbSourceBook As Workbook
Dim rngSourceRange As Range
Dim rngDestination As Range
Set wkbCrntWorkBook = ActiveWorkbook
With Application.FileDialog(msoFileDialogOpen)
.Filters.Clear
.Filters.Add "Excel 2007-13", "*.xlsx; *.xlsm; *.xlsa"
.AllowMultiSelect = False
.Show
If .SelectedItems.Count > 0 Then
Workbooks.Open .SelectedItems(1)
Set wkbSourceBook = ActiveWorkbook
Set rngSourceRange = Application.InputBox(prompt:="Select source range", Title:="Source Range", Default:="A1", Type:=8)
wkbCrntWorkBook.Activate
Set rngDestination = Application.InputBox(prompt:="Select destination cell", Title:="Select Destination", Default:="A1", Type:=8)
rngSourceRange.Copy rngDestination
rngDestination.CurrentRegion.EntireColumn.AutoFit
wkbSourceBook.Close False
End If
End With
End Sub
I add here some part I modified:
arrC = Split("CODE|ident|Piece", "|")
lastColO = shO.Cells(1, Cells.Columns.Count).End(xlToLeft).Column
arrO = shO.Range(shO.Cells(1, 1), shO.Cells(1, lastColO)).Value
'Copy the columns:
arrC = Split("CODE|ident|Piece", "|")
lastColO = shO.Cells(1, Cells.Columns.Count).End(xlToLeft).Column
arrO = shO.Range(shO.Cells(1, 1), shO.Cells(1, lastColO)).Value
'Copy the columns:
For j = 0 To UBound(arrC)
If arrC(j) = "Ident" Then strH = "ident" Else strH = arrC(j)
If arrC(j) = "Code" Then strH = "CODE" Else strH = arrC(j)
If arrC(j) = "Piece" Then strH = "Part" Else strH = arrC(j)
For i = 1 To UBound(arrO, 2)
If arrO(1, i) = strH Then
lastRowO = shO.Cells(Rows.Count, i).End(xlUp).Row 'last row of the found orig header column
lastRowC = shC.Cells(Rows.Count, j + 1).End(xlUp).Row 'last row of toCopy sheet header column
arrTransf = shO.Range(shO.Cells(2, i), shO.Cells(lastRowO, i)).Value
Set copyCell = shC.Range(shC.Range("A1"), shC.Cells(1, lastColO)).Find(arrC(j))
If copyCell Is Nothing Then MsgBox "There is not a column named """ & _
arrC(j) & """ in the page to Copy.": Exit Sub
copyCell.Offset(1, 0).Resize(UBound(arrTransf, 1), UBound(arrTransf, 2)).Value = arrTransf
End If
Next i
Next j
End Sub
Try this code, please. It copies columns from the active sheet to shC worksheet, which must be set in the code below:
Sub moveColumnsContent()
Dim shO As Worksheet, shC As Worksheet, lastRowO As Long, lastRowC As Long
Dim arrO As Variant, arrC As Variant, lastColO As Long, lastColC As Long
Dim El As Variant, arrTransf As Variant, strH As String, copyCell As Range
Dim wbNumb As Variant, wb As Workbook, ws As Worksheet, strWB As String
Dim WbC As Workbook, sh As Worksheet, strWh As String, shNunb As String
Dim i As Long, j As Long
Set shC = ActiveSheet
WbSelection:
For i = 1 To Workbooks.count
strWB = strWB & Workbooks(i).Name & " - " & i & vbCrLf
Next i
wbNumb = InputBox("Please, write the the right workbook name number to be chosen:" & vbCrLf & _
vbCrLf & strWB, "Choose the workbook from where to copy columns!", 1)
If wbNumb = "" Then MsgBox "You did not select anything and code stops!"
Exit Sub
If IsNumeric(wbNumb) Then
On Error Resume Next
Set WbC = Workbooks(CLng(wbNumb))
if Err.Number<> 0 Then
Err.Clear: On Error GoTo 0:Exit Sub
End If
On Error GoTo 0
Else
MsgBox "Please select the number to the right side of the chosen workbook!": GoTo WbSelection
End If
WsSelection:
For i = 1 To WbC.Worksheets.count
strWh = strWh & WbC.Worksheets(i).Name & " - " & i & vbCrLf
Next
shNunb = InputBox("Please, write the the right sheet name number to be chosen:" & vbCrLf & _
vbCrLf & strWh, "Select the worksheet to be used for copying the columns!", 1)
If shNunb = "" Then MsgBox "Please select a worksheet number to be selected for copying columns!": _
GoTo WsSelection
Set shO = WbC.Worksheets(CLng(shNunb))
arrC = Split("Code|Ident|Piece", "|")
lastColO = shO.Cells(1, Cells.Columns.count).End(xlToLeft).Column
arrO = shO.Range(shO.Cells(1, 1), shO.Cells(1, lastColO)).value
'Copy the columns:
For j = 0 To UBound(arrC)
If arrC(j) = "Piece" Then strH = "Part" Else strH = arrC(j)
For i = 1 To UBound(arrO, 2)
If arrO(1, i) = strH Then
lastRowO = shO.Cells(Rows.count, i).End(xlUp).Row 'last row of the found orig header column
lastRowC = shC.Cells(Rows.count, j + 1).End(xlUp).Row 'last row of toCopy sheet header column
arrTransf = shO.Range(shO.Cells(2, i), shO.Cells(lastRowO, i)).value
Set copyCell = shC.Range(shC.Range("A1"), shC.Cells(1, lastColO)).Find(arrC(j))
If copyCell Is Nothing Then MsgBox "There is not a column named """ & _
arrC(j) & """ in the page to Copy.": Exit Sub
copyCell.Offset(1, 0).Resize(UBound(arrTransf, 1), UBound(arrTransf, 2)).value = arrTransf
End If
Next i
Next j
End Sub
If you will need more headers in the sheet to copy, it is enough to add them in "Code|Ident|Piece" string.
Now, trying to think how it would be more convenient for you to use it, probably, a better way would be to name the sheet where from the columns will be copied, in a specific way (maybe "MasterSheet") and copy the columns to the active one. Or, iterate between all Workbook sheets and do this process automatically. But, please, try the code as it is and let me know how looks more convenient for you.
If your intention is to extract a three columns set in the fixed order Code|Ident|Part=Piece copying them to the first three target columns A:C, you may try the following Rearrange procedure executing these steps:
[0-1] get source data
[2 ] rearrange columns of source data in a given order by a one-liner instead of copying separate columns arrays each time
[3 ] write (rearranged) data to target sheet
Sub Rearrange(src As Worksheet, tgt As Worksheet)
'Purpose: extract and rearrange data array columns
'Author: https://stackoverflow.com/users/6460297/t-m
With src
'[0] get last row of source data in column A:A (Ident)
Dim lastRow As Long: lastRow = .Range("A" & .Rows.Count).End(xlUp).Row
'[1] assign data to (1-based) 2-dim variant datafield array
Dim data: data = .Range("A2:G" & lastRow)
'[2] rearrange columns
' where Array(3,1,4) gets the 3rd, 1st and 4th column only
' (and Evaluate("ROW(1:nnn)") gets the entire row set)
data = Application.Index(data, Evaluate("ROW(1:" & (lastRow - 1) & ")"), Array(3, 1, 4))
End With
'[3] write (rearranged) data to target sheet
tgt.Range("A2").Resize(UBound(data), 3) = data
End Sub
If, however you are confronted with a variable target column structure you might play around with Moving columns based on header name and change it to your needs :-)
This should work, you only need to tweak the target sheet and, if it were the case, add more cases where origin/target have different column names:
Option Explicit
Sub Main()
Dim arrOrigin As Variant: arrOrigin = GetArrayFromSheet
Dim OriginHeaders As New Dictionary: Set OriginHeaders = GetOriginHeaders(arrOrigin)
With ThisWorkbook.Sheets("Your target sheet name") 'change this name
Dim arrTarget As Variant: ReDim arrTarget(1 To UBound(arrOrigin), _
1 To .UsedRange.Columns.Count)
'Last row on column 1 (or column A)
Dim LastRow As Long: LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row + 1
End With
Dim TargetHeaders As New Dictionary: Set TargetHeaders = GetTargetHeaders(arrTarget)
Dim i As Long
Dim Key As Variant
Dim OriginColumn As Long, TargetColumn As Long
Dim x As Long: x = 1
For i = 2 To UBound(arrOrigin)
For Each Key In TargetHeaders.Keys
OriginColumn = OriginHeaders(Split(TargetHeaders(Key), "\")(0))
TargetColumn = Split(TargetHeaders(Key), "\")(1)
arrTarget(x, TargetColumn) = arrOrigin(i, OriginColumn)
Next Key
Next i
ThisWorkbook.Sheets("Your target sheet name").Range("A" & LastRow).Resize(UBound(arrTarget), UBound(arrTarget, 2)).Value = arrTarget
End Sub
Private Function GetArrayFromSheet() As Variant
Dim wb As Workbook: Set wb = FilePicker
Dim ws As Worksheet
For Each ws In wb.Sheets
If ws.Name Like "* Annex 1" Then
GetArrayFromSheet = ws.UsedRange.Value
wb.Close False
Exit Function
End If
Next ws
End Function
Private Function FilePicker() As Workbook
With Application.FileDialog(msoFileDialogOpen)
.Filters.Clear
.Filters.Add "Excel 2007-13", "*.xlsx; *.xlsm; *.xlsa"
.AllowMultiSelect = False
.Show
If .SelectedItems.Count > 0 Then
Set FilePicker = Workbooks.Open(.SelectedItems(1))
Else
MsgBox "No file was selected, the procedure will end"
End
End If
End With
End Function
Private Function GetOriginHeaders(arr As Variant) As Dictionary
Set GetOriginHeaders = New Dictionary
Dim i As Long
For i = 1 To UBound(arr, 2)
GetOriginHeaders.Add arr(1, i), i
Next i
End Function
Private Function GetTargetHeaders(arr As Variant) As Dictionary
Set GetOriginHeaders = New Dictionary
Dim i As Long
Dim MyHeader As String
For i = 1 To UBound(arr, 2)
MyHeader = arr(1, i)
Select Case MyHeader
Case "Piece"
MyHeader = "Part"
'..More cases for different names
End Select
TargetHeaders.Add arr(1, i), MyHeader & "\" & i
Next i
End Function

Resources