How to fix 'Run-time error '1004' PasteSpecial - excel

I have a file (called original) that has partially information for each row. Each row has a file name column (from where information is to be captured from).
For each row I'd like to open up the file in the file name column, and grab information from certain rows.
In the file it is only one column, with rows "Supplier Number : _____", the location of this row is variable, so I'd like to iterate through each row in the file to copy this cell value and paste it into the original file in the corresponding row.
This is what I have so far:
Const FOLDER_PATH = "C:\Users\[user]\Downloads\"
Sub iterateThroughAll()
ScreenUpdating = False
Dim wks As Worksheet
Set wks = ActiveSheet
Dim source As String
Dim target As String
Dim update As String
Dim rowT As Integer
rowT = 2
rowTT = 1
Dim rowRange As Range
Dim colRange As Range
Dim rowRangeT As Range
Dim LastCol As Long
Dim LastRow As Long
Dim LastRowT As Long
LastRow = wks.Cells(wks.Rows.Count, "A").End(xlUp).Row
Set rowRange = wks.Range("A2:A" & LastRow)
'Loop through each row
For Each rrow In rowRange
source = FOLDER_PATH & wks.Cells(i, 18).Value 'the name of the file we want to grab info from in this Column, always populated
'if the cell is empty, search through the file for "Supplier Number : "
If IsEmpty(wks.Cells(rowT, 19)) Then
Set wb = Workbooks.Open(source)
wb.Activate
LastRowT = Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
Set rowRangeT = wks.Range("A1:A" & LastRowT)
For Each i In rowRangeT
If InStr(i.Cells.Offset(rowTT), "Supplier") > 0 Then
Range("A" & rowTT).Select
Selection.Copy
Windows("Get Supplier Number.xlsm").Activate
Range("A" & rowT).Select
wks.Paste
Else
rowTT = rowTT + 1
End If
Next i
wb.Close
Next rrow
ScreenUpdating = True
End Sub
I get the pastespecial error 1004.
What is expected is that for each row in "Get Supplier Number.xlsm", the row's A column is updated with the information
Thank you for helping!

First of all you should get rid of Activate and Select methods. You don't have to use them and they give nothing to your code. Using them is not a good approach.
To avoid them you should use specific references. Which you are doing so, until a specific point. Inside the for loop, after setting the wb, replace everything with the following:
With wb.Worksheets(1)
LastRowT = .Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
Set rowRangeT = .Range("A1:A" & LastRowT)
For Each i In rowRangeT
If InStr(i.Cells.Offset(rowTT), "Supplier") > 0 Then
.Range("A" & rowTT).Copy wks.Range("A" & rowT)
Else
rowTT = rowTT + 1
End If
Next i
wb.Close
End With
I think this should do the job for you.
PS: If you need just the value of the cell in the opened workbook, then you could replace the Copy line with a simple equality:
wks.Range("A" & rowT) = .Range("A" & rowTT)

Related

Looping Through 2 Columns & Copying 2nd Column's Data Under the First

