Sum last 3 columns in Excel using macros - vba

In an Excel File, I need to find out the last 3 columns since because my column length gets updated each month when new data comes and need to do a sum on that last 3 columns values as I want recent data and put the sum in the new column.
EG:
A B C D
1 2 3 4
5 6 7 8
1 2 3 2
In the existing sheet, I want to find B, C, D as recent 3 columns (last 3 columns) and sum it up and put in new column E.
A B C D E
1 2 3 4 9
5 6 7 8 21
1 2 3 2 7
Please help me with ways to automate it with Excel macros. Thanks in advance for your help. I created a code but it's not working
Dim LastCol As Integer
With ActiveSheet
LastCol = .Cells(1, .Columns.Count).End(xlToLeft).column
End With
MsgBox LastCol
Dim LastRow As Integer
With ActiveSheet
LastRow = .Cells(.Rows.Count, "H").End(xlUp).Row
End With
MsgBox LastRow
Dim j As Integer
For i = 2 To LastRow
For j = LastCol - 2 To LastCol
Range(i & LastCol + 1).Value = Range(i & j).Value + Range(i & j + 1).Value + Range(i & j + 2).Value
Next j
Next i

Put this formula in E1
=SUM(OFFSET(E1,0,-3,1,3))
to get the sum of the 3 columns before column E.
Any time you insert new columns before E it will adjust automatically to the last 3 columns right before the sum column.
For more information have a look at OFFSET function.

Use Arrays , Whenever you have to work with multiple cells,rows or columns. Use Arrays. First you hold the value in array and work with the array in memory.
Try to minimize the interaction with the Workbook/Worksheet/sheets.
It will make you code less slow and less error prone.
Here is the code for your reference :
Function getsum()
Dim wb As Workbook
Dim ws As Worksheet
Dim tot_row As Long
Dim row_to_start As Long
Dim cl As Long
Dim irow As Long
Dim sumarr As Variant ' Declare the array
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
sumarr = ws.Range("A1").CurrentRegion ' Put the current region in the array
tot_row = UBound(sumarr, 2) 'Get total rows
row_to_start = tot_row - 2 ' Get the rows from where sum process with start
For cl = row_to_start To UBound(sumarr, 2) ' start from 3rd last column till last column
For irow = LBound(sumarr) + 1 To UBound(sumarr) ' from second row to the last row
msum = msum + sumarr(irow, cl) ' do the sum
Next irow
Next cl
getsum = msum ' return the sum
End Function

Thanks a lot for your precious time to reply.
I have found a solution in Excel VBA macro and it works very fine!!
Dim start As Integer
Dim Mid As Integer
Dim finish As Integer
start = LastCol - 2
Mid = LastCol - 1
finish = LastCol
For i = 2 To LastRow
For j = start To LastCol
If Not IsEmpty(Range("D" & i).Value) Then
Cells(i, LastCol + 1).Formula = "=SUM(" & Cells(i, start).Address & ":" & Cells(i, finish).Address & ")"
End If
Next j
Next i

Related

How to set this dynamic range in vba?

