Copying the data from two columns from every sheet into a new sheet - excel

I have several workbooks with multiple worksheets. Each worksheet has two columns in positions "H" and "I". These columns in each worksheet has a different number of rows for these two columns. The worksheets are named differently as in
Sheet1: Data
Sheet2: Calc
Sheet3: Settings
Sheet4: Append1
Sheet5: Append2
.....
After the "Settings" sheet, each sheet is named append and then 1,2,3,...
I want to copy the columns H and I from every sheet except Calc and Settings into a new sheet.
It should be copied as columns. So it should look something like this in the new sheet
Data.col(H)|Data.col(I)|Append1.col(H)|Append1.col(I)|Append2.col(H)|Append2.col(I)| .....
How do I achieve this?
I have been using the formula =Append1H:H and =Append1I: I but it is too much data and cannot be done manually.
Any help is appreciated.

Please, try the next way. It will be very fast, using arrays and working mostly in memory. It does not use clipboard, it will not copy the range format. It will return in columns "A:B" of the newly created sheet (or the cleaned one, if already existing):
Sub copyColumns()
Dim wb As Workbook, ws As Worksheet, lastR As Long, arrC, arrFin, i As Long
Set wb = ActiveWorkbook 'use here the apropriate workbook
For Each ws In wb.Worksheets
If ws.name <> "Settings" And ws.name <> "Calc" And _
ws.name <> "Cons_Sheet" Then
i = i + 1
lastR = ws.Range("H" & ws.rows.count).End(xlUp).row
arrC = ws.Range("H" & IIf(i = 1, 1, 2) & ":I" & lastR).value 'copy header only from the first sheet
arrFin = buildArr(arrFin, arrC, i) 'add arrC to the one keeping all processing result
End If
Next ws
'add a new sheet, or clean it if existing:
Dim shC As Worksheet
On Error Resume Next
Set shC = wb.Worksheets("Cons_Sheet")
On Error GoTo 0
If Not shC Is Nothing Then
shC.UsedRange.ClearContents
Else
Set shC = wb.Worksheets.Add(After:=wb.Worksheets(wb.Worksheets.count))
shC.name = "Cons_Sheet"
End If
'drop the processed array content in the new added sheet:
shC.Range("A1").Resize(UBound(arrFin), UBound(arrFin, 2)).value = arrFin
MsgBox "Ready..."
End Sub
Function buildArr(arrF, arrC, i As Long) As Variant
If i = 1 Then arrF = arrC: buildArr = arrF: Exit Function 'use the first returned array
Dim arrSum, j As Long, k As Long
arrSum = WorksheetFunction.Transpose(arrF)
ReDim Preserve arrSum(1 To UBound(arrF, 2), 1 To UBound(arrF) + UBound(arrC))
k = UBound(arrF)
For i = 1 To UBound(arrC)
k = k + 1
For j = 1 To UBound(arrC, 2)
arrSum(j, k) = arrC(i, j)
Next j
Next i
buildArr = WorksheetFunction.Transpose(arrSum)
End Function

You can Just use this formula.
I choose 3 different range in the formula just to show you, you can use any kind of range for this to work.
=FILTERXML(""&SUBSTITUTE(TEXTJOIN(",",TRUE,Table1[Fruits Name],Sheet3!E2:E128,Sheet4!A2:A73),",","")&"","//y")

Related

Is there a way to extract the same range of data from specific worksheets in an excel workbook?