I am trying to create a list with 2 columns by placing the values from the 2nd column under the first on a new tab. In my screenshot I have column A "Data 1" and column B "Data 2". Each value under Data 1 has a corresponding value under Data 2. I am trying to make it look like the Second Tab column where the value under Data 1 is copied over first then Data 2 is Copied underneath. There are blanks in between values so im trying to figure out a way to capture all the data excluding the blanks so its 1 organized list. I have tried the following so far but i cant figure it out:
Sub MoveData()
Dim wb As Workbook: Set wb = ThisWorkbook
For i = 1 To 15
wb.Sheets("Sheet1").Range("A2:A" & i).Copy Destination:=wb.Sheets("Sheet2").Range("A1")
wb.Sheets("Sheet1").Range("A2:A" & i).Offset(0, 1).Copy _
Destination:=wb.Sheets("Sheet2").Range("A2" & lastrow).Offset(1, 0)
wb.Sheets("Sheet1").Range("A2:A" & i).Offset(0, 1).Copy _
Destination:=wb.Sheets("Sheet2").Range("A2:A" & i).Offset(1, 0)
Next i
End Sub
With the help of the following function you will find the last non empty row in column 1
Function FindLastRow(rg As Range) As Long
On Error GoTo EH
FindLastRow = rg.Find("*", , Lookat:=xlPart, LookIn:=xlFormulas _
, searchorder:=xlByRows, searchdirection:=xlPrevious).Row
Exit Function
EH:
FindLastRow = rg.Cells(1, 1).Row
End Function
Then you can copy the data into worksheet 2 with the following code
Sub pasteData()
Dim wks1 As Worksheet
Set wks1 = Worksheets("Sheet1")
Dim lastRow As Long
lastRow = FindLastRow(wks1.Columns(1)) ' last non empty row in column 1
Dim rg As Range
Set rg = wks1.Range("A1:B" & lastRow) 'range with the data in question
Dim vdat As Variant
vdat = rg.Value ' copy the data into an arry
' dim array which is big enough for the result
Dim rDat As Variant
ReDim rDat(0 To 2 * lastRow)
' copy the data from the 2-dim array into 1-dim array
Dim i As Long, j As Long
For i = LBound(vdat) To UBound(vdat)
' copy only data where the first column contains data
If Len(vdat(i, 1)) > 0 Then
rDat(j) = vdat(i, 1)
rDat(j + 1) = vdat(i, 2)
j = j + 2
End If
Next i
Dim wks2 As Worksheet
Set wks2 = Worksheets("Sheet2")
' prepare the second range (bigger than needed but does not harm)
Set rg = wks2.Range("A1:A" & 2 * lastRow)
' copy the data into the second sheet
rg = WorksheetFunction.Transpose(rDat)
End Sub

Find string in one worksheet and select it in another

