I need to copy data from sheet1 (P25:Y103) to sheet2 within B63:K1562.
Sheet1 will have new data every time. This should be copied to sheet2.
In Sheet2, B1563:K65536 has been used for other details. So need to paste the data within that range and not overwrite the existing values.
I have code to copy the contents but it will overwrite the existing data.
Sub CopyDatetoSameWorkBook()
Dim rgSource As Range, rgDestination As Range, X As Range
Dim Length As Long
Length = Cells(25, 2).End(xlDown).Row
Set rgSource = ThisWorkbook.Worksheets("Sheet1").Range("P25:Y" & Length)
Set rgDestination = ThisWorkbook.Worksheets("Sheet2").Range("B63")
rgSource.Copy
rgDestination.PasteSpecial xlPasteValues
Set rgSource = ThisWorkbook.Worksheets("Sheet1").Range("Z25:AG" & Length)
Set rgDestination = ThisWorkbook.Worksheets("Sheet2").Range("N63")
rgSource.Copy
rgDestination.PasteSpecial xlPasteValues
ActiveWorkbook.Save
MsgBox "Sheet1 Data Has been copied to Sheet2 Tabsheet"
End Sub
Thanks for your answers. I could achieve the things with below mentioned source code.
We have formed the Table as Range then found the last row in that Table.
Sub CopyDatetoSameWorkBook()
Dim copyrange As Range
Dim rgSource As Range, rgDestination As Range, X As Range
Dim Length As Long
Length = Cells(25, 2).End(xlDown).Row
Set copyrange = LastRowInExcelTable("Sheet1", "Table1")
Set rgSource = ThisWorkbook.Worksheets("Sheet2").Range("P25:Y" & Length)
Set rgDestination = ThisWorkbook.Worksheets("Sheet1").Range("B" & copyrange.Row)
rgSource.Copy
rgDestination.PasteSpecial xlPasteValues
Set rgSource = ThisWorkbook.Worksheets("Sheet2").Range("Z25:AG" & Length)
Set rgDestination = ThisWorkbook.Worksheets("Sheet1").Range("N" & copyrange.Row)
rgSource.Copy
rgDestination.PasteSpecial xlPasteValues
ActiveWorkbook.Save
MsgBox "Sheet1 Data Has been copied to Sheet2"
End Sub
Function LastRowInExcelTable(mysheet As String, mytable As String) As Range
Dim cell As Range
Dim ws As Worksheet
Set ws = Sheets(mysheet)
'Assuming the name of the table is "Table1"
Set LastRowInExcelTable = ws.ListObjects(mytable).Range.Columns(2).Cells.Find("", SearchOrder:=xlByRows, SearchDirection:=xlNext)
ActiveWorkbook.Save
End Function
If you definitely know the range for 'Sheet1' I'd suggest not to use Length. You can select the range directly and copy only values of these sells.
Sub CopyDatetoSameWorkBook()
Dim rgSource As Range, rgDestination As Range, X As Range
Set rgSource = ThisWorkbook.Worksheets("Sheet1").Range("P25:R28")
Set rgDestination = ThisWorkbook.Worksheets("Sheet2").Range("B63")
rgSource.Copy
rgDestination.PasteSpecial
ActiveWorkbook.Save
MsgBox "Sheet1 Data Has been copied to Sheet2 Tabsheet"
End Sub
Please excuse me i am under immense cruelty where I currently work and trying to write to you. But I have got it to working exactly how you describe.
The final trick was to figure out a way of tricking excel to pasting into row 63 first instead of row 1 or row 2 (or elsewhere as it did at times). I was looking for a way to fool excel into treating row 63 as row 1 or row 2 (beneath the header) but there doesn't seem to be..
I had got the continuous pasting right with no errors, but setting it from row 63 in the first instance was leading me stuck. It worked fine from row 1 or 2, and appending itself, or indeed from anywhere else it started, but couldn't figure out a dynamic adaptable solution for the starting row.
One trick I realized is just to check if sheet2 is empty or not. And to split the pasting by that condition: to check if Sheet2 is empty or not. For that I am checking if B63 (or similar) already has data in or not (is blank or IsEmpty). That was it.
I thought there was an easy isblank test for a sheet in excel which would be easier (and sorted us out quickly no problem), but again there isn't. Doesn't seem to be. Best and closest alternative is to check if cell B63 is empty or not, or adapt one of these functions and loops for your purposes Here , Here , Here and Here. as ive also done.
So now the whole process you want works as you want. I've tested numerous times, with changing sheet1 data and running, growing and changing the table-ranges data to paste, and running again and again to make sure it work. etc etc. It builds/grows your insert table in sheet 2 from row 63 onwards without hassle. It works to a tee.
Also prior, I had to change your last rows, as it was wasn't selecting the sheet1 data correctly. (xlCellTypeLastCell).Row works far better.
Now it works as you want.
I've also done a version with If Application.WorksheetFunction.CountA("B63:S64") = 0 Then which works better to check if a range is blank and decide where or if not to paste the data. CountA tests if the range B63:...wherever you want, has or hasn't got anything in it, and pastes sheet1 date into sheet2 from row63 if true, or appends to the end of the last filled row in sheet2 otherwise.
Sub CopyDatetoSameWorkBook13()
Dim rgSource As Range, rgDestination As Range, X As Range
ThisWorkbook.Worksheets("Sheet2").Activate
ActiveSheet.Cells(2, 63).Select
Dim Length As Long
'Length = 0 does nothing changes nothing
Length = ThisWorkbook.Worksheets("Sheet1").Cells(25, 2).SpecialCells(xlCellTypeLastCell).Row + 25
Dim length2 As Long
'length2 = 0 does nothing changes nothing
'length2 = 63 - ditto . absolute bs.
length2 = ThisWorkbook.Worksheets("Sheet2").Cells.SpecialCells(xlCellTypeLastCell).Row + 63
Set rgSource = ThisWorkbook.Worksheets("Sheet1").Range("P25:Y" & Length)
If IsEmpty(Range("B63").Value) = True Then
Set rgDestination = ThisWorkbook.Worksheets("Sheet2").Range("B63:K" & Length)
Else:
Set rgDestination = ThisWorkbook.Worksheets("Sheet2").Range("B" & 1 + length2 - 63) 'K
End If
rgSource.Copy
rgDestination.PasteSpecial xlPasteValues
Set rgSource = ThisWorkbook.Worksheets("Sheet1").Range("Z25:AG" & Length)
If IsEmpty(Range("N63").Value) = True Then
Set rgDestination = ThisWorkbook.Worksheets("Sheet2").Range("N63:S" & length2)
Else:
Set rgDestination = ThisWorkbook.Worksheets("Sheet2").Range("N" & 1 + length2 - 63) 'U
End If
rgSource.Copy
rgDestination.PasteSpecial xlPasteValues
Length = 0 '' dont think i need this any more
length2 = 0 '' ditto
ActiveWorkbook.Save
MsgBox "Sheet1 Data Has been copied to Sheet2 Tabsheet"
End Sub
'rgDestination.Range("N63:U" & sourcelastrow + 62).Value
you can replace the If-conditions, If IsEmpty(Range("B63").Value) = True Then and If IsEmpty(Range("N63").Value) = True Then , with :
If IsEmpty(Range("B63").Value) = True Then
and
If IsEmpty(Range("N63").Value) = True
for a more sturdy check.
Please test it and let me know if you need anything more or have any issues with it.
(Apologies for the very rushed child like message to you.)
Here is my screen shots and working to show you it works.
I like to use a FindRow to do it. It works by finding the first blank row and then the minus 1 represents the row above (the last row with data) so you may need to change the range to a column which will always be complete (like a unique identifier)
I move the data using destinationrange.value = sourcerange.value
I hope this makes sense but if you have any questions about what I've done, please let me know!
Sub CopyDatetoSameWorkBook()
Dim rgSource As excel.worksheet
dim rgDestination As excel.worksheet
dim X As Range
Dim Length As Long
Dim FindObject as object
Dim LastRow as long
Set rgSource = ThisWorkbook.Worksheets("Sheet1")
Set rgDestination = ThisWorkbook.Worksheets("Sheet2")
'find last row of sheet1
Set FindRow = rgSource.Range("P25:P100000").Find(What:="", LookIn:=xlValues)
LastRow = FindRow.Row - 1
'move the data from sheet1 to sheet2
rgDestination.range("B63:K" & lastrow + 62).value = rgSource.range("P25:Y" & LastRow).value
'Set rgSource = ThisWorkbook.Worksheets("Sheet1").Range("Z25:AG" & Length)
Set rgDestination = ThisWorkbook.Worksheets("Sheet2").Range("N63")
'find last row of sheet1
Set FindRow = rgSource.Range("Z25:Z100000").Find(What:="", LookIn:=xlValues)
LastRow = FindRow.Row - 1
'move the data from sheet1 to sheet2
rgDestination.range("N63:U" & sourcelastrow + 62).value = rgSource.range("Z25:AG" & LastRow).value
ActiveWorkbook.Save
MsgBox "Sheet1 Data Has been copied to Sheet2 Tabsheet"
End Sub
I don't know if you are still looking for a other answer, probably not , but as I couldnt get your code to work without a table, and also tried terrifiedjelly's - her versions didn't work, either the first time or the 2nd time, I kept at it.
But I got my version to work for your cases.
I'm finding the final filled row through a worksheet function to find the first blank cell in a range.
FinalRow = Evaluate("=MATCH(TRUE,ISBLANK(U63:U1563),0)+62")
And wrapping whole code in a if -final-available-row is still empty.
I used Destination sheet2 Column U to check, as in my test data that was the only column in my sheet1 with no blank cells in it (others did have blanks here or there - deliberrtely - for testing). So If all of your cells from your table in sheet1 are properly formatted and filled with something, no problem. But if they contain some empty cells, you need to check against a column that definitely does not contain a blank cell. Otherwise it will hilter-skew results and end up pasting source data in the wrong place. Something to bare in mind: Have atleast 1 properly formatted column
So, ensure the data your copying over has at atleast 1 column with no blank cells (or alternatively fill a helper column to the side of the editabe and growing table to denote table exists and check by that columns first blank cell). Otherwise you will have a problem. you could also concatonate your sheet1 table rows somewhere and check the first empty row value in such a off table column or array. Key is to check for first non blank cell by a column you are sure will always be filled or have data as its being produced/grows.
The whole thing works for me now - exactly as you want - as long as 1 column in the selection/ range/ table (Column U in my case) has no blank cells while your building 63 onwards.
So, Just wanted to inform you that Its working for me exactly as you want.
Sub CopyDatetoSameWorkBook15()
Dim rgSource As Range, rgDestination As Range, X As Range
ThisWorkbook.Worksheets("Sheet2").Activate
ActiveSheet.Cells(2, 63).Select
Dim Length As Long
Length = ThisWorkbook.Worksheets("Sheet1").Cells(25, 2).SpecialCells(xlCellTypeLastCell).Row + 25
Dim length2 As Long
length2 = ThisWorkbook.Worksheets("Sheet2").Cells.SpecialCells(xlCellTypeLastCell).Row + 63
FinalRow = Evaluate("=MATCH(TRUE,ISBLANK(U63:U1563),0)+62") '- important. Finds the first blank cell in range 63-1563
If IsEmpty(Range("B1564").Value) = False Then
MsgBox "Paste range is full. Please clear data and try again."
'i presume you anticipate filling until 1563 and that above 1563 you already have non empty filled cells . This will stop program when you fill the range or go over it.
'Could have used if countA 1563 row >0 instead. would be more robust.
Else:
Set rgSource = ThisWorkbook.Worksheets("Sheet1").Range("P25:Y" & Length)
If IsEmpty(Range("B63").Value) = True Then
Set rgDestination = ThisWorkbook.Worksheets("Sheet2").Range("B63:K" & Length)
Else:
Set rgDestination = ThisWorkbook.Worksheets("Sheet2").Range("B" & FinalRow) 'K ' + length2 - 63
End If
rgSource.Copy
rgDestination.PasteSpecial xlPasteValues
Set rgSource = ThisWorkbook.Worksheets("Sheet1").Range("Z25:AG" & Length)
If IsEmpty(Range("N63").Value) = True Then
Set rgDestination = ThisWorkbook.Worksheets("Sheet2").Range("N63:S" & length2)
Else:
Set rgDestination = ThisWorkbook.Worksheets("Sheet2").Range("N" & FinalRow) 'U ' + length2 - 63
End If
rgSource.Copy
rgDestination.PasteSpecial xlPasteValues
'Length = 0
'length2 = 0
ActiveWorkbook.Save
MsgBox "Sheet1 Data Has been copied to Sheet2 Tabsheet"
End If
End Sub
( Tomorrow I willsearch for a more robust worksheet function to check for last blank row in a 2d-range , so that I don't have to rely on a particular column; or just re-use the finalrow formula in a loop or with &'s over columns B to U which will do the trick)
Related
I have what I thought would be a simple script, but I have some some strange results.
Goal: Identify specific IDs in a SOURCE sheet using a list of IDs on a Translator Sheet. When found, copy the entire row to and OUTPUT sheet.
The output has strange results that I can't figure out.
Returns all results instead of the limited list. AND results are in weird groupings. (First result is on row 21 and only has 9 rows of data, the next group has 90 rows of data, starting on row 210, then blank rows, then 900 rows of data, etc.
Results do not start in row 2.
Full code is below attempts:
Attempts:
I first searched the SOURCE sheet based on one ID that was hard coded as a simple test and it worked. but when I changed the code to search a range (z21:z), two things happened: 1, it returns everything in the Source file in multiples of 9 as stated above, AND as you can imagine, the time to complete skyrocketed from seconds to minutes. I think I missed a add'l section of code to identify the range??
Old Code:
For K = 1 To xRg.Count
If CStr(xRg(K).Value) = Worksheets("D62D627EB404207DE053D71C880A3E05") Then
xRg(K).EntireRow.Copy Destination:=Worksheets("Output").Range("A2" & J + 1)
J = J + 1
End If
New code:
For K = 1 To xRg.Count
If CStr(xRg(K).Value) = Worksheets("Translator").Range("z21:z" & I)** Then
xRg(K).EntireRow.Copy Destination:=Worksheets("Output").Range("A2" & J + 1)
J = J + 1
End If
1a. I believe one issue is that the Translator list has duplicates. Second, it is searching the entire column Z. Second issue may be that The list in Translator is generated via a formula in column Z, thus if the formula is false, it will insert a "" into the cell. I seek the code to NOT paste those rows where the cell content is either a "" or is a true blank cell. Reason: The "" will cause issues when we try to load the Output file into a downstream system because it is not a true blank cell.
Results in wrong location: When the script is complete, my first result does not start on Row 2 as expected. I thought the clear contents would fix this, but maybe a different clear function is required? or the clear function is in the wrong place? Below screenshot shows how it should show up. It is in the same columns but doesn't start until row 21.
enter image description here
Slow code: I have a command that copies and pastes of the first row from SOURCE to OUTPUT. My code is cumbersome. There has to be an easier way. I am doing this copy and paste just in case the source file adds new columns in the future.
Worksheets("Output").Cells.ClearContents
Sheets("SOURCE").Select
Rows("1:1").Select
Selection.Copy
Sheets("Output").Select
Rows("1:1").Select
ActiveSheet.Paste
Thank you for all your help.
Option Explicit
Sub MoveRowBasedOnCellValuefromlist()
'Updated by xxx 2023.01.18
Dim xRg As Range
Dim xCell As Range
Dim I As Long
Dim J As Long
Dim K As Long
I = Worksheets("SOURCE").UsedRange.Rows.Count
J = Worksheets("Output").UsedRange.Rows.Count
If J = 1 Then
If Application.WorksheetFunction.CountA(Worksheets("Output").UsedRange) = 0 Then J = 0
End If
Worksheets("Output").Cells.ClearContents
Sheets("SOURCE").Select
Rows("1:1").Select
Selection.Copy
Sheets("Output").Select
Rows("1:1").Select
ActiveSheet.Paste
Set xRg = Worksheets("SOURCE").Range("B2:B" & I)
On Error Resume Next
Application.ScreenUpdating = False
'NOTE - There are duplicates in the Translator list. I only want it to paste the first instance.
'Otherwise, I need to create an =Unique() formula and that seems like unnecessary work.
For K = 1 To xRg.Count
If CStr(xRg(K).Value) = Worksheets("Translator").Range("z21:z" & I) Then
xRg(K).EntireRow.Copy Destination:=Worksheets("Output").Range("A2" & J + 1)
J = J + 1
End If
Next
Application.ScreenUpdating = True
End Sub
Try this out - using Match as a fast way to check if a value is contained in your lookup list.
Sub MoveRowBasedOnCellValuefromlist()
Dim c As Range, wsSrc As Worksheet, wsOut As Worksheet, wb As Workbook
Dim cDest As Range, wsTrans As Worksheet, rngList As Range
Set wb = ThisWorkbook 'for example
Set wsSrc = wb.Worksheets("SOURCE")
Set wsOut = wb.Worksheets("Output")
Set wsTrans = wb.Worksheets("Translator")
Set rngList = wsTrans.Range("Z21:Z" & wsTrans.Cells(Rows.Count, "Z").End(xlUp).Row)
ClearSheet wsOut
wsSrc.Rows(1).Copy wsOut.Rows(1)
Set cDest = wsOut.Range("A2") 'first paste destination
Application.ScreenUpdating = False
For Each c In wsSrc.Range("B2:B" & wsSrc.Cells(Rows.Count, "B").End(xlUp).Row).Cells
If Not IsError(Application.Match(c.Value, rngList, 0)) Then 'any match in lookup list?
c.EntireRow.Copy cDest
Set cDest = cDest.Offset(1) 'next paste row
End If
Next c
Application.ScreenUpdating = True
End Sub
'clear a worksheet
Sub ClearSheet(ws As Worksheet)
With ws.Cells
.ClearContents
.ClearFormats
End With
End Sub
I've been trying to figure this out, but had to luck.
I have a list that starts on A5 on worksheet2. I need to Vlookup each item from A5 down until the last cell in column A (list will never be the same size). The data/info will be on worksheet1. Then paste (as values) the data starting in cell C5 and until the last corresponding cell in column A.
The data on worksheet1 will most likely always been in columns A:L, but this could change so i'm hoping to make it dynamic where the code can know which column the data ends. Data will always start on A1.
I'm not sure how to loop this. Looking to achieve this through VBA using a macro-enabled button I started programming.
Thanks in advance!
sub lookup
dim x as long, lastrow as long
lastrow = Sheet2.cells(rows.count,1).end(xlup).row
for x = 5 to lastrow
Sheet2.Range("C" & x) = worksheetfunction.xlookup arg1:=sheet2.range("A" & x), _
arg2:= Sheet1.Range("A:A"), arg3:=Sheet1.range("B:B")
next x
end sub
I think you are going to need to solidify which column you are wanting to return in order to make the VBA simple. Depending on the column I think you can loop through until you find the header you are looking for, but it would be better if it was always in the same place.
This code says, for the sheet length of sheet2, starting in c5 xlookup your key, find it on sheet 1, and return the column that has your data.
It seems that you are looking for a row number in Worksheet1 and then intend to transfer all available data from that row. That would be a job for the MATCH worksheet function or Find in VBA. Please try the code below.
Sub MatchAndCopy()
' 213
Dim Rng As Range ' source data
Dim Arr As Variant ' one row of data
Dim Crit As Variant ' match criterium
Dim Fnd As Range ' match found
Dim R As Long ' loop counter: rows
Dim Spike As String ' collecting failures
Set Rng = Worksheets("Sheet1").UsedRange
Application.ScreenUpdating = False ' speed up execution
With Worksheets("Sheet2")
For R = 5 To .Cells(.Rows.Count, "A").End(xlUp).Row
Crit = .Cells(R, "A").Value
Set Fnd = Rng.Columns(1).Find(Crit, LookIn:=xlValues, LookAt:=xlWhole)
If Fnd Is Nothing Then
If Len(Spike) Then Spike = Spike & vbCr
Spike = Spike & String(5, " ") & """" & Crit & """ in row " & R
Else
Arr = Fnd.Offset(0, 1).Resize(1, Rng.Columns.Count - 1).Value
.Cells(R, 2).Resize(1, UBound(Arr, 2)).Value = Arr
End If
Next R
End With
Application.ScreenUpdating = True
If Len(Spike) Then
Spike = "Transfer of the following items failed." & vbCr & Spike
Else
Spike = "Data were transferred successfully and without errors."
End If
MsgBox Spike, vbInformation, "Transfer report"
End Sub
I'm sure this is possible, im just not sure what the code should be. i have 2 sheets: (1)Component which has all the Component Names where an analyst got marked down on, including dates of when the call occurred, and (2)Calculator, which counts the number of times a specific component appeared in a specific week number.
ive created a code which gets the distinct Component Names from the Component Sheet, and then copies and transpose them to the Calculator sheet. all the Component Names are in Row 1 starting from Column D1 then goes to E1, F1, and so on. i want row 2 to display the count or the number of times the component(listed in row 1) appeared in a week.
The code i have only works for columns, i do not know how to make it get the non-empty values of an entire row.
'//here the code i used to transpose Distinct Components from the Component sheet to the Calculator Sheet
Public Sub GetDistinctComponents()
Application.ScreenUpdating = False
Dim lr As Long
lr = Sheets("Components Data").Cells(Rows.Count, "F").End(xlUp).Row
Sheets("Calculator").Unprotect Password:="secret"
Sheets("Components Data").Range("F1:F" & lr).AdvancedFilter Action:=xlFilterCopy, _
CopyToRange:=ActiveSheet.Range("DW1"), Unique:=True
With ThisWorkbook.Worksheets("Calculator")
.Range(.Range("DW1"), .Range("DW1").End(xlDown)).Copy
.Range("DX1").PasteSpecial xlPasteValues, Transpose:=True
.Columns("DW").EntireColumn.Delete
End With
Sheets("Calculator").Protect Password:="secret", DrawingObjects:=False
End Sub
Here's my Component sheet
And below is my Calculator sheet. as you can see, the code to transpose the distinct Components works fine. i just do not know how to get the value of Row 1 starting from DX so i can store it in a variable which i will use in counting the number of times that component appeared in a week . I'm thinking it should go like this
Component = wsCalculator.Cells(i, "D").Value
But this code only works if i want to get the Values of all cells in Column D, not the values of the cells next to D1
and here's the code i currently have
Public Sub CountComponent()
Application.ScreenUpdating = False
Sheets("Calculator").Unprotect Password:="secret"
Set wsComponentData = Sheets("Components Data")
Set wsCalculator = Sheets("Calculator")
Dim ComponentCount As Integer
'//Get the index of the last filled row based on column A
LastComponentRowIndex = wsComponentData.Cells(Rows.Count, "A").End(xlUp).Row
'//Get Range for ComponentData
Set ComponentRange = wsComponentData.Range("F2:F" & LastComponentRowIndex)
'//Get the index of the last filled row based on column C
LasttotalauditRowIndex = wsCalculator.Cells(Rows.Count, "C").End(xlUp).Row
'//Get range for Calculator
Set MyRange = wsCalculator.Range("C2:C" & LasttotalauditRowIndex)
TotalCalls = WorksheetFunction.Sum(MyRange)
'//Looping through all filled rows in the Components Data sheet
For i = 2 To wsCalculator.Cells(Rows.Count, "A").End(xlUp).Row
'//Get Component from cell in column "DW"
'Component = wsCalculator.Cells(i, "DW").Value
'//Count the # of calls that got hit in the corresponding Component
If wsCalculator.Cells(i, "DW").Value <> "" Then
ComponentCount = Application.WorksheetFunction.CountIf( _
ComponentRange, component)
wsCalculator.Cells(i, "DX").Value = ComponentCount
End If
Next
End Sub
I'll take a crack at this. I'm not 100% sure what you are doing, but I'm going to assume you will have soon calculations in cells D2, down, and to the right. Is that correct? Try this small code sample to copy from D2 (down and right) on the "Components Data" sheet, and transpose to your "Calculator" sheet.
Sub TransposeThis()
Set Rng = Sheets("Components Data").Range("D2:D7") 'Input range of all fruits
Set Rng_output = Sheets("Calculator").Range("B2") 'Output range
For i = 1 To Rng.Cells.Count
Set rng_values = Range(Rng.Cells(i).Offset(0, 1), Rng.Cells(i).End(xlToRight)) 'For each fruit taking the values to the right which need to be transposed
If rng_values.Cells.Count < 16000 Then 'To ensure that it doesnt select till the right end of the sheet
For j = 1 To rng_values.Cells.Count
Rng_output.Value = Rng.Cells(i).Value
Rng_output.Offset(0, 1).Value = rng_values.Cells(j).Value
Set Rng_output = Rng_output.Offset(1, 0) 'Shifting the output row so that next value can be printed
Next j
End If
Next i
End Sub
Before:
After:
If I got something wrong, post your feedback, and I'll adjust the code to suit your needs.
The code below is your own code, in part, which I commented, and of my own making for those parts where you seemed to have lost your way.
Public Sub CountComponent()
' Locations:-
Dim WsComp As Worksheet
Dim WsCalc As Worksheet
Dim CompRng As Range ' column A
Dim CalcRng As Range ' Calculator!D1:D?)
Dim Rt As Long ' Target row (in WsCalc)
' Helpers:-
Dim Cell As Range
Dim R As Long
Set WsComp = Sheets("Components Data")
Set WsCalc = Sheets("Calculator")
WsCalc.Unprotect Password:="secret"
Application.ScreenUpdating = False
'//Get the index of the last filled row based on column A
With WsComp
' observe the leading period in ".Rows.Count"
'LastComponentRowIndex = .Cells(.Rows.Count, "A").End(xlUp).Row
'//Get Range for ComponentData
'Set CompRng = .Range("A2:A" & LastComponentRowIndex)
' avoids the need for decalring LastComponentRowIndex
Set CompRng = .Range(.Cells(2, "A"), _
.Cells(.Rows.Count, "A").End(xlUp))
End With
With WsCalc
' set a range of all criteria to look up
Set CalcRng = .Range(.Cells(1, "D"), _
.Cells(1, .Columns.Count).End(xlToLeft))
'//Get the index of the last non-empty row in column B
' loop through all rows in WsCalc
For R = .Cells(.Rows.Count, "B").End(xlUp).Row To 2 Step -1
If Val(.Cells(R, "B").Value) Then ' presumed to be a week number
'//Loop through all audit criteria
For Each Cell In CalcRng
With .Cells(R, Cell.Column)
.Value = WorksheetFunction.CountIfs( _
CompRng, Cell.Value, _
CompRng.Offset(0, 1), WsCalc.Cells(R, "B").Value)
.NumberFormat = "0;-0;;" ' suppress display of zero
End With
Next Cell
End If
.Cells(R, "C").Value = WorksheetFunction.Sum(CalcRng.Offset(R - 1))
Next R
End With
Application.ScreenUpdating = True
End Sub
Frankly, I couldn't understand all of your intentions. I presumed that column B in your Calculations sheet would contain a week number and that this week number would also be found in the Components Data (in column B). If so, you would be counting the occurrences of each component by week, and that is what I programmed.
I think it doesn't matter if I got that part wrong. Your main question was how to look up each of the Components in Calculations!D1:??. That method is very well demonstrated in my above answer and I feel confident you will be able to transplant the useful bits to your own project. Good luck!
I suggest taking a look at VBA dictionaries. In this case, you could store each component as a key and for the value you can accumulate the number of occurrences of the component for a given week.
I don't have a VBA editor available on my computer at the moment to test this, but it would likely look something along the lines of what I've got below. Also, I'll admit that I may not have fully understood the layout of your sheets, but the general principle here will definitely apply.
For a pretty full overview of dictionaries in VBA, here's a good resource that'd I'd recommend: https://excelmacromastery.com/vba-dictionary/
Public Sub CountComponent()
Application.ScreenUpdating = False
Sheets("Calculator").Unprotect Password:="secret"
Set wsComponentData = Sheets("Components Data")
Set wsCalculator = Sheets("Calculator")
'//Get the index of the last filled row based on column A
LastComponentRowIndex = wsComponentData.Cells(Rows.Count, "A").End(xlUp).Row
'//Get Range for ComponentData
Set ComponentRange = wsComponentData.Range("A2:A" & LastComponentRowIndex)
'//Get the index of the last filled row based on column C
LasttotalauditRowIndex = wsCalculator.Cells(Rows.Count, "C").End(xlUp).Row
'//Get range for Calculator
Set MyRange = wsCalculator.Range("C2:C" & LasttotalauditRowIndex)
TotalCalls = WorksheetFunction.Sum(MyRange)
'// Declare a new dictionary
dim componentDict as New Scripting.Dictionary
'// First loop through the Calculator sheet to get each component
'// and set initial value to zero
dim i as Long, lastCalcColumn as Long
lastCalcColumn = wsCalculator.Cells(1, Columns.count).end(xlToLeft).Column
for i = 4 to lastCalcColumn
'// Adding each item to dictionary, a couple of ways to write this,
'// but this is probably the easiest
componentDict(wsCalculator.Cells(i, 1).Value) = 0
next i
'//Looping through all filled rows in the Components Data sheet
'// I changed this to loop through each row in your component sheet
'// So that we can accumulate the total occurences
dim current_key as String
For i = 2 To LastComponentRowIndex
If wsComponentData.Range("G" & i).Value <> "" Then
'// assuming component names are in the "G" column
'// change this as needed
current_key = wsComponentData.Range("G" & i).Value
componentDict(current_key) = componentDict(current_key) + 1
end if
Next i
'// now back to the Calculator sheet to enter the values
for i = 4 to lastCalcColumn
current_key = wsCalculator.Cells(i, 1).Value
wsCalculator.Cells(i, 2).Value = componentDict(current_key)
next i
End Sub
I'm looping over values in Column B of the current worksheet. If the value's length is 8 characters, copy the WHOLE row to another sheet.
It is kind of working, but I'm missing around a hundred rows that should have been copied.
I guess it's to do with the format of the cell values in Column B. There are some that are just Text headers which will definitely not meet the criteria. The ones that it should copy are all in this format (Column B):
6008571X
60088242
....
The rows I'm interested in have 8 characters in Column B. The problem is that some of them might be formatted as numbers some as text (or perhaps preceded by ').
Sub aims()
Dim i As Long
'Get the address of the first non blank cell in Row B from the bottom
MyFirstBlankAddress = Range("B1048576").End(xlUp).Offset(1, 0).Address
'Extract the number from the address to get the row number
MyRowNumber = Split(MyFirstBlankAddress, "$")(2)
For i = 1 To MyRowNumber
With Range("B" & i)
If Len(.Value) = 8 Then .EntireRow.Copy Destination:=Sheets("Sheet2").Range("A" & Rows.Count).End(xlUp).Offset(1)
End With
Next i
End Sub
I was expecting 410 rows copied, while only 276 got copied.
EDIT: I have been reading your answers/suggestions and testing stuff. I've found out that the problem lies elsewhere. My original code identifies the rows in a correct way, it's something to do with copying.
If I change my code to just highlight the matching rows, it matches all the right rows:
If Len(.Value) = 8 Then .EntireRow.Interior.Color = 5296274
I'm sure there is a better way to do the copy/paste, which is where your issue is, but the below works.
Sub aims()
Dim i As Long
Dim vLastRow As Long
Dim s2 As Long
'find last row in sheet, or you could change to find last row in specified column
'Example: Cells = Columns(column number or letter), Cells(1, 1) = Cells(1, column number)
vLastRow = Cells.Find(what:="*", after:=Cells(1, 1), searchorder:=xlByRows, searchdirection:=xlPrevious).Row
s2 = 1
Application.ScreenUpdating = False
For i = 1 To vLastRow
If Trim(Len(CStr(Cells(i, 2)))) = 8 Then
Rows(i).EntireRow.Copy Destination:=Sheets(2).Range(Cells(s2, 1).Address)
s2 = s2 + 1
End If
Next i
Application.ScreenUpdating = True
End Sub
You can try something like this. The below code attempts to copy everything at once instead of having many instances of copy/paste. The two tests are seeing if the trimmed value has a character length of 8 OR if the trimmed value has a character length of 9 but the last character is the apostrophe. If either of these criteria are met, we will add that cell to a Union.
Once the code has looped through all rows, it will copy the entire union at all once
Option Explicit
Sub shooter()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1") '<-- Update
Dim LR As Long, i As Long, Add As Boolean, CopyMe As Range
Dim x As Range
LR = ws.Range("B" & ws.Rows.Count).End(xlUp).Row
For Each x In ws.Range("B2:B" & LR)
Add = False
If Len(Trim(x)) = 8 Then
Add = True
ElseIf Len(Trim(x)) = 9 And Right(Trim(x), 1) = "'" Then
Add = True
End If
If Add Then
If Not CopyMe Is Nothing Then
Set CopyMe = Union(CopyMe, x)
Else
Set CopyMe = x
End If
End If
Next x
If Not CopyMe Is Nothing Then
CopyMe.EntireRow.Copy Destination:=Sheets(2).Range(“A1”)
End If
End Sub
I have an excel sheet with around 200 work sheets each containing a list of products sold to a company.
I need to add
A total at the bottom of row D-G where the bottom can be a different value. I.E. E4
below the total a formula based on the total. I.E. if E4 (being the bottom of the above row) is below $999 the display text "samples", if between 1000-3000 then multiply E4 by 2%, 3001-7500 x 5% etc.
I need to be able to add it to the entire workbook easily using vba. Since I must do this to numerous ss it would literally save me 15-20 hours a month.
Edit:
So I have something that seems to be the right path.
Sub Split_Worksheets()
Dim rRange As Range, rCell As Range
Dim wSheet As Worksheet
Dim wSheetStart As Worksheet
Dim strText As String
Set wSheetStart = ActiveSheet
wSheetStart.AutoFilterMode = False
'Set a range variable to the correct item column
Set rRange = Range("A1", Range("A65536").End(xlUp))
'Delete any sheet called "UniqueList"
'Turn off run time errors & delete alert
On Error Resume Next
Application.DisplayAlerts = False
Worksheets("UniqueList").Delete
'Add a sheet called "UniqueList"
Worksheets.Add().Name = "UniqueList"
'Filter the Set range so only a unique list is created
With Worksheets("UniqueList")
rRange.AdvancedFilter xlFilterCopy, , _
Worksheets("UniqueList").Range("A1"), True
'Set a range variable to the unique list, less the heading.
Set rRange = .Range("A3", .Range("A65536").End(x2Up))
End With
On Error Resume Next
With wSheetStart
For Each rCell In rRange
strText = rCell
.Range("A1").AutoFilter 1, strText
Worksheets(strText).Delete
'Add a sheet named as content of rCell
Worksheets.Add().Name = strText
'Copy the visible filtered range _
(default of Copy Method) and leave hidden rows
.UsedRange.Copy Destination:=ActiveSheet.Range("A1")
ActiveSheet.Cells.Columns.AutoFit
Next rCell
End With
With wSheetStart
.AutoFilterMode = False
.Activate
End With
On Error GoTo 0
Application.DisplayAlerts = True
Dim colm As Long, StartRow As Long
Dim EndCell As Range
Dim ws As Worksheet
StartRow = 3
For Each ws In Worksheets
Set EndCell = ws.Cells(Rows.Count, "c").End(xlUp).Offset(1, 1)
If EndCell.Row > StartRow Then EndCell.Resize(, 4).Formula = "=SUM(R" & StartRow & "C:R[-1]C)"
Set EndCell = ws.Cells(Rows.Count, "D").End(xlUp)
If EndCell.Row >= 1000 Then
Range(J2) = Formula = ((EndCell.Row) * (0.05))
Range(J3) = "5% Discount"
ElseIf EndCell.Row >= 3000 Then
Range(J2) = Formula = ((EndCell.Row) * (0.1))
Range(J3) = "10% Discount"
End If
Next ws
End Sub'
Just need to figure out how to display the results and text to the right cells (J2 in this case)
I will supply the logic and all the references you need to put this one together; and will let you try to put it together on your own :). Come back for more help if needed.
You need to loop through all the worksheets in your workbook (Microsoft Tutorial)
You need to find the last row for the given columns (Online tutorial)
You need to use an IF statement to choose which formula to use (MSDN reference)
UPDATE
What's wrong with your code is this line :
Range(J2) = Formula = ((EndCell.Row) * (0.1))
What you're telling the computer is :
Multiply EndCell.Row by 0.1 (which has the number of the row below and to the right of the last cell in column C)
Compare Formula with the result previously obtained
Store the result of that logical expression at the range stored in variable J2
First of all, what you want is to put the result of the equation, and want to change J2 to "J2" so it gets the cell J2, instead of the what's contained in J2 (which has nothing at that point)
Also, you seem to say that you're not getting the right cells, maybe it is caused by this :
Set EndCell = ws.Cells(Rows.Count, "c").End(xlUp).Offset(1, 1)
In that line, you're finding the last cell of column C, but then you select the cell below, and to the right of it.
There are so many things wrong with your code it's hard to say what's not working properly.