Suppose you have an excel workbook with 30 sheets, and you would like to extract data from the range A1:K40 from every sheet into that workbook, and then add all of the extracted data into a single excel sheet in a different workbook. Would this be possible?
I would like to build it such that the user is prompted to select a file, and the file selected will be of the same format, a workbook with 28-31 sheets, and complete the function as previously stated. An additional feature I'd like to add would be when the user selects the workbook, they will be prompted to select which sheets to extract data from within that workbook.
I'd appreciate if anyone could direct me to similar example or even better, just guide me on which vba functions would be best to make this work.
Please, try the next way. It will create a string composed by concatenation of all sheets name with their index and the user is asked to choose the index of sheets to have ranges copied. They should choose them writing the index separated by comma (2,5,9,12). In case of more consecutive sheets, they can use 2,5,9-12,15, 9-12 meaning sheets form 9 to 12 (9,10,11,12). You should configurate the code using the sheets name where the ranges to be copied **instead "AllSheets" generically used in the code):
Sub CopySomeSheets()
Dim wbC As Workbook, wb As Workbook, ws As Worksheet, wsCopy As Worksheet, lastR As Long
Dim strAllSheets As String, strSelSheets As String, arrSheets, i As Long
Set wbC = ActiveWorkbook 'use here the workbook where from to copy sheets
Set wb = ThisWorkbook 'use here the workbook where to copy all ranges
Set ws = wb.Worksheets("AllSheets") 'the sheet where all ranges should be pasted!!!
'create the list of all sheets from whitch to choose the ones to be copied:
For i = 1 To wbC.Sheets.count
strAllSheets = strAllSheets & wbC.Sheets(i).name & " - " & i & vbCrLf
Next i
strSelSheets = InputBox("Please, select the sheets to be copied." & vbCrLf & _
"Use the sheets number (after its name) separated by comma ("","")." & vbCrLf & _
"More consecutive sheets should be represented as ""5-8"", which means sheets 5,6,7,8." & _
vbCrLf & vbCrLf & strAllSheets, _
"Make sheets selection")
If strSelSheets = "" Then MsgBox "You did not select any sheet...": Exit Sub 'in case of pressing Cancel or Top right X
arrSheets = makeSheetsArray(Split(Replace(strSelSheets, " ", ""), ","), wbC) 'make an array of sheets indexes
'copy the ranges from above selected sheets:
Application.ScreenUpdating = False: Application.Calculation = xlCalculationManual 'a little optimization
For i = 0 To UBound(arrSheets)
lastR = ws.Range("A" & ws.rows.count).End(xlUp).row 'lastr row on A:A
If lastR > 1 Then lastR = lastR + 1 'first range is copying starting from A1, the rest in the next empty row
wbC.Worksheets(CLng(arrSheets(i))).Range("A1:K40").Copy ws.Range("A" & lastR) 'copy the range
Next i
Application.ScreenUpdating = True: Application.Calculation = xlCalculationAutomatic
MsgBox "Ready..."
End Sub
Function makeSheetsArray(arr, wb As Workbook) As Variant 'it makes an array of all selected sheets (split x-y cases)
Dim arrFin, arr_, k As Long, i As Long, j As Long
ReDim arrFin(wb.Sheets.count) 'maximum possible number of sheets
For i = 0 To UBound(arr)
If InStr(arr(i), "-") > 0 Then 'for the case of x-y:
arr_ = Application.Evaluate("ROW(" & Replace(arr(i), "-", ":") & ")") ' 2D array base 1
For j = 1 To UBound(arr_)
arrFin(k) = arr_(j, 1): k = k + 1
Next j
Else
arrFin(k) = arr(i): k = k + 1
End If
Next i
ReDim Preserve arrFin(k - 1)
makeSheetsArray = arrFin
End Function
Please, send some feedback after testing it.
If something not clear enough, do not hesitate to ask for clarifications...

How do i copy and paste data to worksheets that i created in VBA using for loop?

