Insert Row Every Two Rows and Subtract the Difference - excel

I have numerous excel sheets that contain rows that have paired data. Specifically, I need to subtract the first row from the one that follows (e.g., row 2-row 1; row 4-row3; etc.) and place the result into a new row below each pair. My data in each sheet appear as follows:
I am not new to programming languages, but I am new to visual basic.
My current code is:
Sub test() Dim rng As Range
Columns(1).Insert
With Range("b2", Range("b" & Rows.Count).End(xlUp)).Offset(, -1)
.Formula = "=if(mod(row(),2)=1,1,"""")"
.Value = .Value
.SpecialCells(2, 1).EntireRow.Insert
End With
Columns(1).Delete
With Range("a1", Range("a" & Rows.Count) _
.End(xlUp)(2)).Resize(, 3)
.Columns(1).SpecialCells(4).Value = "Difference"
Union(.Columns(2).SpecialCells(4), .Columns(3) _
.SpecialCells(4)).Formula = _
"=r[-1]c-r[-2]c"
End With
End Sub
However, the result is this:
I am mainly interested in calculating the differences between row pairs in the first column shown, but it is clearly not working.
Any help would be greatly appreciated!

Easier to use formulae, rather than VBA.
Go to a second sheet in the file ("Sheet2")
Enter in A1: =Sheet1!A1-Sheet1!A2
On Sheet2, select Rows1 AND 2.
Drag down.
Then depends on what you need to do.
May be Copy | Paste Special | Values to Sheet3, and sort to remove blank rows.

Related

Replicating values

Need a little help here.
In the "Data" Tab I want to copy values in column "c2:c1000" and paste in column "a1" of another Tab.
This is what i have so far,
Dim x As Long
Dim lastRow As Long
lastRow = Worksheet("Data").Cells(3, Columns.Count).End(xlUp).Column
For x = 1 To lastRow
If Worksheets("Sheet2").Cells(2, "A") = "" Then
Worksheets("Data").Range("c2:c1000").Copy Destination:=Worksheets("Sheet2").Range(1, "A")
Sheets("Sheet2").Range("A1").Value = Format(Now, "mm/dd/yyyy HH:mm:ss")
Else
Worksheets("Data").Range("c2:c1000").Copy Destination:=Worksheets("Sheet2").Cells(2,
Columns.Count).End(xlToLeft).Offset(, 1)
'Sheets("Sheet2").Range("A1").Value = Format(Now, "mm/dd/yyyy HH:mm:ss") --> can't figure how to increment this as this will need to be on the subsequent empty column
End If
Next
End Sub
Your help will be greatly appreciated!
Thank you.
Pasting values first into range A1 and down and then next time to cell B1 and so on, leaves no space for the timestamp to A1, B1 etc. So, I assume that you would like to paste the random values to row 2. So cells A1, B1, ... are left for the timestamp.
Inside the With statements we can refer to properties of the wsAudit so we can replace the "Worksheets("Audit")." reference with just "."
The column.count expression just checks the amount of columns in the worksheet.
The expression .Cells(2, Columns.Count) just points to last cell in the row 2.
The .End(xlToLeft).Column then looks from this column to left and is supposed to find the last not empty cell on this row. It's basically the same idea that in Excel's sheet you would go to cell XDF2 and hit CTRL+Arrow Left from keyboard.
But instead of activating the cell we just want to get the columns index number and then add 1 (the new column) and save it into variable. Now the new column is known.
The expression Range(.Cells(2, newColAudit), .Cells(1000, newColAudit)).Value is really the same as e.g. Range("B2:B1000"), but with this we can use the row and column index numbers instead. This is useful as the column number varies.
And as Samuel pointed out the copy paste operation can be avoided by setting the areas equal.
Dim wsAudit As Worksheet
Dim newColAudit As Long
Set wsAudit = Worksheets("Audit")
With wsAudit
newColAudit = .Cells(2, Columns.Count).End(xlToLeft).Column + 1
Range(.Cells(2, newColAudit), .Cells(1000, newColAudit)).Value = Worksheets("Data").Range("C2:C1000").Value
.Cells(1, newColAudit).Value = Format(Now, "mm/dd/yyyy HH:mm:ss")
End With
Much like your LastRow* variable for your source sheet, create a LastColumn variable for your destination sheet, which will find the last used column the same way you are finding your last used row.
Like so:
Dim LastColumn As Long
LastColumn = Sheets("Audit").Cells(1, Columns.Count).End(xlToLeft).Column
Then use the variable like so:
Destination:= Worksheets("Audit").Cells(1, LastColumn)
It seems that your code contradicts your question too, in your question you explained the data will be written to the Audit sheet in row 1, using the next column each time but your code looks for values in row 2 in your If statement:
If Worksheets("Audit").Cells(2, "A") = "" Then is the same as If Worksheets("Audit").Range("A2") = "" Then.
If you mean to check the first row, change the 2 to 1.
To help improve your codes efficiency:
(Also see the link to 'how to avoid select' in that question):
You can achieve 'copy/paste' without actually using the 'copy' and 'paste' methods by assigning the value of one range to the other, as example, like so:
Worksheets("Audit").Cells(1, LastColumn).Resize(999, 1) = Worksheets("Data").Range("c2:c1000").Value
Note: Change the Resize Property rows to suit the source range (in this case you are wanting to move values from C2:C1000).
*The LastRow variable is a bit confusing, as it is looking for the last used column in row 3.
If it's meant to find a column, consider renaming it to avoid confusion later on in debugging.
If it's meant to find the last row, try like this:
LastRow = Worksheet("Data").Cells(Rows.Count, 1).End(xlUp).Row

Return cells content from range

Yesterday I learned here how to copy a row to a second sheet.
Sub maJolieProcedure(Texte As String)
With Worksheets("employes").Range("A:A")
Set c = .Find(what:=Texte)
If Not c Is Nothing Then
firstAddress = c.Row
Worksheets("employes").Rows(firstAddress).Copy _
Destination:=Worksheets("rapport").Range("A1")
MsgBox "Ok"
Else
MsgBox "Nok"
End If
End With
End Sub
To respect the formatting of the second sheet, I want to copy and paste the contents of each cell one by one.
I can identify the line number. However, I can't figure out how the Range object can return each cell one by one. For example, C3 content if Rows = 3.
Thanks a lot.
If you don't want to paste the formatting from one range to another paste values only.
Worksheets("employes").Rows(firstAddress).Copy
Worksheets("rapport").Range("A1").PasteSpecial xlValues
That's the same for all ranges, whether 1 cell or a million. The Copy process copies the subject to memory as one block. Any parsing must be done before the Copy instruction. An alternative is to read a range into an array.
Dim Arr As Variant
Arr = Worksheets("employes").Rows(firstAddress).Value
This will create a 3D array of 1 row and about 16000 columns. You might decide to limit your enthusiasm to only what you need.
With Worksheets("employees")
Arr = .Range(.Cells(firstAddress, 1), .Cells(firstAddress, .Columns.Count).End)xlToLeft)).Value
End With
Pay attention to the leading periods within the With statement. Each such period stands for the object mentioned in the With statement.
If your goal is to respect the formating of the second sheet, you don't need to loose time copying cell by cell.
It is more effective to do a paste special, like you do with the mouse:
Range("A1").Copy
Range("B1").PasteSpecial Paste:=xlPasteValues
works very well also with bigger ranges if you need:
Range("A1:A12").Copy
Range("B1:B12").PasteSpecial Paste:=xlPasteValues
or even
Range("A1:A12").Copy
Range("D3").PasteSpecial Paste:=xlPasteValues
If your goal is to really access all cell of a range individually , you just iterate on the range. For example:
For Each cell In Range("A1:A12")
cell.Value = cell.Value + 2
Next cell

Need to compare two column and provide output to third column

I want to write VBA code that will compare two columns from two different sheets.
I have data in Sheet1 column B and in Sheet2 column B.
The formula to compare both columns is in Sheet2: =B2=Sheet1!B2.
Could you please help me to write VBA code for the above formula.
I am not sure how to use the above formula in VBA code.
The basic code to compare is
If Sheet1.Range("B1").Value = Sheet2.Range("B1").Value Then
'Code to execute when criteria is met
Else
'Code to execute when criteria is not met
End If
The else part is optional and can be omitted if you don't need it
If you want to compare the full column there are a few ways to do it.
My favorite is following:
Dim iLastRow As Integer
iLastRow = Sheet1.Cells(Sheet1.Rows.Count, 2).End(xlUp) 'Gets the last row
For i = 1 To iLastRow 'Compares each row and executes the code if
If Sheet1.Range("B" & i).Value = Sheet2.Range("B" & i).Value Then
'Code to execute when criteria is met
Else
'Code to execute when criteria is not met
End If
Next i
If you want to compare the displayed/formatted text of the cell and not the value behind it use .Text instead of .Value (e.g. "10th Sep. 2019" instead of 43718)

Excel vba: Sum values for an account number and enter total on all rows for that account

I have a table like this:
Acct number Transaction
XXXXXX5030 11.33
XXXXXX5030 37.53
XXXXXX5030 101.30
XXXXXX1133 20.33
XXXXXX1133 21.21
I need code that will loop through and find all the accounts with the same number and all all the transactions. Then it would create a new column with the total on each row for that transaction. Like this:
Acct number Transaction Total
XXXXXX5030 11.33 150.16
XXXXXX5030 37.53 150.16
XXXXXX5030 101.30 150.16
XXXXXX1133 20.33 41.54
XXXXXX1133 21.21 41.54
I have tried using sumif, but it doesn't work. It returns #VALUE! for all cells. My code so far:
Private Sub CommandButton1_Click()
Sheets("Create_CSV").Range("A8").Select '
' Set Do loop to stop when an empty cell is reached.
Do Until IsEmpty(ActiveCell)
Dim ActN As String
ActN = Right(ActiveCell.Value, 4)
Dim TotalAmm
TotalAmm = Application.SumIf(Range("B8:B1000"), Right(ActiveCell.Value, 4), "=ActN")
ActiveCell.Offset(0, 2).Value = TotalAmm
ActiveCell.Offset(1, 0).Select
Loop
End Sub
Your VBA is an unneeded overkill. Simply use SUMIF.
=SUMIF($A$2:$A$100, $A2, $B$2:$B$100)
You can use the SumIf formula in excel to do this. If your account number is in Column A and the Transactions in Column B you would put this formula in Column C and drag down.
=SUMIFS($B$1:$B$10,$A$1:$A$10,A2)
My example only assumed there was a range of 10
I needed to do it in vba because it is a huge spreadsheet that is doing many other things... and I want to be able to run it all at once. You all put me on the right path. For the vba, the following worked:
TotalAmm = Application.SumIf(Range("A8:A1000"), ActiveCell.Value, Range("B8:B1000"))
You don't need to do this line by line:
Range("C8:C" & Range("A" & rows.count).end(xlup).row).formula = "Sumif(A$8:$A$1000,A8,B$8:B$1000)
Application.calculate
Range("C8:C" & Range("C" & Rows.Count).End(xlUp).row).copy
Range("C8:C" & Range("C" & Rows.Count).End(xlUp).row).pastespecial xlPasteValues
Populate the formula to column C in one go (it auto updates the reference automatically)
calculate
Copy the cells
Paste as values
You will find this a LOT quicker than looping the cells.
This code replaces ALL of this:
Do Until IsEmpty(ActiveCell)
Dim ActN As String
ActN = Right(ActiveCell.Value, 4)
Dim TotalAmm
TotalAmm = Application.SumIf(Range("B8:B1000"), Right(ActiveCell.Value, 4), "=ActN")
ActiveCell.Offset(0, 2).Value = TotalAmm
ActiveCell.Offset(1, 0).Select
Loop
This works on what you posted as your solution, I might point out this is different to your original post where your original is trying to sum only on the last 4 charts of the acc no, if you need this post back, we can just create a temp helper column for the formula, do the formula, copy and paste as values then delete the helper column

adding new rows in excel without breaking a vba macro that uses Range.Value

I've written a macro in VBA that simply fills in a given cell's value from another cell in that sheet. I do this for lots of cells in the sheet, and I'm doing it like so:
Range("B3").Value = Range("B200")
Range("B4").Value = Range("B201")
'etc.
Now, I am often adding values by inserting new rows, so I might insert a new row
between B200 and B201, which will break the macro because it doesn't autoupdate when
I insert the new row.
How can I code the macro so it autoupdates the cell references when I insert new rows or columns?
My suggestion would be to make sure the ROW you want to retrieve values from has a unique value in it that you can .FIND anytime you want, then grab your values from column B of that found cell's row. So right now you want to get a value in B200 and A200 always has the text in it: "Final Total" and that is unique.
Dim MyRNG As Range
Set MyRNG = Range("A:A").Find("Final Total", LookIn:=xlValues, LookAt:=xlWhole)
Range("B3").Value = Range("B" & MyRNG.Row)
Range("B4").Value = Range("B" & MyRNG.Row + 1)
This is not an answer but an alternative.
Naming your range is the way to go as Shiin suggested but then if you have 500 cells then like I mentioned earlier, naming 500 cells and using them in your code can be very painful. The alternative is to use smart code. Let's take an example
Let's say you have a code like this
Sub Sample()
Range("B3").Value = Range("B200")
Range("B4").Value = Range("B201")
Range("B5").Value = Range("B201")
' And
' So On
' till
Range("B500").Value = Range("B697")
End Sub
The best way to write this code is like this
Sub Sample()
Dim i As Long
For i = 200 To 697
Range("B" & i - 197).Value = Range("B" & i)
Next i
End Sub
and say if you insert a line at say row 300 then simply break the above code in two parts
Sub Sample()
Dim i As Long
For i = 200 To 299
Range("B" & i - 197).Value = Range("B" & i)
Next i
For i = 301 To 698
Range("B" & i - 197).Value = Range("B" & i)
Next i
End Sub
So every time you insert a row, simply break the for loop into an extra part. This looks tedious but is much better than naming 500 cells and using them in your code.
If you are planning to use the macro only once (i.e for 1 time use) then read ahead.
If you are worried that when the user inserts the row then the cells are not updated then you can instead of assigning a value, assign a formula.
For example
Range("B3").Formula = "=B200"
This will put a formula =B200 in cell B3. So next time when you insert a row so that the 200th row moves it's position, you will notice that the formula automatically gets updated in cell B3
HTH
Try giving a name to the range. If you refer to the range by name Excel searches for it and retrieves the rows that defines it. Range names update their definition when new rows are added.
Adding to the above, i think this tutorial illustrates my point:
http://www.homeandlearn.co.uk/excel2007/excel2007s7p6.html this is how to define the name of the range.
This tutorial explains how to use it on macros and vba:
http://excel.tips.net/T003106_Using_Named_Ranges_in_a_Macro.html
I hope this helps :D

Resources