0

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
Raúl
  • 159
  • 1
  • 10
  • just in your target sheet, use `=` cells of ur original sheet of corresponding column, then drag the formula. – Anabas May 22 '20 at 10:12
  • But can you explain more? Like the whole code if possible, I have never used vba and I don’t know how to put it :/ – Raúl May 22 '20 at 10:13
  • not vba,.just using = to get the value of cells in your originalsheet, it is also dynamic. – Anabas May 22 '20 at 10:17
  • If u insist on vba, use macro recorder to record the step of your operations, you can repeat it next time. I think it is easier for your current stage of vba – Anabas May 22 '20 at 10:19
  • But I want to use vba as I have several origin sheets and I want to copy them when I need them. I mean, I want my final excel sheet that I push a button and I choose the file from which I copy the data, and the data is copied to the columns of my final sheet. – Raúl May 22 '20 at 10:21
  • I added now some code I have but I want to try to see if it’s possible doing it automatically. Thank you – Raúl May 22 '20 at 10:37
  • Do you need to also copy format, or only data from the column without format? I am asking about that, in order to prepare a piece of code able to work fast. It can be also done to copy format, but if not necessary it will be a pity to waste Excel resources... – FaneDuru May 22 '20 at 10:41
  • Only the data, the format is not necessary, thank you so much :) – Raúl May 22 '20 at 10:43
  • u using inputbox to fetch/select the range, this could be automatic, here you can `dim lastrow as integer `(or long , if huge datas), `lastrow = cells(rows.count, 'A').end(xlup).row` to get the last cell. then you can get a accurate range to copy and past it continuely. (sorry for that, i am on the way , type via phone). eg `range("A2:A" & lastrow)` – Anabas May 22 '20 at 10:51
  • Your files do always contain the data in the same excel sheet? I mean, is the sheet from which you will take the data with the same name? – Damian May 22 '20 at 10:58
  • I understand but that way you need to tell the code where the headers are. Is there a way to find the headers and copy the data below them? – Raúl May 22 '20 at 11:03
  • No Damian, the origin sheet have different names. For example: 10000 Annex 1, 10004 Annex 1, 10030 Annex 1, ... all names would be a “xxxxx Annex 1” – Raúl May 22 '20 at 11:04
  • Following that, could there be more sheets called `"* Annex 1"`which won't contain data? – Damian May 22 '20 at 11:06
  • No Damian, all the sheets that contain my data have those names. But I only need to open one at a time, the one I choose. – Raúl May 22 '20 at 11:12
  • FYI - Posted a late approach extracting and rearranging a datafield array by a *one-liner* via the Application.Index() function :-) – T.M. May 22 '20 at 19:58

3 Answers3

1

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.

FaneDuru
  • 38,298
  • 4
  • 19
  • 27
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/214523/discussion-on-answer-by-faneduru-copy-paste-columns). – Samuel Liew May 24 '20 at 11:53
  • One question @FaneDuru, in that code of yours, let's say we have two workbooks so your code let you chosse between numbers 1 or 2. But is it possible that the code exits if you put a number that is not in the list of workbooks (for example anything bigger than 2 or below 1) and also that it exits if you cancel the dialog box?? Because right now it goes in a loop until you insert one correct number but I prefer it to exit. I have tried to change it but I can't get it. Thank you! – Raúl May 28 '20 at 15:15
  • @Raúl: Now, the code would exit with a warning in case of `Cancel` and without any warning in case of inputting a number exceeding the existing open Workbooks number – FaneDuru May 28 '20 at 18:07
  • Thank you so much! Your solutions are always great! – Raúl May 28 '20 at 18:52
1

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 :-)

T.M.
  • 9,436
  • 3
  • 33
  • 57
  • 1
    Nice way of columns rearranging. Voted up for the nice way of rearranging. But, in this specific case, the columns order is variable in both worksheets and cannot be applied exactly like it is. I like using arrays and even playing with them. For instance, yesterday I posted a piece of code, after the one asking the question already marked an accepted answer, only for the sake of playing with arrays... I used an array of arrays, each array element being obtained by slicing columns to be analyzed. And then I used `sort` array instead of `Count`. In fact, let me find it and post a link... – FaneDuru May 22 '20 at 20:40
  • See [here](https://stackoverflow.com/questions/61938334/loop-through-non-continuous-columns?noredirect=1#comment109552642_61938334) the answer, if interested and find time to look of it... – FaneDuru May 22 '20 at 20:41
0

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
Damian
  • 5,152
  • 1
  • 10
  • 21
  • Can it be done for different workbooks rather that different sheets in the same workbook? Thank you – Raúl May 22 '20 at 12:13
  • using Dir to iterate all of the files under the folder – Anabas May 22 '20 at 12:33
  • The private function gives me an error “user is not defined” – Raúl May 22 '20 at 12:36
  • this top topic takes too much time. better question description with pictures or file would save everyone's time. – Anabas May 22 '20 at 12:40
  • Sorry, I was working with my phone, I’ll try to make it clear when I get a computer. Sorry for wasting your time :/ – Raúl May 22 '20 at 12:45