Im trying to copy and paste my data and assign them into different worksheets.For example, if column F is martin 1, the entire row that has martin1 will be paste to worksheets("Index1"). Same thing for Charlie 1 and it will be paste to worksheets("Index2"). However, I faced with a object defined error here as shown in my code below. Any ideas how to solve it?
Sub SaveRangewithConsecutiveDuplicateValuestoNewSheet()
'Define all variables
Dim wb As Workbook, ws As Worksheet, sCel As Range, rwNbr As Long
Set wb = ThisWorkbook 'Set workbook variable
Set ws = wb.Worksheets("Sheet1") 'set worksheet variable using workbook variable
Set sCel = ws.Cells(1, 6) 'Set the first start cell variable to test for duplicate values
Dim i As Integer
Dim site_i As Worksheet
For i = 1 To 3
Set site_i = Sheets.Add(after:=Sheets(Worksheets.count))
site_i.Name = "Index" & CStr(i)
Next i
Application.DisplayAlerts = False
For rwNbr = 2 To ws.Cells(ws.Rows.count, 6).End(xlUp).Offset(1).Row Step 1 'Loop
If ws.Cells(rwNbr, 6).Value = "Martin1" Then
ws.Range(sCel, ws.Cells(rwNbr, 6)).EntireRow.Copy Destination:=Sheets("Index1").Range("A1")
ElseIf ws.Cells(rwNbr, 6).Value = "Charlie1" Then
ws.Range(sCel, ws.Cells(rwNbr - ws.UsedRange.Rows.count, 6)).EntireRow.CopyDestination:=Sheets("Index2").Range("A1") '<----application defined or object defined error here
End If
Next rwNbr
Application.DisplayAlerts = True
End Sub
This is the link to my worksheet. https://www.dropbox.com/home?preview=Sample+-+Copy.xlsm
The final output should look something like this...
If your raw data does not have a header row then I would use a loop to gather up your target cells and copy them accordingly.
You will need to update your 3 target values inside Arr to Charlie1, Martin1, etc.
Macro Steps
Loop through each name in Arr
Loop through each row in Sheet1
Add target row to a Union (collection of cells)
Copy the Union to the target sheet where target Sheet Index # = Arr position + 1
Sub Filt()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim cs As Worksheet
Dim Arr: Arr = Array("Value1", "Value2", "Value3")
Dim x As Long, Target As Long, i As Long
Dim CopyMe As Range
'Create 3 Sheets, move them to the end, rename
For x = 1 To 3
Set cs = ThisWorkbook.Sheets.Add(After:=Sheets(ThisWorkbook.Sheets.Count))
cs.Name = "Index" & x
Next x
lr = ws.Range("F" & ws.Rows.Count).End(xlUp).Row
'Loop through each name in array
For Target = LBound(Arr) To UBound(Arr)
'Loop through each row
For i = 1 To lr
'Create Union of target rows
If ws.Range("F" & i) = Arr(Target) Then
If Not CopyMe Is Nothing Then
Set CopyMe = Union(CopyMe, ws.Range("F" & i))
Else
Set CopyMe = ws.Range("F" & i)
End If
End If
Next i
'Copy the Union to Target Sheet
If Not CopyMe Is Nothing Then
CopyMe.EntireRow.Copy Destination:=ThisWorkbook.Sheets("Index" & Target + 1).Range("A1")
Set CopyMe = Nothing
End If
Next Target
End Sub
Tested and working as expected on my end, however....
If you had headers this would be much easier with a copy/paste. If you run the same macro on same book twice this will break for many reasons such as having duplicated sheet names, breaking the relationship between Sheet Index # = Arr Position + 1, etc...

How can I change the direction this script is grouping data?

I have a script that forms arrays within an overarching array based on duplicate values in column A.
manager 1 its own workbook
manager 2
manager 2 these two would be grouped into another workbook and so on.
problem is, these cells in column A are now transposed as headers in row 1.
How would I edit this script to now group this data by the row headers and take the whole column instead of how the script is originally written?
I figure it has something to do with swapping the Last = Data(1,i) or something like that.
Option Explicit
Sub Main()
Dim wb As Workbook
Dim Data, Last, JobFamily
Dim i As Long, j As Long, k As Long, a As Long
Dim Dest As Range
'Refer to the template
Set wb = Workbooks("Book2.xlsx")
'Refer to the destination cell
Set Dest = wb.Sheets("Sheet11").Range("B1")
'Read in all data
With ThisWorkbook.Sheets("Sheet1")
Data = .Range("bj2", .Range("A" & Rows.Count).End(xlUp))
End With
wb.Activate
Application.ScreenUpdating = False
'Process the data
For i = 1 To UBound(Data)
'Manager changes?
If Data(i, 1) <> Last Then
'Skip the first
If i > 1 Then
'Scroll into the view
Dest.Select
'Save a copy
wb.SaveCopyAs ThisWorkbook.Path & Application.PathSeparator & _
ValidFileName(Last & ".xlsx")
End If
'Clear the employees
Dest.Resize(, Columns.Count - Dest.Column).EntireColumn.ClearContents
'Remember this manager
Last = Data(i, 1)
'Start the next round
j = 0
End If
'Write the employee data into the template
a = 0
For k = 1 To UBound(Data, 2)
Dest.Offset(a, j) = Data(i, k)
a = a + 1
Next
'Next column
j = j + 1
Next
End Sub
You called it. All references to "Data(i,1)" must be replaced with "Data(1,i)" to transpose the first column of the range into the first row.

