I have two separate excel workbooks. One is an order form, the other is a master inventory file.
I have a column where I input the order amount for each individual items (let's say it spans from cell C2:C130, each row is a different item). My optimal solution is to develop a macro that takes that order and adds it to an existing master inventory to keep track of total orders.
I wrote some code that I thought would work:
Private Sub CommandButton1_Click()
Dim wsCopy As Worksheet
Dim wsDest As Worksheet
Dim lCopyLastRow As Variant
Dim lDestLastRow As Variant
Workbooks.Open "C:\Users\Dave\Desktop\Designs Work\Master_Inventory.xlsm"
Set wsCopy = Workbooks("Production file_Dave Edits").Worksheets("Order")
Set wsDest = Workbooks("Master_Inventory").Worksheets("Sheet1")
Set lCopyLastRow = wsCopy.Range("E2:E130")
Set lDestLastRow = wsDest.Range("E2:E130")
lDestLastRow.Value = lDestLastRow.Value + lCopyLastRow.Value
End Sub
when I run this code, I get a mismatch error (type 13?).
So I went into the watch window to see the type of each expression and the combined right side of the equation is a "variant/integer" type, whereas each individual expression is a "variant/variant" type. Moreover, when I run the code and call only one cell instead of a matrix, the code works; it doesn't run when multiple cells are called.
Can anyone help? Or have a more elegant code? Thank you
Set lCopyLastRow = wsCopy.Range("E2:E130")
Set lDestLastRow = wsDest.Range("E2:E130")
This makes both variables Variant/Range, because the Set keyword says the right-hand side of the assignment operator is yielding an object reference: the two variables might as well be declared As Range.
Now, the Value of a Range object that refers to multiple cells, is a 2D Variant array.
lDestLastRow.Value = lDestLastRow.Value + lCopyLastRow.Value
That's where you're getting the type mismatch error, because you can't use the + operator with array operands.
when I run the code and call only one cell instead of a matrix, the code works
That's because a single-cell Range.Value returns that numeric value, and + will work with that - although, if the cell contains an error value (e.g. #REF! or #VALUE!), you'll still get a type mismatch error, because a Variant/Error can't be an operand.
I wish I could help beyond that, but I have no idea what this + intends to be doing.
As for a more elegant solution, I'd recommend indenting the procedure body, moving declarations closer to their assignment, and keeping a reference to the destination "inventory" workbook:
Private Sub CommandButton1_Click()
Dim sourceSheet As Worksheet
Set sourceSheet = Workbooks("Production file_Dave Edits").Worksheets("Order")
Dim inventoryBook As Workbook
Set inventoryBook = Workbooks.Open("C:\Users\Dave\Desktop\Designs Work\Master_Inventory.xlsm")
Dim destSheet As Worksheet
Set destSheet = inventoryBook.Worksheets("Sheet1")
Dim sourceRange As Range
Set sourceRange = sourceSheet.Range("E2:E130")
Dim destRange As Range
Set destRange = destSheet.Range("E2:E130")
'todo: figure out intent
'lDestLastRow.Value = lDestLastRow.Value + lCopyLastRow.Value
End Sub
If that + intends to add everything up in both ranges, you could use Application.WorksheetFunction.Sum(sourceRange) + Application.WorksheetFunction.Sum(destRange), although I doubt that's really want you're looking for.
Related
I'm trying to write a code that makes a graph from different parts of the data I have, the issue is that I need a for loop to go through all my lines of data and when the row changes also the range for the next graph
Basically I need the code to check when i is 1 to graph using the range A2:A12 from a different sheet (The table below exists on Sheet1, while the range is from Sheet2)
Cycle
Starting range
Ending range
1
A2
A12
2
A22
A32
The issue I'm facing is that when I try to define Range("Variable 1:Variable 2") the code doesn't seem to work.
I'm sure I'm thinking about this wrong but I haven't found any solutions online.
Never use Range or Cells (as well as Columns, Rows) without specifying in which sheet you expect them to be. Otherwise your code depends on which workbook/worksheet is active the moment it runs and your code is not reliable.
Use something like
ThisWorkbook.Worksheets("Sheet2").Range(…)
or better define a variable so you don't have to repeate it:
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet2")
'and then always use
ws.Range(…)
To use variable ranges you either need to concatenate your variables to a valid address you can use:
ws.Range(Variable1 & ":" & Variable2)
or simly use
ws.Range(Variable1, Variable2)
where for example Variable1 = "A2" and Variable2 = "A12"
Reference Ranges Using a List of Cell Addresses
Option Explicit
Sub ReferenceRanges()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = wb.Worksheets("Sheet1")
Dim dws As Worksheet: Set dws = wb.Worksheets("Sheet2")
Dim drg As Range
Dim r As Long
For r = 2 To sws.Cells(sws.Rows.Count, "B").End(xlUp).Row
' Note the importance of using '.Value' (try without it).
Set drg = dws.Range(sws.Cells(r, "B").Value, sws.Cells(r, "C").Value)
' Continue using 'drg', e.g.:
Debug.Print drg.Address(0, 0)
Next r
End Sub
I tried to copy a set of filtered data from the worksheet "PEP" (Filter: Emetteurs, in column 18) and paste it in the next empty row in the worksheet"Sanctions". I found this text code online and add the section NextRow in order to paste it on the next Empty Row in the "Sanctions" Worksheet. I just started using VBA not long ago, so I tried to use codes online however in this case, I think the problem here is that I cannot define the value of range. I tried to look it up online but no result. Thank you in advance for all your help!
Dim k,j,i As Interger
ActiveWorkbook.Sheets("Sanctions").Select
NextRow=Cells(Range("A"&Rows.Count).End(xlUp).Row+1,1
k=1
With sheets("PEP")
T=.Range("A1:AS"&.Cells(Rows.Count,1).End(xlUp).Row)
.Rows(1).Copy Sheets("Sanctions").Range("NextRow")
End With
For i=2 To UBound(T,1)
If T(i,18)="Emetteurs" Then
k=k+1
For j =1 to UBound(T,2)
T(k,j)=T(i,j)
Next j
End If
Next i
With Sheets("Sanctions")
Application.ScreenUpdating=False
.Range("NextRow").Resiwe(k,UBound(T,2))=T.Offset(1)
.Columns.AutoFit
Application.ScreenUpdating=True
End With
End Sub
And if possible I would also like to find a solution to remove the header once I do the Copy-Paste process.
Read the code's comments and adjust it to fit your needs.
Some things to begin:
Use option explicit so you don't have unexpected behavior with undefined variables
Always indent your code (see www.rubberduckvba.com a free tool that helps you with that)
Try to separate your logic defining variables and the reusing them
EDIT: Some reviews to your code:
As mentioned by #Pᴇʜ you need to declare each of your variables types.
This:
Dim k,j,i As Interger
is the same as declaring:
Dim k As Variant
Dim j As Variant
Dim i As Integer
Side note. You had a typo in Interger
Which is not what you really want, knowing that all of them are going to store numbers
Declare the objects that you're going to work with. For example, you refer to the sheet Sanctions three times in your code:
ActiveWorkbook.Sheets("Sanctions")
This can be set once like this:
Set targetSheet = ThisWorkbook.Worksheets("Sanctions")
And then reused in lines like this:
With Sheets("Sanctions")
or this:
Sheets("Sanctions").Range("NextRow")
by writing this:
With targetSheet
in this way, if you ever need to change it (the person working with your code, or your future you will be really thankful)
Declaring your variables to just letters, makes your code really hard to understand.
Dim j,k,l
is different when you have:
Dim lastRow As Long
Dim lastColumn As Long
etc.
I suggest that you use the key F8 and step through the code, and see the logic behind it. That way you can learn more.
Code:
Public Sub ConditionalRowCopy()
' Declare object variables
Dim sourceSheet As Worksheet
Dim targetSheet As Worksheet
Dim cell As Range
' Declare other variables
Dim sourceLastRow As Long
Dim targetLastRow As Long
' Set a reference to the sheets so you can access them later
Set sourceSheet = ThisWorkbook.Worksheets("PEP")
Set targetSheet = ThisWorkbook.Worksheets("Sanctions")
' Find last row in source sheet based on column "R"
sourceLastRow = sourceSheet.Cells(sourceSheet.Rows.Count, "R").End(xlUp).Row
' Find cell with word "Emetteurs", search in column R)
For Each cell In sourceSheet.Range("R1:R" & sourceLastRow).Cells
' If match
If cell.Value = "Emetteurs" Then
' Find last row in target sheet based on column "A"
targetLastRow = targetSheet.Cells(targetSheet.Rows.Count, "A").End(xlUp).Row
' Copy entire row to next empty row in target sheet
cell.EntireRow.Copy Destination:=targetSheet.Range("A" & targetLastRow).Offset(RowOffset:=1)
End If
Next cell
End Sub
Let me know if it works!
Ill use an example to explain my problem. Lets say I write the function,
Function Sums()
Sums = Cells(1, 1) + Cells(1, 2)
End Function
Then, lets say I have two spreadsheets, A and B. If I refresh all spreadsheets while on A, the Sums() function on spreadsheet B, utilizes the data from spreadsheet A. How can I make the functions utilize the data specifically on the spreadsheet the function is written on?
The issue here is when using Cells(1, 1) or Range("A1") without specifying in which worksheet that is, Excel starts guessing which worksheet you mean. The result of that guessing may be different.
Code in Module:
when the code is in a module it assumes that you mean the ActiveSheet (the sheet which is on top at the moment the code runs)
Code in Sheet:
when the code is in a worksheet then it assumes that you mean that worksheet the code is in.
So we see any Cells(1, 1) or Range("A1") without specifying a worksheet is a bit lottery.
The next issue here is that the Sums() function can not know on which sheet it was written on unless you give the cells as parameters in a range:
Public Function Sums(Rng As Range) As Double 'specify a return type here
Sums = Application.WorksheetFunction.Sum(Rng)
End Function
So you can use it in a cell like: =Sums(A1:B1)
Another advantage is that you don't need to press re-calculate, because it watches if the values in the given range Rng changed and automatically recalculates then.
Note: A function should always use the parameters given to the function instead of accessing the worksheet directly. Otherwise the function might show wrong values (unless you make it volatile).
I'm not 100% sure what you're asking. But here is a guess: Be sure to always (fully) define your worksheets.
Function Sums()
Dim wb as Workbook
Dim ws as Worksheet
Dim ws2 as Worksheet
Dim sum as Integer
Set wb = Application.ActiveWorkbook
Set ws = wb.Worksheets("Sheet1")
Set ws2 = wb.Worksheets("Sheet2")
'now you defined your worksheets and always know which cells you're referring to
sum = ws.Cells(1, 1) + ws2.Cells(1, 2)
End Function
Also I generally recommend to use ranges, instead of Cells(x,y). Let me know if this answers the question
I am trying to filter my data, and add the desired data to the Range. I have set my desired range to null and initiated a search in a particular column for a particular keyword, which if found adds the entire row in my Range.
Column 'E' is been searched for a Value "Desired Value", if found the entire row is added to the PTRange, which is initially set to Nothing(is Empty).
Dim rng As Range
Dim PTRange As Range
Dim count As Long
count = 1
Set PTRange = Nothing
Set rng = Range("E2:E20")
For Each cell In rng
If cell.Value = ("Desired Value") Then
PTRange = Application.Union(Range(PTRange), Range(Rows(count)))
End If
count = count + 1
Next cell
But the compiler shows that there is something wrong in the code where Union method is used.
UPDATE :
Initialization of PTRange and the following change
PTRange=Union(PTRange,Rows(count))
worked for me. Thanks
It is hard to get you to your desired goal as I'm a little unclear what that is but the below can serve as both assistance on your code with a possible solution following.
Firstly I would recommend you always have Option Explicit turned on especially if calling out to Stack Overflow for assistance.
Option Explicit forces you to declare all variables, in this instance it would be of help as you cell as a variable which can appear ambiguous. When it is not declared, it starts its life as a variant and is defined to a specific format on first use, in this case the compiler declared cell as a Range.
It also helps encourage better coding and to understand what is happening in the code more. To turn it on, type Option Explicit at the very top of the code pane (before any procedures or variable declarations), also while in the VBE (Visual Basic Editor) click 'Tools' > 'Options' and ensure 'Require Variable Declaration' is ticked.
Below are some comments within the code you have, these should help you understand the issues you are facing.
Dim rng As Range
Dim PTRange As Range
Dim count As Long
count = 1
'If the range is nothing then it can not be used, it must be started as
'something
Set PTRange = Nothing
Set rng = Range("E2:E20")
'This is where declaring 'cell' would help understanding, it will become a
'Range at the VBE discretion\decision based on what you are trying to do
'with it. Even when looking at a single cell, you do so through a Range,
'it is a reference to one or more cells in a worksheet
For Each cell In rng '
If cell.Value = ("Desired Value") Then
'This will not work for a number of reasons
'O- As BrakNicku stated, there needs to be 'Set' at the front
'O- This is not remembering the values into the 'PTRange' but
' adding a reference to where those values are. I.e. You are
' not taking note of everybody in the house, but just remembering
' their house address
'O- You do not need to put 'Range()' around 'PTRange' as it is
' already a range
'O- 'Rows(count)' is not valid syntax and does not pass a range into
' the union
'O- 'Union' will put together 2 or more references to ranges, it
' will not put their values in any specific place on a worksheet
PTRange = Application.Union(Range(PTRange), Range(Rows(count)))
End If
count = count + 1
Next cell
I hope the above has been informative and below is an example to a potential solution:-
Option Explicit
Public Sub Sample()
Dim LngRow As Long
Dim Rng As Excel.Range
Dim Rng_Cl As Excel.Range
Dim Rng_PT As Excel.Range
LngRow = 21
'Initialise my Range
Set Rng_PT = Range("E21") '" & LngRow)
'Get the search range
Set Rng = Range("E2:E20")
'Loop through all the cells
For Each Rng_Cl In Rng
If Rng_Cl.Value = ("Duck") Then
'Adds just 5 column and not the whole row
Range(Rng_Cl.Address, ActiveSheet.Cells(Rng_Cl.Row, Rng_Cl.Column + 5).Address).Copy Range("E" & LngRow)
LngRow = LngRow + 1
End If
Next
'It is good practice to remove references once you are done with them
'This can help with memory consumption/speed and also more serious
'memory leaks (and actual stack overflows!) on larger macros
Set Rng = Nothing
Set Rng_PT = Nothing
End Sub
I have several ranges that may need sorting. I want to use a range name selected from a dropdown list to select and sort that range:
Sub Sorts()
Dim Wb As Workbook
Dim Ws1 As Worksheet
Dim rng As String
Set Wb = ThisWorkbook
Set Ws1 = ThisWorkbook.Sheets("Products")
Set rng = Ws1.Range("G76").Value
Ws1.Range("rng").Sort Range("rng").Cells(1, 1)
End Sub
macro stops at St rng .......object required
Your code is ok, apart for a little detail.
The right line of code should be this one:
Ws1.Range(rng).Sort Range(rng).Cells(1, 1)
This is due to the difference between variables and strings. Your rng is a variable of type string; it means that it's a string, but it contains a specific value that you have "almost" correctly referenced here (you don't need the Set keyword for a string:
rng = Ws1.Range("G76").Value
However, if you pass the variable rng with quotes "", VBA will understand that has to look for a range named exactly rng; which, apparently, doesn't exist so you get an object not-set error.