substract using activecell.offset - vba

my lastrow is (315) - in the below code i need help in the last line (i am trying to do (sum of col H) - (sum of col J) by using activecell-offset in col k
''Totals''
Range("K" & LastRow).Offset(5, 0).Formula = "=activecell.offset(0,-3)-activecell.offset(0-1)"

I don't know how your last row becomes 315 and you have data on the 320 but you are looking for this:
Range("K" & LastRow).Offset(5, 0).Value = ActiveCell.offset(0,-3) - ActiveCell.offset(0-1)

Range("K" & LastRow).Offset(5, 0).Formula = "=" & ActiveCell.Offset(0, -3).Address & "-" & ActiveCell.Offset(0 - 1).Address
This would work, but is not pretty. An alternative would be a static VBA solution such as Damian's answer.
Furthermore, I would advise you to refer to the correct workbook and -sheet. If you were to omit this, the VBA code would always refer to the active workbook/-sheet, something you often don't want.
E.g.
With Workbooks(REF).Sheets(REF)
.Range("K" & LastRow).Offset(5, 0).Formula = "=" & ActiveCell.Offset(0, -3).Address & "-" & ActiveCell.Offset(0 - 1).Address
End With
Next to that, referring to the active cell is also asking for trouble. Perhaps you are better off with referring to better defined ranges, such as
With Workbooks(REF).Sheets(REF)
.Range("K" & LastRow).Offset(5, 0).Formula = "=" & .Cells(LastRow + 4, "K").Address & "-" & .Cells(LastRow, "J").Offset(0 - 1).Address 'Ranges randomly chosen
End With
EDIT
Formulas are dynamic. In other words, their results change dynamically with values/ranges they refer to. Unless you code a Worksheet_Change event, the calculation below will not update if you change the values for which the sum is calculated. However, you don't always need calculations to be dynamic, so pick what you need. I am assuming your columns have headers.
With Workbooks(REF).Sheets(REF)
LastRow = .Cells(.Rows.Count, "K").End(xlUp).Row
LROWJ = .Cells(.Rows.Count, "J").End(xlUp).Row
LROWH = .Cells(.Rows.Count, "H").End(xlUp).Row
.Range("K" & LastRow).Value = Application.Sum(.Range("H2:H" & LROWH)) - Application.Sum(.Range("J2:J" & LROWJ))
End With

Related

How to write a average formula in excel vba

I am trying to calculate average, but i am getting an run-time error. Here is my code..
lastrowcell = Range("B1", Range("B1").End(xlDown)).Rows.Count
Range("F1").Value = "High+Low/2"
For n = 2 To lastrowcell
Range(Cells(n, 6)).Formula = "=average(" & Range(Cells(n, 2), Cells(n, 3)).Address(False, False) & ")"
Next
Can anyone show what I did wrong.
Thanks in advance...
You don't need to loop, Excel is smart enough to fill the entire thing in one go:
lastrowcell = Range("B1", Range("B1").End(xlDown)).Rows.Count
Range("F1").Value = "High+Low/2"
Range("F6:F" & LastRow).Formula = "=AVERAGE(B6:C6)"
The 6 will be incremented in each row
If you want the last row though, its always better to come from the bottom up unless you are specifically looking for the first blank:
lastrowcell = Range("B" & Rows.Count).end(xlup).row
Range(Cells(n, 6))
is not correct syntax for Range property. When there is only one parameter, it should be string. Instead you can use:
Cells(n, 6)
or
Range("F" & n).

Copying to cells without having to select them first

I'm currently trying to use VBA to copy some cells from one location to another and because I'm new to VBA I was wondering if anyone could help me make my code a bit more efficient I know there must be a way to copy to a cell without having to select the cell and then copy to it
For i = 1 To dataSheet.Range("A" & dataSheet.Rows.Count).End(xlUp).Row
dataSheet.Range("A" & i & ":" & "CT" & i).Copy
Set rCell = dataSheet.Range("C" & i)
pasteSheet.Activate
If rCell = condition1 Then
With ActiveSheet
.Range("CU" & rowLoop2).Select
ActiveSheet.paste
End With
You have 2 options. Either use the .PasteSpecial method, or you can just reference the original range and set the new range to it's value.
.Range("CU" & rowLoop2).PasteSpecial Paste:=xlPasteAll
With the setting values option, you have to define the whole range which the values should fill.
Range("A3:E3").Value = Range("A1:E1").Value
If you just used Range("A3").Value = Range("A1:E1").Value only cell A3 would be populated, and it would take th value from cell A1. Hope this helps.
Edit: it's worth noting that you do not have to change sheets to paste either. Your code could be amended to the below:
With dataSheet
For i = 1 To .Range("A" & .Rows.Count).End(xlUp).Row
.Range("A" & i & ":" & "CT" & i).Copy
Set rCell = .Range("C" & i)
If rCell = condition1 Then
pasteSheet.Range("CU" & rowLoop2).PasteSpecial Paste:=xlPasteAll
End If
Next i
End With

countif outputting "true" or "false" rather than number vba

I have been trying to code a countif function into a loop, however, I am having a little trouble with the outputs. Instead of reading a number when the computation occurs, the function keeps outputting "true" or "false". Maybe there is an error in my code, but I have used many countif functions in the past without experiencing a problem such as this. As you can see below, I tried to write the function in two different ways, but both either didn't work or outputted "true" or "false".
Please Help.
Sub CorrectSets()
Dim Cell As Range
Range("B100000").End(xlUp).Select
LastRow = ActiveCell.Row
For Each Cell In Range("S2:S" & LastRow)
StartTime = Cell.Offset(0, -12)
Shift = Cell.Offset(0, -14)
SortedOp = Cell.Offset(0, -17)
DOW = Cell.Offset(0, -5)
'Cell.Value = CountIF(E2:E & LastRow, Shift, N2:N & LastRow ,DOW, B2:B & LastRow,SortedOp, G2:G & LastRow, " < " & StartTime)
Cell.Value = "=CountIF(E2:E" & LastRow & ", " & Shift & ", N2:N" & LastRow & "," & DOW & ", B2:B" & LastRow & "," & SortedOp & ", G2:G" & LastRow & ", " < " " & StartTime & ")"
Next Cell
If you want to put a countif() Formula in Cell then:
Cell.Formula = "=CountIF(E2:E &...............
If you want to put the formula's result in Cell then:
Cell.Value = Application.Worksheetfunction.CountIF(E2:E &....................
You should use
Cell.Formula = "=CountIFs..."
or
Cell.Value = WorksheetFunction.CountIfs...
See official documentation.
Plus:
To find the last row containing data in a column (B in this case) use
Dim ws as Worksheet
Set ws = ActiveSheet
Dim LastRow as Long
LastRow = ws.Range("B" & ws.Rows.Count).End(xlUp).Row
ws is a reference to the Worksheet of interest (ActiveSheet in my example).
See this answer.
You'd rather fully qualify your ranges, and avoid using Select unless it is strictly needed.
With the code posted above,
Range("B100000").End(xlUp).Select
might not be needed.
If using Cell.Formula = "=CountIFs...", it might be convenient to use
Dim frm as String
frm = "=CountIFs..."
Cell.Formula = frm
for easier debugging.

Splitting cell with multiple data into multiple rows in more than 1 column

I have a sheet with multiple data in 1 cell this happen in a couple of columns. What I need to do is split the cell into individual rows while still keep the details from the other columns
Screen 1 shows the data i got
http://imageshack.com/a/img845/1783/wxc8.png (Screen 1)
Screen 2 is what i wish the macro to output.
http://imageshack.com/a/img842/7356/7yra.png (screen 2)
The macro i found and edited in only allows me to split 1 column and i can't get the editing of the range right. the columns that needs to be split is "J" "K" "N" and "O". The columns "A"- "I" and "L""M" just needs to copy their content to the new row.
Thank you in advance for the help.
Here the Macro im using
Sub Splt1()
Dim LR As Long, i As Long
Dim X As Variant
Application.ScreenUpdating = False
LR = Range("J" & Rows.Count).End(xlUp).Row
Columns("J").Insert
For i = LR To 1 Step -1
With Range("K" & i)
If InStr(.Value, Chr(10)) = 0 Then
.Offset(, -1).Value = .Value
Else
X = Split(.Value, Chr(10))
.Offset(1).Resize(UBound(X)).EntireRow.Insert
.Offset(, -1).Resize(UBound(X) - LBound(X) + 1).Value = Application.Transpose(X)
End If
End With
Next i
Columns("K").Delete
LR = Range("J" & Rows.Count).End(xlUp).Row
With Range("L1:M" & LR)
On Error Resume Next
.SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "=R[-1]C"
On Error GoTo 0
.Value = .Value
End With
Application.ScreenUpdating = True
End Sub
The problem appears to be the with operator. It constrains your selection. Try reformulating your macro without the with and refer to the the ranges direct. For example, replace your first for loop with something like this:
For i = LR To 1 Step -1
If InStr(Range("K" & i).Value, Chr(10)) = 0 Then
Range("K" & i).Offset(, -1).Value = Range("K" & i).Value
'Range("J" ...
'Range("N" ...
'Range("O" ...
Else
K_collection = Split(Range("K" & i).Value, Chr(10))
Range("K" & i).Offset(1).Resize(UBound(K_collection)).EntireRow.Insert
Range("K" & i).Offset(, -1).Resize(UBound(K_collection) - LBound(K_collection) + 1).Value = Application.Transpose(K_collection)
'J_collection = Split(Range("J"...
'N_collection = Split(Range("N"...
'O_collection = Split(Range("O"...
End If
Next i
In general I avoid with because it tends to obscure the visual pattern of code.
You might also consider eliminating the .INSERT and .DELETE columns, and overwrite directly to the cells. When working with more than one at a time, it becomes hard to keep track which column is temporary and which one is the source. But that all depends on your preference.
Copying values for the other columns should be easy compared to this.

Loop through rows in an excel worksheet and copy a range if the cell isn't blank

I have virtually no VBA experience except that, from what I have seen other spreadsheets do, I am convinced this must be possible. I have searched all over but cannot find any explanations to help or code I can just use. I hope someone can help.
I have a download from our website cart which does not format the data how it needs to be to then up-load into some new sales order/invoice generating software.
As an example here is a link to an image that shows how the data currently looks(the workbook is called 'Orders.csv' but I can convert to xlsx if needed):
http://web225.extendcp.co.uk/fiercepc.co.uk/img1.jpg
As you can see if the customer purchases more than one product (not qty of a product, a completely different product) it is listed across the row. The first product is starts at column H, the second from column O, the third from column V and so on.
I need the data to be displayed as follows:
http://web225.extendcp.co.uk/fiercepc.co.uk/img2.jpg
So each product is listed below each other and with the same customer details before it. This is so the invoicing software can check each order ID and create an invoice accordingly showing all the different products.
I have no idea how to go about this. I guess it needs to be a looping macro that checks if a row has data in a cell and then copies ranges accordingly. Also, the macro would need to be in a different workbook (maybe called macros) so it acts on this download as it will be a new workbook each time it is downloaded. I hope this makes sense.
I'm sure this will be very easy for somebody, just not me. Please help! Ideally I need the macro with explanations so I can manipulate the ranges etc as this is only an example spreadsheet, the actual sheet is much bigger and contains more data.
I managed to get an answer to my own question from elsewere, but thought I'd share the answer with everyone who might be interested as the reply was spot on and indepth.
'****This macro is to use on sheets within the same workbook
'****If you want to transfer your data to another workbook you
'****will have to alter the code somewhat, but the idea is the same
Sub copydata()
Dim x As Integer
Dim y As Integer
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Set ws1 = Worksheets("Ouput sheet") 'whatever you worksheet is
Set ws2 = Worksheets("Orders") 'or whatever your worksheet is called
'Item 1 - I'm calling the separate sections where each item ordered is in your worksheet Item 1, Item 2
'this encompasses columns H-N for item 1, etc, etc
r = 3 'this is the first row where your data will output
x = 3 'this is the first row where you want to check for data
Do Until ws2.Range("A" & x) = "" 'This will loop until column A is empty, set the column to whatever you want
'but it cannot have blanks in it, or it will stop looping. Choose a column that is
'always going to have data in it.
If Not ws2.Range("H" & x).Value = "" Then 'This checks your column H to make sure it's not empty
'If empty, it goes on to the next line, if not it copies the data.
'This column should be something that will have something in it if
'there is a product ordered for Item 1
'i.e. don't choose column J if it will have blanks where there is
'actually an item ordered
'this section copies the data, the worksheet left of the = sign is the one data will be written to
ws1.Range("A" & r).Value = ws2.Range("A" & x).Value 'Order Date
ws1.Range("B" & r).Value = ws2.Range("B" & x).Value 'Order ID
ws1.Range("C" & r).Value = ws2.Range("C" & x).Value 'Customer
ws1.Range("D" & r).Value = ws2.Range("D" & x).Value 'Billing Add
ws1.Range("E" & r).Value = ws2.Range("E" & x).Value 'Subtotal
ws1.Range("F" & r).Value = ws2.Range("F" & x).Value 'Tax Amount
ws1.Range("G" & r).Value = ws2.Range("G" & x).Value 'Total Amount
ws1.Range("H" & r).Value = ws2.Range("H" & x).Value 'Product ID
ws1.Range("I" & r).Value = ws2.Range("I" & x).Value 'Column J - couldn't read your headings for a few of these
ws1.Range("J" & r).Value = ws2.Range("J" & x).Value 'Column K
ws1.Range("K" & r).Value = ws2.Range("K" & x).Value 'L
ws1.Range("L" & r).Value = ws2.Range("L" & x).Value 'Price
ws1.Range("M" & r).Value = ws2.Range("M" & x).Value 'Attributes
r = r + 1 'Advances r and x when there is a matching case
x = x + 1
Else
x = x + 1 'Advances only x (to check the next line) when there is not a matching case,
'i.e. your output line stays on the next line down from where it last wrote data
'while x advances
End If
Loop 'End of Item 1
'Item 2
x = 3 'this time we only define x, we want r to stay where it's at so it can continue copying the data into one
'seamless list
Do Until ws2.Range("A" & x) = "" 'still want this to stay the same
If Not ws2.Range("O" & x).Value = "" Then 'This one needs to change to match the column in your second Item
'the ranges on ws1 will stay the same, ws2 ranges pertaining to customer data stay the same, ws2 ranges pertaining
'to specific Item 2 info will change
ws1.Range("A" & r).Value = ws2.Range("A" & x).Value 'Order Date *SAME
ws1.Range("B" & r).Value = ws2.Range("B" & x).Value 'Order ID *SAME
ws1.Range("C" & r).Value = ws2.Range("C" & x).Value 'Customer *SAME
ws1.Range("D" & r).Value = ws2.Range("D" & x).Value 'Billing Add *SAME
ws1.Range("E" & r).Value = ws2.Range("E" & x).Value 'Subtotal *SAME
ws1.Range("F" & r).Value = ws2.Range("F" & x).Value 'Tax Amount *SAME
ws1.Range("G" & r).Value = ws2.Range("G" & x).Value 'Total Amount *SAME
ws1.Range("H" & r).Value = ws2.Range("O" & x).Value 'Product ID *CHANGED!!!!
ws1.Range("I" & r).Value = ws2.Range("P" & x).Value 'Column J *CHANGED!!!!
ws1.Range("J" & r).Value = ws2.Range("Q" & x).Value 'Column K *CHANGED!!!!
ws1.Range("K" & r).Value = ws2.Range("R" & x).Value 'L *CHANGED!!!!
ws1.Range("L" & r).Value = ws2.Range("S" & x).Value 'Price *CHANGED!!!!
ws1.Range("M" & r).Value = ws2.Range("T" & x).Value 'Attributes *CHANGED!!!!
r = r + 1 'Advances r and x when there is a matching case
x = x + 1
Else
x = x + 1 'Advances only x (to check the next line) when there is not a matching case,
'i.e. your output line stays on the next line down from where it last wrote data
'while x advances
End If
Loop 'End of Item 2
'simply copy Item 2 code and change the appropriate values to match Items 3,4,5,6, etc, etc
'You will get a list of all the info for Item 1, follow by all info for Item 2, etc, etc
'i.e. if Paul orders 2 items, they won't end up right below each other, but his second
'item will end up farther down, but will still be on the list
'If this is not what you want you could sort afterwards or alter the code, but it is a significant alteration
End Sub
A solution would be:
Loop through rows
For each rows, get number of occupied columns.
Find number of orders in row by simple math(assuming each product order occupies same number of columns)
Loop over the orders - Copy the product data in new sheet
For each of these copy operations, copy customer data from the row under process in outermost loop.
As for the last requirement. Open the workbook orders.csv(assuming the filename and location remain the same) in you macro and do all the operations mentioned above onto it.
I can write it for you. But, it would be a good learning experience for you if you write it on your own. You will able find answers to most of your queries(like how to get number of occupied columns in a row etc.) here on stackoverflow.
Also, go through this page to get started on Excel VBA:
http://www.functionx.com/vbaexcel/