Consolidate column from multiple sheets into single column on another sheet

I need to copy all the text values from Column F on +10 sheets and place them in a single Column on an aggregate sheet. I do not need to perform any computation on the data, just copy the text values derived from formulas. For example:
Sheet1 Col F:
1
2
3
Sheet2 Col F:
4
5
6
I would like "Master" Col A be:
1
2
3
...
6
This code gets me mostly there, but I need the Range to vary. For instance, not every sheet has 3 rows of data, but I want them to be copied directly after each other.
Sub MM1()
Dim ws As Worksheet, lr As Long
lr = Sheets("Master").Cells(Rows.Count, "A").End(xlUp).Row + 1
For Each ws In Worksheets
If ws.Name <> "Master" Then
ws.Range("F1:G15").Copy
Sheets("Master").Range("A" & lr).PasteSpecial xlPasteValues
lr = Sheets("Master").Cells(Rows.Count, "A").End(xlUp).Row + 1
End If
Next ws
End Sub
EDIT: Every sheet DOES have the same number of rows with a formula in them, but the Values vary from sheet to sheet. So I need some check that looks for a "" Value as the "last row" then move to the next sheet.
First of all, you can use the same logic to get the last row in the column "F" in each datasheet instead of hard-coding 3 rows usingrange.end(xlUp).Row method.
2nd I don't like the copy-paste method. it is slow and is very bothering you always calculate new insertion point and paste. You can utilize array in VBA to realize this functionality. And work with Array is very straightforward and fast.
Below is the code you can grab and use.
Sub MM1()
Application.ScreenUpdating = False
'Loop through worksheets, put the values in column F into arr array
Dim arr(1 To 10000), cnt As Integer, i As Integer
cnt = 0
For Each ws In Worksheets
If ws.Name <> "Master" Then
For i = 1 To ws.Cells(Rows.Count, "F").End(xlUp).Row
cnt = cnt + 1
arr(cnt) = ws.Cells(i, "F").Value
Next i
End If
Next ws
'Loop through arr array, populate value into Master sheet, column A
For i = 1 To cnt
ThisWorkbook.Sheets("Master").Cells(i, "A") = arr(i)
Next i
Application.ScreenUpdating = True
End Sub
only small changes and its working good :)
1. I changed the Master to Sheet5 => you can use your sheet name.
2. Added a new variable in loop to identify the range for each sheet to be copied.
3. Change the method to paste the copied data to destination.
Sub MM1()
Dim ws As Worksheet, lr As Long
lr = Sheets("Sheet5").Cells(Rows.Count, "A").End(xlUp).Row + 1
For Each ws In Worksheets
If ws.Name <> "Sheet5" Then
Dim currentRange As Long
currentRange = ws.Cells(Rows.Count, "A").End(xlUp).Row
ws.Range("A1:A" & currentRange).Copy Destination:=Sheets("Sheet5").Range("A" & lr)
lr = Sheets("Sheet5").Cells(Rows.Count, "A").End(xlUp).Row + 1
End If
Next ws
End Sub
let me know if this works for you or not ?
I tried to keep your code as intact as possible. Here is one way to make it work (with as much preservation of your code as possible). There are still minor "touch ups" you would need to do (eg your "Master" sheet would have a blank row).
Sub MM1()
Dim ws As Worksheet
For Each ws In Worksheets
If ws.Name <> "Master" Then
ws.Range(ws.Range("F1"), ws.Range("F1").End(xlDown)).Copy
Sheets("Master").Range("A65535").End(xlUp).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
End If
Next ws
End Sub

Transferring Cell Values Between Worksheets | Str Looper