Here are 3 worksheets.
Case:
I want to sum the days for each person. The result is displayed on worksheet-report.
The days information is stored in worksheet-day. The day is not fixed. The range of the days is dynamic which should be according to
row: that person row in worksheet-day
column :Worksheets("button").Cells(1, 2 ) to Worksheets("button").Cells(2, 2)
For example :
Worksheets("button").Cells(1, 2 ) 'store 3
Worksheets("button").Cells(2, 2 ) 'store 5
In column A of worksheet-report ,the sum of day of Peter ,Tom ,Mary should be calculated .The result should be displayed on column B correspondingly .
Then ,their sum of the days should be searched in worksheet-day and add up the total within the range (D:F 'because the value in worksheet("button"))
The result should be :
Peter: 2
Tom: 3
Mary: 3
Lastly,this result displayed on worksheet-report column B correspondingly .
Here is my code:
Sub gg()
row = Worksheets("report").Range("A" & Rows.Count).End(xlUp).row
Row2 = Worksheets("day").Range("A" & Rows.Count).End(xlUp).row
Dim rng As Range 'the range for sum
Dim row999 As Integer 'store the selected row index
For k = 2 To Row2 'check and store appropriate row
If Worksheets("report").Cells(1, k).Value = Worksheets("day").Cells(1, k).Value Then
row999 = ActiveCell.row
End If
Next k
rng = Range(Cells(Worksheets("button").Cells(1, 2), row999), Cells(Worksheets("button").Cells(2, 2), row999))
For j = 2 To row 'do the sum
Worksheets("report").Cells(2, i) = Application.WorksheetFunction.Sum(rng)
Next j
End Sub
I guess the rng is inaccurate here.
rng = Range(Cells(Worksheets("button").Cells(1, 2), row999), Cells(Worksheets("button").Cells(2, 2), row999))
The problem that you are having is that the names do not match. Tom is uppercase on Report and lower case on day. Mary has an extra space on the Day worksheet "mary ".
Try to use shorter variable names when possible so that you can read each line of code without having to scan back and forth with your eyes.
Sub ProcessDays()
Dim day1 As Integer, day2 As Integer
Dim i As Integer, j As Integer
Dim name1 As String, name2 As String
With Worksheets("button")
day1 = .Cells(1, 2)
day2 = .Cells(2, 2)
End With
With Worksheets("day")
For i = 2 To Worksheets("report").Range("A" & Rows.Count).End(xlUp).Row
For j = 2 To .Range("A" & Rows.Count).End(xlUp).Row
name1 = UCase(Trim(Worksheets("report").Cells(i, 1)))
name2 = UCase(Trim(.Cells(j, 1)))
If name1 = name2 Then
Set rng = .Range(.Cells(j, day1), .Cells(j, day2))
Worksheets("report").Cells(i, 2) = WorksheetFunction.Sum(rng)
End If
Next
Next
End With
End Sub
The first parameter passed to Cells is the row, and the second parameter is the column.
You should also qualify which worksheet the range is on.
So your line saying:
rng = Range(Cells(Worksheets("button").Cells(1, 2), row999), Cells(Worksheets("button").Cells(2, 2), row999))
should say
rng = Worksheets("day").Range(Cells(row999, Worksheets("button").Cells(1, 2)), _
Cells(row999, Worksheets("button").Cells(2, 2)))
However, you have a lot of other errors in your code. I believe what you want is:
Sub gg()
Dim lastrowReport As Long
Dim lastrowDay As Long
Dim rowReport As Long
Dim rowDay As Long
lastrowReport = Worksheets("report").Range("A" & Rows.Count).End(xlUp).row
lastrowDay = Worksheets("day").Range("A" & Rows.Count).End(xlUp).row
Dim rng As Range 'the range for sum
For rowReport = 2 To lastrowReport ' loop through each of the rows on the report
For rowDay = 2 To lastrowDay ' find the entry for this person on the days sheet
If Worksheets("report").Cells(rowReport, 1).Value = Worksheets("day").Cells(rowDay, 1).Value Then
set rng = Worksheets("day").Range(Worksheets("day").Cells(rowDay, Worksheets("button").Cells(1, 2)), _
Worksheets("day").Cells(rowDay, Worksheets("button").Cells(2, 2)))
Worksheets("report").Cells(rowReport, 2) = Application.WorksheetFunction.Sum(rng)
Exit For
End If
Next
Next
End Sub

Upon change in row value, Do a sumif one row back in another column

I hope you can help me my VBA question. I would like to use a loop going down column A. Once a change is detected, I would like to insert a SUMIF formula in column C to total of column B if grouped the column A (Total offset to the right). A is already sorted. Kind of like Subtotal but without using the Subtotaling row.
A B C
1 2
1 6
1 3 11 =SUMIF(A:A,A3,B:B)
2 7
2 8 15 =SUMIF(A:A,A5,B:B)
3 8
3 6 14 =SUMIF(A:A,A7,B:B)
(without the added blank rows between 1 & 2 and 2 & 3 changes) I believe I am part of the way there with the following pieces of code pieces, but am having trouble getting to work together.
Sub SUMIF_Upon_Change()
Dim r As Long, mcol As String, i As Long
' find last used cell in Column A
r = Cells(Rows.Count, "A").End(xlUp).Row
' get value of last used cell in column A
mcol = Cells(r, 1).Value
' insert rows by looping from bottom
For i = r To 2 Step -1
If Cells(i, 1).Value <> mcol Then
Cells(n, 3).Formula = _
"=SUMIF(A:A,RC[-1],B:B)"
'AND / OR This added at the change row minus one row.
'Places formula in each adjacent cell (in column "D").
Range("C2:C" & Range("A" & Rows.Count).End(xlUp).Row).Formula = "=SUMIF(A:A,A3,BB)"
End If
Next i
End Sub
Any help will be much appreciated.
XLMatters, You pretty much have it. Your only major issue is that you seem to be mixing formula reference styles ("RC[-1]" and "A3"). You have to pick one or the other. Here is a working example of your code with a few minor modifications:
Sub SUMIF_Upon_Change()
Dim r As Long, mcol As String, i As Long
' find last used cell in Column A
r = Cells(Rows.Count, "A").End(xlUp).Row
' get value of last used cell in column A
mcol = Cells(r, 1).Value
For i = r To 2 Step -1
If Cells(i, 1).Value <> Cells(i + 1, 1).Value Then
Cells(i, 3).Formula = "=SUMIF(A:A,A" & i & ",B:B)"
End If
Next i
End Sub
If your heart is set on FormulaR1C1 style, here you go:
Sub SUMIF_Upon_ChangeRC()
Dim r As Long, mcol As String, i As Long
' find last used cell in Column A
r = Cells(Rows.Count, "A").End(xlUp).Row
' get value of last used cell in column A
mcol = Cells(r, 1).Value
For i = r To 2 Step -1
If Cells(i, 1).Value <> Cells(i + 1, 1).Value Then
Cells(i, 3).FormulaR1C1 = "=SUMIF(C1,RC1,C2)"
End If
Next i
End Sub
Should you have any other questions, just ask.

search compare columns sheet 1 & sheet 2 and change a cell in sheet 1

I really need some help.
Problem: I have a workbook with 2 worksheets. Both sheets has headers. Sheet1 is a list of account numbers in column A and the same for sheet 2 column A. Now, what I need to do is this:
if I place a date in column AI in sheet 2 for a specific account number, then find the corresponding account number in sheet 1 and place the word "Complete" in column Y for that account.
I hope I explained this enough. Below is what I came up with so far, but got stuck:
Sub UpdateTBP()
Dim i
Dim j
Dim k
Dim LastRow
Dim LastRow2
LastRow = Sheets("Portfolio").Cells(Rows.Count, 1).End(xlUp).Row
LastRow2 = Sheets("TBP").Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To (LastRow - 1)
For j = 2 To (LastRow2 - 1)
If Sheets("Portfolio").Cells(i, 1) = Sheets("TBP").Cells(j, 1).value Then
For k = 35 To 35
If Sheets("TBP").Cells(j, 35) <> "" Then
Sheets("Portfolio").Cells(i, Y).value = "Complete"
End If
Next
End If
Next
Next
ThisWorkbook.Save
End Sub
I was able to make it work by using the following modified code:
Sub UpdateTBP()
Dim i
Dim j
Dim k
Dim LastRow
Dim LastRow2
LastRow = Sheets("Portfolio").Cells(Rows.Count, 1).End(xlUp).Row
LastRow2 = Sheets("TBP").Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To (LastRow - 1)
For j = 2 To (LastRow2 - 1)
If Sheets("Portfolio").Cells(i, 1).Value = Sheets("TBP").Cells(j, 1).Value Then
If Sheets("TBP").Cells(j, 35) <> "" Then
Sheets("Portfolio").Cells(i, 25).Value = "Complete"
End If
End If
Next
Next
ThisWorkbook.Save
End Sub
Please note that this will not include your last row of data as you have subtracted it out with:
For i = 2 To (LastRow - 1)
For j = 2 To (LastRow2 - 1)
If you wish to include that last row, just use the following:
For i = 2 to LastRow
For j = 2 to LastRow2
This includeds the previous comments I made. I just gave it a quick test and it is working.

VBA Excel "random" two column generator

I'm generating a "random" (with no repeats) list of the questions using the following:
Sub randomCollection()
Dim Names As New Collection
Dim lastRow As Long, i As Long, j As Long, lin As Long
Dim wk As Worksheet
Set wk = Sheets("Sheet1")
With wk
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
For i = 2 To lastRow
Names.Add wk.Cells(i, 1).Value, CStr(wk.Cells(i, 1).Value)
Next i
lin = 1
For i = lastRow - 1 To 1 Step -1
j = Application.WorksheetFunction.RandBetween(1, i)
lin = lin + 1
Range("B" & lin) = Names(j)
Names.Remove j
Next i
End Sub
I'm stuck on how to pick up data in column B, and generate it with the corresponding data in column A.
For example, A1 and B1 need to stay together on the "random" list, as does A2, B2, etc.
If I understand your task correctly, you want to take whatever is in column A and put it in column B in random locations, not including a header row. If this is the case, try this:
Sub randomCollection()
Dim wrk As Worksheet, source As Long, dest As Long, lastRow As Long, i As Long, rowCount As Long
Set wrk = ActiveWorkbook.ActiveSheet
lastRow = wrk.Rows.Count
lastRow = wrk.Range("A1:A" & Trim(Str(lastRow))).End(xlDown).Row
'First, clear out the destination range
wrk.Range("B2:B" + Trim(Str(lastRow))).Clear
source = 2
Do Until source > lastRow
dest = Application.WorksheetFunction.RandBetween(1, lastRow - source + 1)
'Find the blank row corresponding to it
rowCount = 1
For i = 2 To lastRow
If dest = rowCount And wrk.Cells(i, 2) = "" Then
wrk.Cells(i, 2) = wrk.Cells(source, 1)
Exit For
End If
If wrk.Cells(i, 2) = "" Then '2 is column B
rowCount = rowCount + 1
End If
Next
source = source + 1
Loop
End Sub
This looks for the first random blank space in column B to put each cell in column A.

Taking rows of data and converting into columns with consecutive rows

I've seen some similar posts but not quite what I need or could understand to solve my simple problem.
I have hundreds of rows of data that I'd like to transform into columns. Original data is like so with two empty rows between and the sets of related data can vary in length:
9
8
7
6
5
4
3
2
1
J
I
H
G
F
E
D
C
B
A
I'd like to be able to reverse the order of each set and then transpose them in columns going down another row for each data set like so:
1 2 3 4 5 6 7 8 9
A B C D E F G H I J
I had some success with the first part using a simple formula =OFFSET($A$2,COUNTA(A:A)-ROW(),0) because I wasn't sure how to do it in VBA.
The code I'm using to grab all the data and then transpose, I'm having trouble getting it to go down a row for each unique data set. Here's the code I'm trying to use, but it doesn't seem to work and just start running down the worksheet until the macro craps out.
Sub TransposeRange()
Dim InRange As Range
Dim OutRange As Range
Dim i As Long
Set InRange = Sheets("Output").Range("A3:A10002")
Set OutRange = Sheets("Output").Range("H2:NTR2")
For i = 1 To 10000 Step 1
OutRange.Cells(1, i) = InRange.Cells(i, 1)
ActiveCell.Offset(1, 0).Select
Next i
End Sub
I'm sure there's something obvious and simple I'm missing but alas I'm still a noob in training. Any suggestions would be greatly appreciated.
Assuming your data is at column A, please try the following using sort then pastespecial with transpose: (please change sheets name according to your own)
Sub sortNtranspose()
Dim r As Integer
Dim i As Integer
Dim j As Integer
Dim rn As Range
r = Sheets("Sheet1").UsedRange.Rows.Count
For i = 1 To r
Set rn = Range(Cells(i, 1), Cells(Cells(i, 1).End(xlDown).Row, 1))
rn.Sort key1:=Cells(i, 1), order1:=xlAscending, Header:=xlNo
Set rn = Range(Cells(i + 1, 1), Cells(Cells(i, 1).End(xlDown).Row, 1))
rn.Copy
Cells(i, 2).PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=True
Do While Not IsEmpty(Cells(i, 1))
If IsEmpty(Cells(i, 2)) Then
Cells(i, 2).EntireRow.Delete
Else:
i = i + 1
End If
Loop
r = Sheets("Sheet1").UsedRange.Rows.Count
If j >= r Then
Exit Sub
End If
j = Cells(i, 1).End(xlDown).Row
i = j - 1
Next i
End Sub
This code assumes that your data are constants, and uses VBA's wonderful SpecialCells property to break out each chunk in column 1. It also uses an array, which is much faster than looping through cells:
Sub TransposeColumnSections()
Dim ws As Excel.Worksheet
Dim LastRow As Long
Dim ColumnConstants As Excel.Range
Dim i As Long
Dim ColumnArea As Excel.Range
Dim AreaRowsCount As Long
Dim ReversedConstants() As Variant
Dim j As Long
Set ws = ActiveSheet
With ws
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
Set ColumnConstants = .Columns(1).SpecialCells(xlCellTypeConstants)
For i = 1 To ColumnConstants.Areas.Count
Set ColumnArea = ColumnConstants.Areas(i)
AreaRowsCount = ColumnArea.Rows.Count
ReDim ReversedConstants(1 To AreaRowsCount)
For j = AreaRowsCount To 1 Step -1
ReversedConstants(AreaRowsCount - (j - 1)) = ColumnArea(j).Value
Next j
.Cells(i, 2).Resize(1, AreaRowsCount) = ReversedConstants
Next i
.Columns(1).Delete
End With
End Sub