I'm trying to calculate the Average value of multiple ranges as shown in attached Fig.
Conditions -
It should match the cell value of column "L" and "M" with a range of column "A" and Make a range (e.g 322810 to 324900) to calculate the average of column B values which are against the specific range (e.g 322810 to 324900).
I've been able to write the following code but it obviously not working.
Dim lastrow As Long
Dim i As Long, j As Long
With Worksheets("Source")
lastrow = .Range("A" & .Rows.Count).End(xlUp).Row
For i = 2 To lastrow + 1 'loop whole range (column C)
If .Cells(i, "L") = .Range("A").Value Then 'If column L cell value match with any cell of Range "A"
For j = i To lastrow 'Loop "group" range.
If .Cells(j, "M") = .Range("A").Value Then ' (end of small group range) then apply formula
.Cells(i, "N").Formula = "=AVERAGE(B" & i & ":B" & j & ")" 'AVG
Exit For
End If
Next j
End If
Next I
End With
All kind of help will be appreciated (Formula or VBA Code)
Yes, BigBen is right. This is the way. The Formula in my example is
=AVERAGEIFS($B$3:$B$16,$A$3:$A$16,">="&L4,$A$3:$A$16,"<="&M4)
Try,
Sub test()
Dim Lastrow As Long
Dim i As Long, j As Long
Dim r As Long
Dim mPoint As Long
Dim Ws As Worksheet
Dim vDB, vR()
Dim rngStart As Range, rngEnd As Range
Dim rngDB As Range
Set Ws = Worksheets("Source")
With Ws
Lastrow = .Range("A" & .Rows.Count).End(xlUp).Row
vDB = .Range("L3", .Range("m" & .Rows.Count).End(xlUp))
r = UBound(vDB, 1)
ReDim vR(1 To r, 1 To 1)
For i = 1 To r
For k = 1 To Lastrow
If .Range("a1").Cells(k) = vDB(i, 1) Then
Set rngStart = .Range("a1").Cells(k)
mPoint = rngStart.Row
Exit For
End If
Next k
If rngStart Is Nothing Then
Else
For k = mPoint To Lastrow
If .Range("a1").Cells(k) = vDB(i, 2) Then
Set rngEnd = .Range("a1").Cells(k)
Exit For
End If
Next k
End If
If rngStart Is Nothing Or rngEnd Is Nothing Then
Else
Set rngDB = .Range(rngStart, rngEnd).Offset(, 1)
Debug.Print rngDB.Address
vR(i, 1) = WorksheetFunction.Average(rngDB)
End If
Set rngStart = Nothing
Set rngEnd = Nothing
Next i
.Range("n3").Resize(r) = vR
End With
End Sub
Related
I am trying to write a macro that will look in column A on sheet1 and see if it is missing any values from column A on sheet2 or column A on sheet3. If it is missing have the value added to the bottom of the column A on sheet1. The same value may exist on sheet2 and sheet3 but it only needs to be represented once on sheet1.
I'm working with the code below.
Sub newRow()
Dim rngSh1 As Range, rngSh2 As Range, rngSh3 As Range, mySelSh2 As Range, mySelSh3 As Range
Dim lastRowSh1 As Long, lastRowSh2 As Long, lastRowSh3 As Long
Dim wb As Worksheet
Dim cell As Range
Set wb = ThisWorkbook
With wb
lastRowSh1 = Worksheets("Sheet1").Range("A" & .Rows.Count).End(xlUp).Row
lastRowSh2 = Worksheets("Sheet2").Range("A" & .Rows.Count).End(xlUp).Row
lastRowSh3 = Worksheets("Sheet3").Range("A" & .Rows.Count).End(xlUp).Row
Set rngSh1 = Worksheets("Sheet1").Range("A1:A" & lastRowSh1)
Set rngSh2 = Worksheets("Sheet2").Range("A1:A" & lastRowSh2)
Set rngSh3 = Worksheets("Sheet3").Range("A1:A" & lastRowSh3)
End With
For Each cell In rngSh2.Cells
If IsError(Application.Match(cell.Value, rngSh1, 0)) Then
If mySelSh2 Is Nothing Then
Set mySelSh2 = cell
Else
Set mySelSh2 = Union(mySelSh2, cell)
End If
End If
Next cell
If Not mySelSh2 Is Nothing Then mySelSh2.Copy Destination:=Worksheets("Sheet1").Range("A" & lastRowSh1 + 1)
For Each cell In rngSh3.Cells
If IsError(Application.Match(cell.Value, rngSh1, 0)) Then
If mySelSh3 Is Nothing Then
Set mySelSh3 = cell
Else
Set mySelSh3 = Union(mySelSh3, cell)
End If
End If
Next cell
If Not mySelSh3 Is Nothing Then mySelSh3.Copy Destination:=Worksheets("Sheet1").Range("A" & lastRowSh1 + 1)
End Sub
I've made every adjustment I can think of but with every change I make I get a different error.
Any help would be greatly appreciated. Thanks!
Save yourself a little bit of time using a Scripting.Dictionary:
Option Explicit
Sub test()
Dim dict As New Scripting.dictionary, sheetNum As Long
For sheetNum = 2 To Sheets.Count
With Sheets(sheetNum)
Dim lastRow As Long: lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
Dim rowNum As Long
For rowNum = 1 To lastRow
Dim dictVal As Long: dictVal = .Cells(rowNum, 1).Value
If Not dict.Exists(dictVal) Then dict.Add dictVal, 0
Next rowNum
End With
Next sheetNum
With Sheets(1)
Dim checkableRangeLastRow As Long: checkableRangeLastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
Dim checkableRange As Range: Set checkableRange = .Range(.Cells(1, 1), .Cells(checkableRangeLastRow, 1))
Dim dictKey As Variant
For Each dictKey In dict.Keys
If IsError(Application.Match(dictKey, checkableRange, 0)) = True Then
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
.Cells(lastRow + 1, 1).Value = dictKey
End If
Next dictKey
End With
End Sub
You add all values in your not-master-sheet into dict then loop through that list; if it's not found in your master-sheet, then you add then to the end of the list.
A significant note is that the Type of value used as the dictVal may cause the IsError() statement to always be True if it is not the same Type as the data being assessed in the checkableRange.
I want to create a variant array when using a union to join ranges.
If I select one of the ranges the variant array will work.
When I union, I only receive the row dimensions and not the column dimensions.
For example,
Sub arrTest()
'Declare varbs
Dim ws As Worksheet
Dim myArr() As Variant
Dim lRow As Integer
Dim myRng As Range
'Assign varbs
Set ws = ThisWorkbook.Worksheets("Sheet1")
With ws
lRow = .Cells(Rows.count, "C").End(xlUp).row
Set myRng = Application.Union(.Range("G3:G" & lRow), .Range("J3:O" & lRow), .Range("AD3:AE" & lRow), .Range("AI3:AI" & lRow))
myArr = myRng.Value2
End With
Will return a variant of
myArr(1, 1)
myArr(2, 1)
myArr(1, 3)
However if I were to select one of the ranges within the union for example:
Sub arrTest()
'Declare varbs
Dim ws As Worksheet
Dim myArr() As Variant
Dim lRow As Integer
Dim myRng As Range
'Assign varbs
Set ws = ThisWorkbook.Worksheets("Sheet1")
With ws
lRow = .Cells(Rows.count, "C").End(xlUp).row
Set myRng = .Range("J3:O" & lRow)
myArr = myRng.Value2
End With
I properly get
myArr(1, 1)
myArr(1, 2)
myArr(1, 3)
etc.
How do I return the column dimensions as well, without looping through the sheet?
Like this:
Sub ArrayTest()
Dim ws As Worksheet
Dim arr, lrow As Long
Set ws = ThisWorkbook.Worksheets("Sheet1")
lrow = ws.Cells(Rows.Count, "C").End(xlUp).Row
arr = GetArray(ws.Range("G3:G" & lrow), ws.Range("J3:O" & lrow), _
ws.Range("AD3:AE" & lrow), ws.Range("AI3:AI" & lrow))
With ThisWorkbook.Worksheets("Sheet2").Range("B2")
.Resize(UBound(arr, 1), UBound(arr, 2)).Value = arr
End With
End Sub
'Given a number of input ranges each consisting of one or more columns (assumed all input ranges have
' the same # of rows), return a single 1-based 2D array with the data from each range
Function GetArray(ParamArray sourceCols() As Variant) As Variant
Dim arr, rng, numCols As Long, numRows As Long, r As Long, c As Long, tmp, col As Long
numRows = sourceCols(0).Rows.Count
'loop over ranges and get the total number of columns
For Each rng In sourceCols
numCols = numCols + rng.Columns.Count
Next rng
ReDim arr(1 To numRows, 1 To numCols) 'size the output array
c = 0
For Each rng In sourceCols 'loop the input ranges
tmp = As2DArray(rng) 'get range source data as array ####
For col = 1 To UBound(tmp, 2) 'each column in `rng`
c = c + 1 'increment column position in `arr`
For r = 1 To numRows 'fill the output column
arr(r, c) = tmp(r, col)
Next r
Next col
Next rng
GetArray = arr
End Function
'Get a range's value, always as a 2D array, even if only a single cell
Function As2DArray(rng)
If rng.Cells.Count > 1 Then
As2DArray = rng.Value
Else
Dim arr(1 To 1, 1 To 1)
arr(1, 1) = rng.Value
As2DArray = arr
End If
End Function
I have a task to add a comment in a sumtotal by month to show the items involved and the amount. I Want to simplify the works because Ido it by monthly and every job (over 20 jobs)!
I found solutions for a single cell.
I need to add related cell values to one comment for the month.
Option Explicit
Sub CreateComment()
Dim rng As Range
Dim cel As Range
Dim myColumn, myRow As Integer
Set rng = Selection
myColumn = ActiveCell.Column
myRow = ActiveCell.Row
For Each cel In rng
If cel.Value <> "" Then
Range("myColumn" & "1").AddComment [Cell("myRow", "1")).Value & " -$" & Cell("myRow","myColumn")_.value]
End If
Next
End Sub
Add Comments to a Range
Option Explicit
Sub AddComments()
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim lRow As Long: lRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim lCol As Long: lCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
Dim srg As Range: Set srg = ws.Range("A1").Resize(lRow, lCol)
Dim Data As Variant: Data = srg.Value
Dim r As Long, c As Long, n As Long
Dim Comm As String
For c = 2 To lCol
For r = 4 To lRow
If Len(Data(r, c)) > 0 Then
n = n + 1
Comm = Comm & n & ". " & Data(r, 1) & " - " _
& Format(Data(r, c), "$#,##0") & vbLf
End If
Next r
If n > 0 Then
With srg.Cells(1, c)
.ClearComments
.AddComment Left(Comm, Len(Comm) - 1)
End With
n = 0
Comm = ""
End If
Next c
MsgBox "Comments added.", vbInformation
End Sub
I have created below code which works like IF Col"B" any cell <> "" And Col"L" any cell = "Leop" then add row below to the active cell.
I mean I'm trying to achieve is to insert single row after certain row which contain in column B any value, and if column L in same row contains value = "Leop". Then add the row after that certain row.
But an error is appear. Compile Error: Invalid use of property on xlDown
Your help will be appreciated to fix it.
From this:
to this:
Sub firstcondition()
Dim ws As Worksheet
Dim LRow As Long
Dim rng As Range
Dim rng2 As Range
Dim i As Long
Dim p As Long
Dim dat As Variant
Dim datt As Variant
Dim IRow As Long
Set ws = Thisworkbooks.Sheets("Sheet2")
With ws
LRow = .Range("A" & .Rows.Count).End(xlUp).Row
Set rng = .Range("B2:B" & LRow)
Set rng2 = .Range("L2:L" & LRow)
dat = rng
datt = rng2
IRow = Selection.Row
For i = LBound(dat, 1) To UBound(dat, 1)
For p = LBound(datt, 1) To UBound(datt, 1)
If dat(i, 1) <> "" And datt(p, 1) = "Leop" Then
Rows(IRow + 1).Select
Selection.Insert Shift: xlDown
End If
End Sub
It will be like in formula:
IF(AND(B2<>"",L2="Leop"),"InsertRowBelow to Row 2 If condition is met","")
and will drag it down to the lastRow.
Thisworkbooks.Sheets("Sheet2") should be Thisworkbook.Sheets("Sheet2") and missing = in Selection.Insert Shift:= xlDown
Inserting or deleting rows will change the last row number so start at the bottom and work upwards.
Option Explicit
Sub firstcondition()
Dim ws As Worksheet, LRow As Long, r As Long
Dim n As Long
Set ws = ThisWorkbook.Sheets("Sheet2")
With ws
LRow = .Range("B" & .Rows.Count).End(xlUp).Row
For r = LRow To 2 Step -1
If .Cells(r, "B") <> "" And .Cells(r, "L") = "Leop" Then
.Rows(r + 1).Insert shift:=xlDown
n = n + 1
End If
Next
End With
MsgBox n & " rows inserted", vbInformation
End Sub
Try this with autofilter, you dont have to loop through each row. So it will work faster for larger data.
Option Explicit
Sub firstcondition()
Dim ws As Worksheet
Dim LRow As Long, cl As Range
Set ws = ThisWorkbook.Sheets("Sheet2")
LRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
ws.Range("L1:L" & LRow).AutoFilter 1, "Leop"
For Each cl In ws.Range("_FilterDatabase").SpecialCells(12).Cells
If ws.Range("B" & cl.Row) <> "" Then
cl.Offset(1).EntireRow.Insert Shift:=xlDown
End If
Next
ws.AutoFilterMode = False
End Sub
I'm trying to insert a blank row between 2 rows if the values in A1 <> A2. I was given the code below and it works if the data looks like this a,a,b,b,c,c,d,d,e,e,f,f,g,g,h,h,i,i. The results are exactly what I'm looking for: a, blank row, a, blank row, b blank row, b, blank row, c, blank row, c, etc.
However, if the data looks like this a,b,c,d,e,f,g,h,i then I get 8 blank rows between a and b but none anywhere else.
Any ideas why this is happening like this?
Sub Social_Distance()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet2")
Dim lr As Long, MyUnion As Range, xCell As Range
lr = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
For Each xCell In ws.Range("A2:A" & lr)
If xCell.Value <> xCell.Offset(1).Value Then
If Not MyUnion Is Nothing Then
Set MyUnion = Union(MyUnion, xCell.Offset(1))
Else
Set MyUnion = xCell.Offset(1)
End If
End If
Next xCell
If Not MyUnion Is Nothing Then MyUnion.EntireRow.Insert Shift:=xlDown
End Sub
Maybe this is what you need, It works well with different consecutive value cases:
Note: when two consecutive cells (in the same column) are added, it will be treated as one cell (this is how union works) so you will get the Position of another Cell in the same Row but diferrent Column, now the result: union = yellow range [see an image]
Sub Social_Distance()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet2")
Dim lr As Long, MyUnion As Range, xCell As Range
lr = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
k = 1
Dim cell As Range
For Each xCell In ws.Range("A2:A" & lr)
If xCell.Value <> xCell.Offset(1).Value Then
If Not MyUnion Is Nothing Then
Set MyUnion = Union(MyUnion, xCell.Offset(1, k)): k = k + 1 'increase column index
Else
Set MyUnion = xCell.Offset(1, 0)
End If
End If
Next xCell
If Not MyUnion Is Nothing Then MyUnion.EntireRow.Insert Shift:=xlDown
End Sub
For example:
this uses a helper column and sorting to put in blank rows:
Sub Social_Distance()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet14")'change to your sheet
Dim lr As Long
lr = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Dim rngArr As Variant
rngArr = ws.Range(ws.Cells(1, 1), ws.Cells(lr, 1)).Value
Dim rowArr() As Variant
ReDim rowArr(1 To lr - 1, 1 To 1)
Dim x As Double
x = 0#
Dim i As Long
For i = 2 To lr
If rngArr(i, 1) <> rngArr(i - 1, 1) Then x = x + 1
rowArr(i - 1, 1) = x
Next i
Dim newLineArray() As Variant
ReDim newLineArray(1 To Int(x - 1), 1 To 1)
For i = 1 To Int(x - 1)
newLineArray(i, 1) = CDbl(i) + 0.1
Next i
ws.Columns(1).Insert
ws.Range("A1").Value = "Temp"
ws.Range("A2").Resize(lr - 1).Value = rowArr
ws.Range("A" & lr + 1).Resize(Int(x - 1)).Value = newLineArray
ws.UsedRange.Sort key1:=ws.Range("A1"), Header:=xlYes
ws.Columns(1).Delete
End Sub