Intended Result
If a row in a table contains any of the listed strings in column L on Sheet1, Then copy the entire row from Sheet1 and paste the row into a duplicate table on Sheet2 (which would be blank at the beginning).
(UNINTERESTED, UNRELATED, UNDECIDED, etc...)
Then delete the entire row that was transferred from sheet 1.
After macro runs, the new transfers should not reset table on Sheet2, rather add rows on the pre-existing lines. This document would be utilized over months.
Variables
Sheet1 is named Pipeline_Input
Sheet2 is named Closed_Sheet
Sheet1 table is named tblData
Sheet2 table is named tblClosed
Images
Image 1 is the code with error
Image 2 is Sheet 1 with some picture explanation
Image 3 is Sheet 2 with some picture explanation
Current Result
Run-time error '1004':
Application-defined or object-defined error
Sub closedsheet()
Application.ScreenUpdating = False
Dim Pipeline_input As Worksheet 'where is the data copied from
Dim Closed_Sheet As Worksheet 'where is the data pasted to
Dim strPhase() As String
Dim i As Integer
Dim intPhaseMax As Integer
Dim lngLstRow As Long
Dim rngCell As Range
Dim finalrow As Integer
Dim lr As Long 'row counter
Dim Looper As Integer
intPhaseMax = 6
ReDim strPhase(1 To intPhaseMax)
strPhase(1) = "LOST"
strPhase(2) = "BAD"
strPhase(3) = "UNINTERESTED"
strPhase(4) = "UNRELATED"
strPhase(5) = "UNDECIDED"
strPhase(6) = "BUDGET"
'set variables
Set Pipeline_input = Sheet1
Set Closed_Sheet = Sheet2
lr = Range("A" & Rows.Count).End(xlUp).Row
For Looper = LBound(strPhase) To UBound(strPhase)
For i = lr To 6 Step -1
Next
If Not Sheet1.Range("L9:L300" & lngLstRow).Find(strPhase(Looper), lookat:=xlWhole) Is Nothing Then
Range(Cells(i, 1), Cells(i, 20)).Copy
Sheet2.Range("A" & Rows.Count).End(3)(2).PasteSpecial xlPasteValues
Range(Cells(i, 1), Cells(i, 20)).Delete
End If
Next
Sheet2.Select
Sheet2.columns.AutoFit
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
Okay, there were a plethora of issues with the code you posted, but I decided to help you out here - Notice a few things - There's no copying and pasting here - we're just transferring data.
Secondly, use easy to understand variables. lr and lngLastRow can't be distinguished from one another, so classify them by which worksheet you're getting that value from.
We create an array in one fell swoop here - Just declare a variant and place our values in. ARRAYS (TYPICALLY) START AT ZERO, NOT ONE, so our loop starts at 0 :). Again, this is what's known as best practice...
I swapped out Looper for j. Again, keep. it. simple!
EDIT: I tested this code out on a simulated workbook and it worked fine - should run into no issues for you either.
EDIT2: Also, always use Option Explicit!
Option Explicit
Sub closedsheet()
Application.ScreenUpdating = False
Dim Pipeline_Input As Worksheet 'source sheet
Dim Closed_Sheet As Worksheet 'destination sheet
Dim i As Long, j As Long, CSlastrow As Long, PIlastrow As Long
Dim strPhase As Variant
'Here we create our array
strPhase = Array("LOST", "BAD", "UNINTERESTED", "UNRELATED", "UNDECIDED", "BUDGET")
'Assign worksheets
Set Pipeline_Input = ActiveWorkbook.Worksheets("Pipeline_Input")
Set Closed_Sheet = ActiveWorkbook.Worksheets("Closed_Sheet")
PIlastrow = Pipeline_Input.Range("A" & Rows.Count).End(xlUp).Row
For j = 0 To UBound(strPhase)
For i = PIlastrow To 6 Step -1
If Pipeline_Input.Range("L" & i).Value = strPhase(j) Then
'Refresh lastrow value
CSlastrow = Closed_Sheet.Range("A" & Rows.Count).End(xlUp).Row
'Transfer data
Closed_Sheet.Range("A" & CSlastrow + 1 & ":S" & CSlastrow + 1).Value = _
Pipeline_Input.Range("A" & i & ":S" & i).Value
'Delete the line
Pipeline_Input.Range("A" & i & ":S" & i).EntireRow.Delete
End If
Next i
Next j
Closed_Sheet.Select
Closed_Sheet.Columns.AutoFit
Application.ScreenUpdating = True
End Sub

Resources