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.
Related
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)
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 have a data set, 10 columns wide, with an ever increasing number of rows.
In column C I have a set of features, e.g. "Search" that will have a few rows corresponding to it; ""Filter" that will have a few rows corresponding to it and so on. However, these could be in any order, so I could have some "Search" features and then some "Filter" features and then some more "Search" features...
I need to create a named range for selected cells in columns D:F where the value in C is the feature I require. This would be for example a named range called "T1" that goes from D3:F6 and maybe D71:F71 for all the "Search" features, but not the "Filter" features.
I have tried using Offset and Count in the Name Manager. But ideally, I need to use VBA in my already existing macro so I don't need to go in and change the Named Ranges every time a new row is added.
Ideally the code would be along the lines of...
If column C contains the word "Filter", make a named range for the three columns to the right of it, every time the word "Filter" occurs.
I used Offset and Count in the name manager:
=OFFSET(Features!$D$3, 0, 0, COUNTA(Features!$D$3:$D$9), COUNTA(Features!$D$3:$F$3))
Sub mySub()
Dim Features As Worksheet
Dim myNamedRange As Range
Dim myRangeName As String
Set Features = ThisWorkbook.Worksheets("Search")
If Range.("C") is "Search"
Set mRangeName= myWorksheet.Range("D:F")
myRangeName = "Search"
ThisWorkbook.Names.Add Name:=Search, RefersTo:=myNamedRange
End Sub
Any help would be greatly greatly appreciated. I hope I have clarified the problem enough.
If I understand correctly then you could try something like the following:
Sub test()
Dim featuresRng As Range
Dim rng As Range
Dim sht As Worksheet
Dim counter As Long
Dim cell As Range
Set sht = ThisWorkbook.Worksheets("Name of your worksheet")
Set featuresRng = sht.Range(sht.Range("C1"), sht.Range("C" & sht.Rows.Count).End(xlUp)) 'dynamically set the range of features
counter = 0 'this counter will help us avoid Union(Nothing, some range), which would give an error
For Each cell In featuresRng 'loop through the range of features
If cell.Value = "search" Then
counter = counter + 1
If counter = 1 Then
Set rng = sht.Range(cell.Offset(0, 1), cell.Offset(0, 3))
Else
Set rng = Union(rng, sht.Range(cell.Offset(0, 1), cell.Offset(0, 3))) 'build the range
End If
End If
Next cell
Debug.Print rng.Address
sht.Names.Add "Something", rng
End Sub
The code above, loops through the range of features and whenever a cell whose value is "search" is found, it adds the corresponding D, E and F cells in a range. In the end you have a total range which you can name whatever you want.
For example, if you have the following set-up:
Then what you'll get is this:
So the resulting range address would be $D$1:$F$2,$D$8:$F$8,$D$10:$F$12,$D$15:$F$19
Now, if you want individual named ranges to be created every time the keyword is found you can modify the code accordingly like so:
Sub test2()
Dim featuresRng As Range
Dim rng As Range
Dim sht As Worksheet
Dim counter As Long
Dim cell As Range
Set sht = ThisWorkbook.Worksheets("Name of your worksheet")
Set featuresRng = sht.Range(sht.Range("C1"), sht.Range("C" & sht.Rows.Count).End(xlUp)) 'dynamically set the range of features
counter = 0
For Each cell In featuresRng
If cell.Value = "search" Then
counter = counter + 1
Set rng = sht.Range(cell.Offset(0, 1), cell.Offset(0, 3))
sht.Names.Add "Something" & counter, rng
End If
Next cell
End Sub
Hi this is my first post and i am newbie when it comes to VBA.
So i tried the last 6 hours to accomplish one task.
I already managed to get the code for the For each loop and it works and copies the value to the existing workbook. But i couldnt find out why it always copies the value to A2 and not further to A3/A4/A5 and so on .
I tried these piece of code " range = range + 1 " but i keep getting runtime errors and it still copies the values to A2 and overwrites it when it gets a new value from the loop.
I think its only a litte change needed but i cant figure it out. :(
Sub copie1()
Dim ws As Worksheet
Dim cell As Range
Dim targetsheet As Worksheet
Dim target As Range
Dim rngTemp As Range
Set wkba = ActiveWorkbook
Worksheets("cop1").Activate
LR = Cells(Rows.Count, "A").End(xlUp).Row
LT = Cells(Rows.Count, "X").End(xlUp).Row
Set rngTemp = Range("X2:X" & LT)
Workbooks.Open Filename:="C:\Users\path......."
Set targetsheet = Worksheets("Data")
Set target= targetsheet.Range("A1")
For Each cell In rngTemp
If cell > 0 Then
target.Offset(1, 0) = cell.Value
End If
target = target+1 '// is this right?
Next cell
End Sub
my goal is the loop through column X in a Workbook and copy every single data that is bigger than 0 ( because there are empty cells & cells with value 0)
and paste it in an existing workbook in range A2/A3/A4 and so on
You can't add the number one to a Range object.
Try replacing target = target+1 '// is this right? with:
Set target = target.Offset(1)
Does this resolve the problem?
SibSib1903, I have added below a simple example that you can easily adapt to your own requirements. It looks at all cell values in column A and any numeric value greater than zero is copied to column C starting in row 1. For example, if column A contains 45 rows with data, and only three of these rows have a numeric value greater than zero, these three values will copied in column C in the first three rows.
Public Sub copieTest()
Dim ws As Worksheet, cell As Range, rngX As Range
Dim tmpVal As Variant, counter As Long
Set ws = ThisWorkbook.Worksheets("cop1")
Set rngX = ws.Range("A1:A" & ws.Cells(ws.Rows.count, 1).End(xlUp).Row)
counter = 1
For Each cell In rngX
tmpVal = Val(Trim(cell.Value))
If tmpVal > 0 Then
ws.Range("C" & counter).Value = tmpVal
counter = counter + 1
End If
Next cell
Set rngX = Nothing: Set ws = Nothing
End Sub
I am trying to create a form which hopefully updates the list of values for a particular dropdown list automatically (without VBA codes) upon user's input immediately.
Here is the form that the user will see:
Currently, both Columns F and H is based on a data-validation formula:
INDIRECT("VList!"&SUBSTITUTE(ADDRESS(1,MATCH($B11,VList!$1:$1,0),1),"1","")&"2:"&SUBSTITUTE(ADDRESS(1,MATCH($B11,VList!$1:$1,0),1),"1","")&COUNTA(INDIRECT("VList!"&ADDRESS(1,MATCH($B11,VList!$1:$1,0),4)&":"&ADDRESS(100,MATCH($B11,VList!$1:$1),4))))
... where VList refers to the sheet as shown below:
So my question here is, based on the Project Name in Column B, is there a way to update the list in sheet VList with the value "Cost Per Unit" [Cell E11], so that the dropdown list in F12 and H12 get automatically updated with the value "Cost Per Unit"?
Been researching a long time for this with no avail, so I'm hoping to seek some experts here to see if such a scenario is even possible without VBA. Thanks!
Edit: So I've been told that VBA codes can be triggered automatically upon changes in the cell value, so I am open to any solutions/help with VBA as well. Will be researching on that direction in the meantime!
Edit2: Added a simple illustration below that hopefully better depicts what I'm trying to achieve on excel:
*Edit3: I'm starting to explore the Worksheet_SelectionChange method, and this is what I've come out so far:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim projectName As String
Dim VariableList As Worksheet
Dim Form As Worksheet
Dim thisRow As Integer
Dim correctColumn As Integer
Dim lastRow As Integer
Set VariableList = ThisWorkbook.Sheets("VList")
Set Form = ThisWorkbook.Sheets("Form")
On Error GoTo EndingSub
If Target.Column = 5 Then
thisRow = Target.Row
projectName = Form.Cells(thisRow, 2)
correctColumn = Application.Match(projectName, VariableList.Range("1:1"), 0)
lastRow = VariableList.Columns(correctColumn).Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
VariableList.Cells(lastRow + 1, correctColumn).value = Form.Cells(5, thisRow).value
End If
EndingSub:
End Sub
Somehow the value of Form.Cells(5, thisRow).Value is always empty.
If I change it to Target.Value it still takes the previous value that was being input (e.g. I first put "ABC" as New Variable, it doesn't get updated. I changed New Variable to "DEF", it updates the list with "ABC" instead of "DEF"). It also takes ALL the values that are under Column E somehow.
Also, pressing Enter after I placed one input in E11 also causes both values of E11 and E12 to be updated when only E12 has been changed. However if I click away after E11 is being input, then only E11's value gets updated.
What exactly am I doing wrong here?
I was almost having fun with this one, if anyone can refine the screwed-up parts feel free to amend.
I furthermore recommend using tables. I do realise you can write lengthy formulae to refer to ranges but giving a name to your table gives an expanding list with a simple reference.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim NewVar As Range
On Error GoTo Err
Set NewVar = Range("C:C") 'data entered here, could be a referstorange kind of named range reference
If Application.WorksheetFunction.CountA(Intersect(Target, NewVar)) <> 0 Then Call ertdfgcvb(Target, NewVar) 'only run if there's an intersect, f*ed up but works anyway
Err:
End Sub
Sub ertdfgcvb(Target As Range, NewVar As Range)
Dim ws As Worksheet, Valid As Long, project As String, ListElmnt As String, Unlisted As Boolean, rng1 As Range, rng2 As Range
Set ws = Sheets("VList") 'the data that you refresh
Valid = 2 'projects in column B
HeaderRow = 1 'headers in Vlist are in row #1
uRow = Cells.Rows.Count 'f* yeah, compatibility considerations
For Each Cell In Intersect(Target, NewVar) 'will evaluate for each cell individually, in case you were to insert columns
ListElmnt = Cell.Value2 'stores the prospective list element
r = Cell.Row 'stores the list element's row to...
project = Cells(r, Valid).Value2 'identify the related project
HeaderRowRef = HeaderRow & ":" & HeaderRow
ColumnNum = ws.Range(HeaderRowRef).Find(What:=project, SearchDirection:=xlPrevious, SearchOrder:=xlByColumns, LookAt:=xlWhole).Column 'finds the project in VList
'MsgBox ws.Name
Set rng1 = ws.Cells(HeaderRow + 1, ColumnNum)
Set rng2 = ws.Cells(uRow, ColumnNum)
LastRow = ws.Range(ws.Cells(HeaderRow + 1, ColumnNum), ws.Cells(uRow, ColumnNum)).Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row 'finds the last row for the project in VList 'f*ed up but works
Unlisted = True 'assumes it's unlisted
For x = HeaderRow + 1 To LastRow
If ListElmnt = CStr(ws.Cells(x, ColumnNum).Value2) Then Unlisted = False 'unless proven otherwise
Next
If Unlisted Then ws.Cells(LastRow + 1, ColumnNum) = ListElmnt 'if it's unlisted it gets appended to the end of the list
Next
End Sub
EDIT:
How to purge the table, example:
Sub ert()
Dim rng As Range
Set rng = Range("Táblázat1") 'obviously the table name
Do While x < rng.Rows.Count 'for each row
If rng(x, 1).Value2 = "" Then 'if it's empty
rng(x, 1).Delete Shift:=xlUp 'then delete but retaining the table format
Else
x = x + 1 'else go to the next line (note: with deletion comes a shift up!)
End If
Loop
End Sub