I've got Workbook where I got names and hours worked of employees. I'm looking for comparing rows in one worksheet (Range B6:CC6) and find it in another with selection on cell with employee name (Range A1:A5000) when I change sheets from 1 to 2.
Tried some Range.Find and others, no idea how to do it
Public Sub FindPosition()
Dim Actcol As Integer, Pos As Range, Name As Range
Actcol = ActiveCell.Column
MsgBox "ActiveCell is" & Actcol
Set Pos = Cells(6, Actcol)
MsgBox Pos
Pos.Select
If Worksheets("Sheet2").Activate Then
Worksheets("Sheet2").Range("A1:AA5100").Select
Set Name = Selection.Find(Pos, LookIn:=xlValues)
End If
End Sub
First, if you want to trigger some macro by activation of Sheet2, you need to handle Activate event of Sheet2. This can be done by declaring subroutine in Sheet module like this.
Private Sub Worksheet_Activate()
'Codes you want to be run when Sheet2 is activated.
End Sub
Second, a simple way to find a cell with specific value is to use WorksheetFunction.Match. For example,
Dim SearchInRange As Range
Set SearchInRange = Range("A1:A5000")
Dim EmployeeName As Variant
EmployeeName = ... 'Actual employee name you want to search
On Error GoTo NotFound
Dim Index As Variant
Index = WorksheetFunction.Match(EmployeeName, SearchInRange, 0)
On Error GoTo 0
SearchInRange.Cells(Index).Select
GoTo Finally
NotFound:
' Handle error
Finally:
Range.Find may also work, but remember it has the side effect of changing the state of "Find and Replace" dialog box.
This may helps you
Option Explicit
Sub test()
Dim i As Long, LastRowA As Long, LastRowB As Long
Dim rngSearchValues As Range, rngSearchArea As Range
Dim ws1 As Worksheet, ws2 As Worksheet
'Set you worksheets
With ThisWorkbook
'Let say in this worksheet you have the names & hours
Set ws1 = .Worksheets("Sheet1")
'Let say in this worksheet you have the list of names
Set ws2 = .Worksheets("Sheet2")
End With
'Find the last row of the column B with the names from the sheet with names & hours
LastRowB = ws1.Cells(ws1.Rows.Count, "B").End(xlUp).Row
'Find the last row of the column A with the names from the sheet with list of names
LastRowA = ws2.Cells(ws2.Rows.Count, "A").End(xlUp).Row
'Set the range where you want to check if the name appears in
Set rngSearchArea = ws2.Range("A1:A" & LastRowA)
'Loop the all the names from the sheet with names and hours
For i = 6 To LastRowB
If ws1.Range("B" & i).Value <> "" Then
If Application.WorksheetFunction.CountIf(rngSearchArea, "=" & ws1.Range("B" & i).Value) > 0 Then
MsgBox "Value appears"
Exit For
End If
End If
Next i
End Sub
Oh right, I found solution. Thanks everyone for help.
Public Sub Position()
Dim Accol As Integer
Dim Pos As Range
Dim name As Range
ActiveSheet.name = "Sheet1"
Accol = ActiveCell.Column
Set Pos = Cells(6, Accol)
Worksheets("Sheet2").Activate
Worksheets("Sheet2").Range("a1:a5000").Select
Set name = Selection.Find(What:=Pos, After:=ActiveCell, LookIn:=xlValues, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
name.Select
End Sub
Last thing I would like to do which I cannot solve is where do I write automatically script running when I choose Sheet2?

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

Im getting an Object Variable or With Block variable not set error when copying ranges from one sheet to another using Excel VBA

Im trying to copy ranges from my WeeklyDiet sheet to my DietStats sheet.
In cell A1 of WeeklyDiet I enter the number of the week as follows e.g Wk1,Wk2 etc.
I want to paste the data for the week in A1 to whatever row in DietStats.Range(B4:B55) that contains its equivalent weeknumber e.g If Wk2 is in A1 I want the data to be pasted in whatever row contains Wk2 in Range(B4:B55) of the DietStats sheet.
Im getting an Object Variable or With Block variable not set error message.
Sub SaveDietMacrosForWeek()
Dim Ws1 As Worksheet
Dim Ws2 As Worksheet
Dim NR As Long
Set Ws1 = ActiveWorkbook.Sheets("WeeklyDiet")
Set Ws2 = ActiveWorkbook.Sheets("DietStats")
Dim Week As String
Week = Ws1.Range("A1")
NR = Ws2.Range("B4:B55").Find(Week).Row
Ws1.Range("E75:I75").Copy
Ws2.Range("K" & NR).PasteSpecial (xlPasteValues)
Ws1.Range("L75:P75").Copy
Ws2.Range("R" & NR).PasteSpecial (xlPasteValues)
Ws1.Range("S75:W75").Copy
Ws2.Range("Y" & NR).PasteSpecial (xlPasteValues)
Ws1.Range("Z75:AD75").Copy
Ws2.Range("AF" & NR).PasteSpecial (xlPasteValues)
Ws1.Range("AG75:AK75").Copy
Ws2.Range("AM" & NR).PasteSpecial (xlPasteValues)
Ws1.Range("AN75:AR75").Copy
Ws2.Range("AT" & NR).PasteSpecial (xlPasteValues)
Ws1.Range("AU75:AY75").Copy
Ws2.Range("BA" & NR).PasteSpecial (xlPasteValues)
ActiveWorkbook.Save
End Sub
Use Applicatiopn.Match on the whole of column B to get the row number. By passing the return to a variant you will either get the true row number or an error value.
Sub SaveDietMacrosForWeek()
Dim Ws1 As Worksheet
Dim Ws2 As Worksheet
Dim NR As Variant
Set Ws1 = ActiveWorkbook.Sheets("WeeklyDiet")
Set Ws2 = ActiveWorkbook.Sheets("DietStats")
Dim Week As String
Week = Ws1.Range("A1")
NR = Application.Match(Week, Ws2.Range("B:B"), 0)
If Not IsError(NR) Then
Ws2.Range("K" & NR).Resize(1, 5) = Ws1.Range("E75:I75").Value
Ws2.Range("R" & NR).Resize(1, 5) = Ws1.Range("L75:P75").Value
Ws2.Range("Y" & NR).Resize(1, 5) = Ws1.Range("S75:W75").Value
Ws2.Range("AF" & NR).Resize(1, 5) = Ws1.Range("Z75:AD75").Value
Ws2.Range("AM" & NR).Resize(1, 5) = Ws1.Range("AG75:AK75").Value
Ws2.Range("AT" & NR).Resize(1, 5) = Ws1.Range("AN75:AR75").Value
Ws2.Range("BA" & NR).Resize(1, 5) = Ws1.Range("AU75:AY75").Value
End If
ActiveWorkbook.Save
End Sub
I've used direct value transfer instead of your Copy, Paste Special, Values. Direct value transfer is more efficient but requires a target that is the same size as the source.
Not sure what Week is so may need some adjusting.
Sub SaveDietMacrosForWeek()
Dim Ws1 As Worksheet
Dim Ws2 As Worksheet
Dim r As Range
Dim NR As Long
Dim Week As String
Set Ws1 = ActiveWorkbook.Sheets("WeeklyDiet")
Set Ws2 = ActiveWorkbook.Sheets("DietStats")
Week = Ws1.Range("A1")
Set r = Ws2.Range("B4:B55").Find(What:=Week, LookAt:=xlWhole, MatchCase:=False, SearchFormat:=False)
If Not r Is Nothing Then
NR = r.Row
'rest of code
End If
End Sub

use range object as part of a loop

I pasted the entire macro below but this is the important part.
Range("B2:B10000").Value = Range("B2").Offset(-1, 1).Value
Range("D2:D10000").Value = Range("D2").Offset(-1, 1).Value
Range("F2:F10000").Value = Range("F2").Offset(-1, 1).Value
Range("H2:H10000").Value = Range("H2").Offset(-1, 1).Value
It works as is except it is creating unnecessary data because I don't know how to use variable names in a range object. My ranges are currently hard coded such as ("A1:A1000"), when I would like it to be something like ("A1:A & LastRow).
Also I have to explicitly call out column names to copy because the range won't accept a variable name like ("currentColumn & 1:currentColumn & LastRow).
Is there a way to use a varible name as part of a range object so we can use them in loops?
Sub prepareWorkbook()
Dim wbk As Workbook
Set wbk = ThisWorkbook
Dim wks As Worksheet
Set wks = wbk.ActiveSheet
Dim colx As Long
Dim ColumnCount As Long
Dim MySheetName As String
MySheetName = "Import"
LastRow = sht.Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
'copy the worksheet and rename it before editing
Sheets(1).Copy After:=Sheets(1)
ActiveSheet.Name = MySheetName
'identify the Id column and move it to 1st column
Dim answer As Variant
Dim IdColumn As Range
answer = Application.InputBox("Enter Letter of Id column")
If Columns(answer).Column = 1 Then
Else
'cut Id column from current location and insert it at column index 1
Columns(answer).Select
Selection.Cut
Columns("A:A").Select
Selection.Insert Shift:=xlToRight
End If
'trim the PartNumber column of any trailing spaces
Dim c As Range
For Each c In Range("A1:A10000")
c.Value = Application.Trim(Replace(c.Value, Chr(160), Chr(32)))
Next
' insert column every other column
' Loop through number of columns.
ColumnCount = Application.WorksheetFunction.CountA(Rows(1)) * 2
'step 2 means skip every other
For colx = 2 To ColumnCount Step 2
Columns(colx).Insert Shift:=xlToRight
Next
Range("B2:B10000").Value = Range("B2").Offset(-1, 1).Value
Range("D2:D10000").Value = Range("D2").Offset(-1, 1).Value
Range("F2:F10000").Value = Range("F2").Offset(-1, 1).Value
Range("H2:H10000").Value = Range("H2").Offset(-1, 1).Value
wks.Cells.EntireColumn.AutoFit
MsgBox ("Done")
End Sub
Assuming the you are running code in the Worksheet added here:
'copy the worksheet and rename it before editing
Sheets(1).Copy After:=Sheets(1)
ActiveSheet.Name = MySheetName
Also not sure what is the purpose of this code, nevertheless using it for the sample
Range("B2:B10000").Value = Range("B2").Offset(-1, 1).Value
Try this:
Dim lLastRow As Long
lLastRow = wbk.Worksheets(MySheetName).UsedRange.SpecialCells(xlLastCell).Row
Rem This updates only columns B, D, F & H - adjust as needed
For colx = 2 To 8 Step 2
With wbk.Worksheets(MySheetName)
Rem Creates Range as Range(Cells(rIni,cIini), Cells(rEnd,cEnd))
rem Corresponding code for "Range("B2:B10000").Value = Range("B2").Offset(-1, 1).Value" (see comment above)
Range(.Cells(2, colx), .Cells(lLastRow, colx)) = .Cells(2, colx).Offset(-1, 1).Value
End With: Next
Something like:
Dim LastRow As Long
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
Range("B2:B" & LastRow).Value = Range("B2").Offset(-1, 1).Value
Range("D2:D" & LastRow).Value = Range("D2").Offset(-1, 1).Value
Range("F2:F" & LastRow).Value = Range("F2").Offset(-1, 1).Value
Range("H2:H" & LastRow).Value = Range("H2").Offset(-1, 1).Value
Although this answer won't be applied to your situation, I feel like this could help answer some questions you have in there.
When specifying a range, you can separate the column (letter) and row (number) and use your own variables.
In a for loop, this could look like
for i = 1 to 100
Range("A" & i).Value = Range("A"&i).Offset(, 1).Value
next
You can also determine the number of the row of the selected cell using:
dim RowNb as long
RowNb = (ActiveCell.Row)
This also applies to columns, and can be used in a loop like I mentionned at the start.
The one thing that was conspicuous by its absence in your description was any mention of the nature of the data in the worksheet. You mentioned A1 briefly but your range value assignments started at row 2 so it may be inferred that row 1 contains column header labels.
Sub prepareWorkbook()
Dim wbk As Workbook, wks As Worksheet
Dim colx As Long
Dim lc As Long, lr As Long
Dim MySheetName As String
Set wbk = ThisWorkbook 'no idea what this does
Set wks = wbk.ActiveSheet 'no idea what this does
MySheetName = "Import"
'no idea what this does or what sht is
'LastRow = sht.Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
'copy the worksheet and rename it before editing
Sheets(1).Copy After:=Sheets(1)
With Sheets(2)
.Name = MySheetName
If CBool(Application.CountIf(.Rows(1), "PartNumber")) Then
colx = Application.Match("PartNumber", .Rows(1), 0)
Else
colx = .Range(Application.InputBox("Enter Letter of Id column") & 1).Column
End If
If .Columns(colx).Column > 1 Then
'cut Id column from current location and insert it at column index 1
.Columns(colx).Cut
.Columns(1).Insert Shift:=xlToRight
End If
'quickest way to trim trailing spaces is with Text-to-Columns, Fixed Width
With .Columns(1)
.TextToColumns Destination:=.Cells(1), DataType:=xlFixedWidth, FieldInfo:=Array(0, 1)
End With
' insert column every other column (working backwards toward A1)
For lc = .Cells(1, Columns.Count).End(xlToLeft).Column To 2 Step -1
.Columns(lc).Insert Shift:=xlToRight
Next lc
For lc = (.Cells(1, Columns.Count).End(xlToLeft).Column - 1) To 2 Step -2
'let's put the row-by-row value in instead of a single value into all cells
lr = .Cells(Rows.Count, lc + 1).End(xlUp).Row
With .Cells(2, lc).Resize(lr - 1, 1)
.Cells = .Offset(-1, 1).Value
.EntireColumn.AutoFit
End With
Next lc
End With
Set wbk = Nothing
Set wks = Nothing
End Sub
Explanations as comments in code.

Resources