Need to write months in a sequential order - vba

I have a list of columns with Month/Year as title (like JAN09, FEB09, AUG10). I have to check if the months are sequentially aligned. if not then align it and if a specific month is not available then create a column name title as the month name and go ahead. I have written a code but it works for the first year (like from year 09-11, it will identify all the months of 09 but after that it fails to identify and creates a new month every time even if it is present).
Sub MonthFinder()
Dim montharray As Variant
montharray = Array("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC")
lastrow = ActiveSheet.UsedRange.Rows.Count + 5
lastcol = ActiveSheet.UsedRange.Columns.Count
minmonth = Right(Cells(5, 2), 2)
range0 = 2
maxmonth = Right(Cells(5, 2), 2)
Do Until range0 > lastcol
If Right(Cells(5, range0), 2) < minmonth Then
minmonth = Right(Cells(5, range0), 2)
End If
If Right(Cells(5, range0), 2) > maxmonth Then
maxmonth = Right(Cells(5, range0), 2)
End If
range0 = range0 + 1
Loop
minsortmonth = minmonth
maxsortmonth = maxmonth
place = 2
Do Until minsortmonth = maxsortmonth + 1
arraycount = 0
Do Until arraycount = 12
range1 = 2
lastcol = ActiveSheet.UsedRange.Columns.Count
Do Until Left(Cells(5, range1), 3) = montharray(arraycount) And Right(Cells(5, range1), 2) = minsortmonth Or range1 > lastcol
range1 = range1 + 1
Loop
If range1 > lastcol Then
Range(Cells(5, place), Cells(lastrow, place)).Select
Selection.Insert Shift:=xlToRight
Cells(5, place).Value = montharray(arraycount) & minsortmonth
Else
If range1 <> place Then
Range(Cells(5, range1), Cells(lastrow, range1)).Cut
Cells(5, place).Select
Selection.Insert Shift:=xlToRight
End If
End If
arraycount = arraycount + 1
place = place + 1
Loop
minsortmonth = minsortmonth + 1
Loop
End Sub

I'd suggest to change the logic you do and to use Dictionary.
Let's say your data (columns) are initially sorted as follow: JAN09, FEB09, APR09...MAR00, MAY00, JUL00, ..., etc. There are some gaps in months collection and you want to fill in missing months. Here is an idea:
'Note: columns are initially sorted!
Sub CheckMonthSequence()
Dim dic As Dictionary
Dim element As Variant
Dim col As Integer, pos As Integer, rng As Range
Dim initialDate As Date, endDate As Date, sTmp As String
Set rng = ThisWorkbook.Worksheets("Sheet1").Range("B5")
'define initial date as January of ...
sTmp = rng
sTmp = "01/01/" & "20" & Right(sTmp, 2)
initialDate = CDate(sTmp)
'define end date
sTmp = rng.End(xlToRight)
sTmp = Left(sTmp, 3) & "/01/" & "20" & Right(sTmp, 2)
endDate = CDate(sTmp)
'create new dictionary with collection of months
Set dic = GetMonthsAsDictionary(initialDate, endDate)
'define a range of columns to sort and update
col = 0
Do While rng.Offset(, col) <> ""
element = rng.Offset(, col)
If dic.Exists(element) Then
pos = dic.Item(element)
If pos > col Then
Do While col < pos
rng.Offset(, col).EntireColumn.Insert xlShiftToRight
'sometimes it loses the reference, so...
Set rng = ThisWorkbook.Worksheets("Sheet1").Range("B5")
rng.Offset(, col) = GetKeyByIndex(dic, col)
col = col + 1
Loop
col = pos
End If
End If
col = col + 1
Loop
End Sub
'needs reference to MS Scripting Runtime
Function GetMonthsAsDictionary(ByVal StartingMonth As Date, EndMonth As Date) As Dictionary
Dim dic As Dictionary
Dim i As Integer, j As Integer
'create new dictionary
Set dic = New Dictionary
i = 0
j = DateDiff("M", StartingMonth, EndMonth)
For i = 0 To j
dic.Add UCase(Format(DateAdd("M", i, StartingMonth), "MMMyy")), i
Debug.Print UCase(Format(DateAdd("M", i, StartingMonth), "MMMyy")), i
Next
Set GetMonthsAsDictionary = dic
End Function
Function GetKeyByIndex(ByVal dic As Dictionary, ByVal ind As Integer) As String
Dim dic_Keys As Variant, element As Variant
dic_Keys = dic.keys
For Each element In dic_Keys
If dic.Item(element) = ind Then
Exit For
End If
Next
GetKeyByIndex = element
End Function
As you can see, above code:
1) create dictionary which contains months and corrensponding index.
2) loops through the collection of columns
3) checks that value corresponds to index in a dictionary
4) fill in header when it's necessary.
I know, it's not perfect, but good point to start.
CheersMaciej
[EDIT]
Using your logic, the code might look like:
Option Explicit 'do not apply initialize variable without its declaration
Sub MonthFinder()
Dim montharray As Variant, rng As Range
Dim firstyear As Integer, lastyear As Integer, curryear As Integer
Dim curroffset As Integer, lastcol As Integer, currmonth As Integer
curroffset = 0
montharray = Array("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC")
'start here:
Set rng = ThisWorkbook.Worksheets("Sheet1").Range("B5")
'first year
firstyear = CInt(Right(rng, 2))
'fid last col
lastcol = rng.End(xlToRight).Column - rng.Column
'find last year
lastyear = CInt(Right(rng.Offset(ColumnOffset:=lastcol), 2))
For curryear = firstyear To lastyear
For currmonth = LBound(montharray) To UBound(montharray)
'if current month is equal to last month - exit for
If CStr(montharray(currmonth) & curryear) = CStr(rng.End(xlToRight)) Then Exit For
'month is proper - do nothing
If rng.Offset(ColumnOffset:=curroffset) = CStr(montharray(currmonth) & curryear) Then GoTo SkipMonth
'other cases
rng.Offset(ColumnOffset:=curroffset).EntireColumn.Insert xlShiftToRight
Set rng = ThisWorkbook.Worksheets("Sheet1").Range("B5")
rng.Offset(ColumnOffset:=curroffset) = CStr(montharray(currmonth) & curryear)
SkipMonth:
curroffset = curroffset + 1
Next
Next
Set rng = Nothing
End Sub
Cheers,
Maciej

Related

Excel VBA: Issues with setting Range Union

I have the following code created by #ScottCraner which populates cells Q8:Q12 with the dates of each friday this month.
Sub myFri()
Dim OArr(1 To 5, 1 To 1) As Variant
Dim k As Long
k = 1
Dim i As Long
For i = DateSerial(Year(Date), Month(Date), 1) To DateSerial(Year(Date), Month(Date) + 1, 0)
If Weekday(i, vbSunday) = 7 Then
OArr(k, 1) = i
k = k + 1
End If
Next i
If k = 5 Then OArr(k, 1) = "-"
Worksheets("Sheet1").Range("Q8:Q12").Value = OArr
Worksheets("Sheet1").Range("Q8:Q12").NumberFormat = "mm/dd/yyyy"
End Sub
I have adjusted this to try and set the range to different sections of the sheet.
Ive done this with a Union range as follows:
Private Sub DateRangePayer1()
Dim rng1, rng2, rng3, rng4, UnionRange As Range
Set rng1 = Range("Q8:Q12")
Set rng2 = Range("T8:T12")
Set rng3 = Range("Q16:Q20")
Set rng4 = Range("T16:T20")
Set UnionRange = Union(rng1, rng2, rng3, rng4)
Dim OArr(1 To 5, 1 To 1) As Variant
Dim k As Long
k = 1
Dim i As Long
For i = DateSerial(Year(Date), Month(Date), 1) To DateSerial(Year(Date), Month(Date) + 1, 0)
If Weekday(i, vbSunday) = 6 Then
OArr(k, 1) = i
k = k + 1
End If
Next i
If k = 5 Then OArr(k, 1) = "-"
UnionRange.Value = OArr
UnionRange.NumberFormat = "dd-mmmm"
End Sub
Unfortunately, its currently not working as expected and is populating the cells with the following format:
It populates ranges Q8:Q12 and Q16:Q20 perfectly however, when filling in row T, it loops through the first friday of this month only.
Thank you all for your help with this so far. Youve all been amazingly helpful and all your time is appreciated. Special thanks to #ScottCraner for all your help with everything I have submitted so far.
You cannot fill a discontiguous union range with one array like that. Probably best to use 5 arrays or one array and slice off the pieces or run through the Areas of the unioned range.
Private Sub dateRangePayer1()
Dim unionRange As Range, uRng As Range
Dim d As Long, k As Long
Set unionRange = Worksheets("sheet8").Range("Q8:Q12, T8:T12, Q16:Q20, T16:T20")
'Set unionRange = ActiveSheet.Range("Q8:Q12, T8:T12, Q16:Q20, T16:T20") deals with the active sheet
ReDim OArr(1 To 5, 1 To 1) As Variant
For d = DateSerial(Year(Date), Month(Date), 1) To DateSerial(Year(Date), Month(Date) + 1, 0)
If Weekday(d, vbSunday) = 6 Then
k = k + 1
OArr(k, 1) = d
End If
Next d
If k = 4 Then OArr(k + 1, 1) = "-"
For Each uRng In unionRange.Areas
uRng.Value = OArr
uRng.NumberFormat = "dd-mmmm"
Next uRng
End Sub
As advised by Jeeped, I substituted the Union Range for individual references. Code changes as follows. If theres a more efficient/neater way of doing this, I would love to know:
Private Sub DateRangePayer1()
'Credit to #Pᴇʜ for pointing out the Array flaw. Corrected this.
Dim rng1 As Range, rng2 As Range, rng3 As Range, rng4 As Range
Set rng1 = Range("Q8:Q12")
Set rng2 = Range("T8:T12")
Set rng3 = Range("Q16:Q20")
Set rng4 = Range("T16:T20")
Dim OArr(1 To 5, 1 To 1) As Variant
Dim k As Long
k = 1
Dim i As Long
For i = DateSerial(Year(Date), Month(Date), 1) To DateSerial(Year(Date), Month(Date) + 1, 0)
If Weekday(i, vbSunday) = 6 Then
OArr(k, 1) = i
k = k + 1
End If
Next i
If k = 5 Then OArr(k, 1) = "-"
rng1.Value = OArr
rng1.NumberFormat = "dd-mmmm"
rng2.Value = OArr
rng2.NumberFormat = "dd-mmmm"
rng3.Value = OArr
rng3.NumberFormat = "dd-mmmm"
rng4.Value = OArr
rng4.NumberFormat = "dd-mmmm"
End Sub

VBA code Adding a cell contains date and a cell contains a number, getting mismatch error

Hi I am Trying to add to cells together and compare them against another cell but I get a type mismatch.
first cell is a date, the one being added is a number"as in number of days" and the third one being compared is a date also.
but I get type mismatch.
my code is below
Sub Macro1()
Macro1 Macro
Dim wks As Worksheet
Set wks = ActiveSheet
Dim x As Integer
Dim p As Integer
Dim rowRange As Range
Dim colRange As Range
Dim LastCol As Long
Dim LastRow As Long
LastRow = wks.Cells(wks.Rows.Count, "A").End(xlUp).Row
Set rowRange = wks.Range("A1:A" & LastRow)
For i = 7 To 189
p = 0
For q = 8 To LastRow
If [aq] = [si] Then
If [cq] + [ui] >= [xi] Then
[oq] = 1
Else
p = p + [dq]
[qq] = 0
End If
End If
Next q
Next i
End Sub
[cq] is a cell that contains date
[ui] is a cell that contains number
[xi] is a cell that contains date
Try it as cells(q, "A") = cells(i, "S").
For i = 7 To 189
p = 0
For q = 8 To LastRow
'If [aq] = [si] Then
If cells(q, "A") = cells(i, "S") Then
'If [cq] + [ui] >= [xi] Then
If cells(q, "C") + cells(i, "U") >= cells(i, "X") Then
'[oq] = 1
cells(q, "O") = 1
Else
'p = p + [dq]
p = p + cells(q, "D")
'[qq] = 0
cells(q, "Q") = 0
End If
End If
Next q
Next i
You need to use the "DateAdd" function. Instructions here: https://www.techonthenet.com/excel/formulas/dateadd.php
Example:
Sub add_dates()
Dim dateOne As Date
Dim dateTwo As Date
Dim lngDays As Long
dateOne = "1/1/2018"
lngDays = 2
dateTwo = "1/3/2018"
Dim result As Boolean
If DateAdd("d", lngDays, dateOne) >= dateTwo Then
MsgBox ("Greater than or equal to")
Else
MsgBox ("Less than")
End If
End Sub

VBA - CountIf cell range displayed value is equal to desired value

I am having an issue running my code through, because the Range.Value is different than the Range.NumberFormat. For example, my value is a date and time and I would like to test for the day of the week. I was able to get the number format to be Sun-Sat, however, I am unsure how to test for it with CountIf.
Dim rep as Worksheet
Dim day As Range
Dim time As Range
Dim wf As WorksheetFunction
Set rep = Worksheets("Report")
Set day = rep.Range("H1", rep.Range("H1").End(xlDown))
Set time = rep.Range("I1", rep.Range("I1").End(xlDown))
Set wf = WorksheetFunction
With rep
.Columns("H").NumberFormat = "dddd"
.Columns("I").NumberFormat = "AM/PM"
.Range("K1") = "Monday"
.Range("K2") = "Tuesday"
.Range("K3") = "Wednesday"
.Range("K4") = "Thursday"
.Range("K5") = "Friday"
.Range("K6") = "Saturday"
.Range("K7") = "Sunday"
.Range("M1") = "AM"
.Range("M2") = "PM"
.Range("L1") = wf.CountIf(day, "Monday")
.Range("L2") = wf.CountIf(day, "Tuesday")
.Range("L3") = wf.CountIf(day, "Wednesday")
.Range("L4") = wf.CountIf(day, "Thursday")
.Range("L5") = wf.CountIf(day, "Friday")
.Range("L6") = wf.CountIf(day, "Saturday")
.Range("L7") = wf.CountIf(day, "Sunday")
.Range("N1") = wf.CountIf(time, "AM")
.Range("N2") = wf.CountIf(time, "PM")
End With
This is what I have so far, but it only outputs 0 for the solution to the countif. Thanks in advance.
Here's another way to do the counts.
Note I did most of the "work" in VBA arrays as this is much faster than repeatedly accessing the worksheet:
EDIT: To include counting the number of entries in column H with AM or PM times
Option Explicit
Sub foo()
Dim rep As Worksheet
Dim rDts As Range
Dim vDts As Variant
Dim vCnts As Variant 'for the weekday count
Dim vAP As Variant 'for the AM PM count
Dim I As Long, J As Long
Set rep = Worksheets("sheet1")
'read dates into array -- faster processing
With rep
vDts = .Range(.Cells(1, 8), .Cells(.Rows.Count, 8).End(xlUp))
End With
'Results array
ReDim vCnts(1 To 7, 1 To 2)
vCnts(1, 1) = "Sunday"
vCnts(2, 1) = "Monday"
vCnts(3, 1) = "Tuesday"
vCnts(4, 1) = "Wednesday"
vCnts(5, 1) = "Thursday"
vCnts(6, 1) = "Friday"
vCnts(7, 1) = "Saturday"
ReDim vAP(1 To 2, 1 To 2)
vAP(1, 1) = "AM"
vAP(2, 1) = "PM"
'Do the counts
For I = 1 To UBound(vDts, 1)
J = Weekday(vDts(I, 1))
vCnts(J, 2) = vCnts(J, 2) + 1
'Check for AM or PM
If Hour(vDts(I, 1)) < 12 Then
vAP(1, 2) = vAP(1, 2) + 1
Else
vAP(2, 2) = vAP(2, 2) + 1
End If
Next I
'output the results
rep.Range("K1:L7").Value = vCnts
rep.Range("M1:N2").Value = vAP
End Sub

Compare Ranges to see if they are equal

I am working on my computer to automate a quote in Excel with VBA
It consists of finding duplicates so they can be summed.
For example:
I have the following information:
Click here for the Excel file
The range from A2:C4 is a group that it states there are 28 bolts, 1 nut for each bolt & 1 washer for each bolt.
A5:C7 is another group that is the same 28 bolts, 1 nut for each bolt & 1 washer for each bolt.
A11:C13 is another group but the difference is that for this one are 2 nuts & 2 washer per bolt.
So this wont be sum
This would be the result:
I have the following code where it only looks through all the cells, I can't find a way to make it look in groups or ranges.
Sub Macro1()
Dim LastRow As Long, LastColumn As Long
Dim wSrc As Worksheet: Set wSrc = Sheets("Hoja1")
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
With wSrc
LastRow = .Range("B" & .Rows.Count).End(xlUp).Row
Set rng = .Range("B1:B" & LastRow)
LastColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column + 2
rng.AdvancedFilter Action:=xlFilterCopy, copytoRange:=.Cells(1, LastColumn), unique:=True
Z = .Cells(.Rows.Count, LastColumn).End(xlUp).Row
LastColumn = LastColumn + 1
.Cells(1, LastColumn).Value = "Total"
.Range(.Cells(2, LastColumn), .Cells(Z, LastColumn)).Formula = _
"=SUMIF(" & rng.Address & "," & .Cells(2, LastColumn - 1).Address(False, False) & "," & rng.Offset(, 1).Address & ")"
End With
With Application
.ScreenUpdating = Truek
.Calculation = xlCalculationAutomatic
End With
End Sub
Click below for the Excel file
Here is an approach that utilizes User Defined Object for the Hardware, and Hardware groups.
We could create more compact code with fewer loops, but, unless there is a significant speed issue, this is probably more readable, and can be more easily adapted to future needs.
We create two class modules (and be sure to rename them as indicated in the code).
One class module is for the hardware items, the second is for the different groups.
The hardware items properties are the description, the weight per item, and the number of items.
The hardware groups properties are a collection of Hardware items, and the Quantity of items in that group.
We then combine the hardware groups into a collection of unique hardware groups.
As the code is written, you could combine in other ways to generate other types of reports.
The results:
Class Module 1
'**Rename: cHardware**
Option Explicit
Private pDescription As String
Private pWt As Double
Private pItemCount As Long
Public Property Get Description() As String
Description = pDescription
End Property
Public Property Let Description(Value As String)
pDescription = Value
End Property
Public Property Get Wt() As Double
Wt = pWt
End Property
Public Property Let Wt(Value As Double)
pWt = Value
End Property
Public Property Get ItemCount() As Long
ItemCount = pItemCount
End Property
Public Property Let ItemCount(Value As Long)
pItemCount = Value
End Property
Class Module 2
'**Rename: cHardwareGrp**
Option Explicit
Private pHW As cHardWare
Private pHWs As Collection
Private pQty As Long
Private Sub Class_Initialize()
Set pHWs = New Collection
End Sub
Public Property Get HW() As cHardWare
Set HW = pHW
End Property
Public Property Let HW(Value As cHardWare)
Set pHW = Value
End Property
Public Property Get HWs() As Collection
Set HWs = pHWs
End Property
Public Function AddHW(Value As cHardWare)
Dim I As Long, J As Long
If pHWs.Count = 0 Then
pHWs.Add Value
Else 'Insert in sorted order
For J = pHWs.Count To 1 Step -1
If pHWs(J).Description <= Value.Description Then Exit For
Next J
If J = 0 Then
pHWs.Add Value, before:=1
Else
pHWs.Add Value, after:=J
End If
End If
End Function
Public Property Get Qty() As Long
Qty = pQty
End Property
Public Property Let Qty(Value As Long)
pQty = Value
End Property
Regular Module
Option Explicit
Sub SummarizeHW()
Dim wsRes As Worksheet, wsSrc As Worksheet, rRes As Range
Dim vSrc As Variant, vRes() As Variant
Dim cHW As cHardWare, colHW As Collection
Dim cHWG As cHardwareGrp, colHWG As Collection
Dim colUniqueHWG As Collection
Dim I As Long, J As Long, K As Long
Dim lQTY As Long
Dim S As String
Dim V As Variant
Dim RE As Object, MC As Object
'Set Source and Results Worksheets and Ranges
Set wsSrc = Worksheets("Hoja1")
Set wsRes = Worksheets("Hoja2")
Set rRes = wsRes.Cells(1, 1)
'Get Source Data
With wsSrc
vSrc = .Range(.Cells(1, 2), .Cells(.Rows.Count, 2).End(xlUp)) _
.Offset(columnoffset:=-1).Resize(columnsize:=3)
End With
'Set up regex to extract number of HW items in description
Set RE = CreateObject("vbscript.regexp")
With RE
.Global = False
.Pattern = "^\((\d+)\)\s*"
.MultiLine = True
End With
'Collect unique list of hardware items
' compute the weight of each single item
Set colHW = New Collection
On Error Resume Next
For I = 2 To UBound(vSrc, 1) 'assumes header row
If vSrc(I, 1) <> "" Then lQTY = vSrc(I, 1)
Set cHW = New cHardWare
With cHW
S = vSrc(I, 2)
If RE.test(S) = True Then
Set MC = RE.Execute(S)
.ItemCount = CLng(MC(0).submatches(0))
Else
.ItemCount = 1
End If
.Wt = vSrc(I, 3) / lQTY / .ItemCount
.Description = S
colHW.Add cHW, .Description
End With
Next I
On Error GoTo 0
'Collect the Hardware Groups
'HW group starts if there is a "Qty" in column 1
Set colHWG = New Collection
For I = 2 To UBound(vSrc, 1)
If vSrc(I, 1) <> "" Then lQTY = vSrc(I, 1)
Set cHWG = New cHardwareGrp
Do
With cHWG
.HW = colHW(vSrc(I, 2))
.AddHW .HW
.Qty = lQTY
End With
I = I + 1
If I > UBound(vSrc, 1) Then Exit Do
Loop Until vSrc(I, 1) <> ""
colHWG.Add cHWG
I = I - 1
Next I
'Collect the unique hardware groups
' A group is defined by ALL of the hardware components being identical
' in both type and quantity. Therefore, we can concatenate them as a key
Set colUniqueHWG = New Collection
On Error Resume Next
For I = 1 To colHWG.Count
With colHWG(I)
ReDim V(1 To .HWs.Count)
For J = 1 To UBound(V)
V(J) = .HWs(J).Description
Next J
S = Join(V, "|")
colUniqueHWG.Add colHWG(I), S
Select Case Err.Number
Case 457 'a duplicate so add the QTY
colUniqueHWG(S).Qty = colUniqueHWG(S).Qty + .Qty
Err.Clear
Case Is <> 0 'error stop
Debug.Print Err.Number, Err.Description
End Select
End With
Next I
On Error GoTo 0
'Final Report
'# of columns = 3
'# of rows = sum of the number of HW items in each group + 1 for the header
J = 0
For I = 1 To colUniqueHWG.Count
J = J + colUniqueHWG(I).HWs.Count
Next I
ReDim vRes(0 To J, 1 To 3)
'Column headers
vRes(0, 1) = "Qty"
vRes(0, 2) = "Hardware Description"
vRes(0, 3) = "Weight"
'populate the results array'
K = 1
For I = 1 To colUniqueHWG.Count
With colUniqueHWG(I)
For J = 1 To .HWs.Count
If J = 1 Then vRes(K, 1) = .Qty
vRes(K, 2) = .HWs(J).Description
vRes(K, 3) = .Qty * .HWs(J).Wt * .HWs(J).ItemCount
K = K + 1
Next J
End With
Next I
'Write the results on a new sheet
Set rRes = rRes.Resize(UBound(vRes, 1) + 1, UBound(vRes, 2))
With rRes
.EntireColumn.Clear
.Value = vRes
.ColumnWidth = 255
With Rows(1)
.Font.Bold = True
.HorizontalAlignment = xlCenter
End With
.EntireColumn.AutoFit
End With
End Sub
Hmmm. I see from your comments that the hardware may not always be in the same order. I will add a sorting routine to our group generation so that will be irrelevant.
EDIT: The AddHW function was modified to insert the HW items in sorted order. Since there should only be a few items, this insertion sort should be adequate.
Taking a different approach.
take advantage of the structure; three lines define it
Put results on a different tab
This input ...
generates this output ...
using this code ...
Option Explicit
Sub Macro1()
Dim LastRow As Long, LastColumn As Long
Dim wSrc As Worksheet: Set wSrc = Sheets("Hoja1")
Dim tmpSrc As Worksheet
Dim outRng As Range, inRng As Range
Dim iLoop As Long, jLoop As Long, QSum As Long
' turn off updating for speed
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' setup - tmpSrc is the working and final result
Set tmpSrc = ActiveWorkbook.Sheets.Add(, wSrc)
Set inRng = wSrc.UsedRange
inRng.Copy
tmpSrc.Range("A1").PasteSpecial (xlPasteAll)
With tmpSrc
.Name = "Hoja2"
Set outRng = .UsedRange
LastRow = .UsedRange.Rows.Count
LastColumn = .UsedRange.Columns.Count
End With
' loop down through the range
For iLoop = 2 To LastRow
If outRng.Cells(iLoop, 1) <> "" Then
QSum = outRng.Cells(iLoop, 1).Value
For jLoop = LastRow To iLoop + 1 Step -1 'loop up through the range to find a match
' matches are defined by all three rows in column B
If outRng.Cells(jLoop, 1) <> "" And _
outRng.Cells(iLoop, 2) = outRng.Cells(jLoop, 2) And _
outRng.Cells(iLoop + 1, 2) = outRng.Cells(jLoop + 1, 2) And _
outRng.Cells(iLoop + 2, 2) = outRng.Cells(jLoop + 2, 2) Then
QSum = QSum + outRng.Cells(jLoop, 1).Value
outRng.Rows(jLoop + 2).Delete
outRng.Rows(jLoop + 1).Delete
outRng.Rows(jLoop).Delete
LastRow = LastRow - 3
End If
Next jLoop
outRng.Cells(iLoop, 1).Value = QSum
End If
Next iLoop
For iLoop = 1 To 3
outRng.Columns(iLoop).ColumnWidth = inRng.Columns(iLoop).ColumnWidth
Next iLoop
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
End With
End Sub
Edit:
Summing weights for bolts, nuts, and washers
Checking for case where nuts and washers appear in reverse order
n.b. I am using .UsedRange to find the last row and last column. Other methods are available.
.
Option Explicit
Sub Macro1()
Dim LastRow As Long, LastColumn As Long
Dim wSrc As Worksheet: Set wSrc = Sheets("Hoja1")
Dim tmpSrc As Worksheet
Dim outRng As Range, inRng As Range
Dim iLoop As Long, jLoop As Long, QSum As Long
Dim WSum1 As Double, WSum2 As Double, WSum3 As Double
' turn off updating for speed
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' setup - tmpSrc is the working and final result
Set tmpSrc = ActiveWorkbook.Sheets.Add(, wSrc)
Set inRng = wSrc.UsedRange
inRng.Copy
tmpSrc.Range("A1").PasteSpecial (xlPasteAll)
With tmpSrc
.Name = "Hoja2"
Set outRng = .UsedRange
LastRow = .UsedRange.Rows.Count
LastColumn = .UsedRange.Columns.Count
End With
' loop down through the range
For iLoop = 2 To LastRow
If outRng.Cells(iLoop, 1) <> "" Then
QSum = outRng.Cells(iLoop, 1).Value
WSum1 = outRng.Cells(iLoop, 3).Value
WSum2 = outRng.Cells(iLoop + 1, 3).Value
WSum3 = outRng.Cells(iLoop + 2, 3).Value
For jLoop = LastRow To iLoop + 1 Step -1 'loop up through the range to find a match
' matches are defined by all three rows in column B
If outRng.Cells(jLoop, 1) <> "" And _
outRng.Cells(iLoop, 2) = outRng.Cells(jLoop, 2) And _
outRng.Cells(iLoop + 1, 2) = outRng.Cells(jLoop + 1, 2) And _
outRng.Cells(iLoop + 2, 2) = outRng.Cells(jLoop + 2, 2) Then
QSum = QSum + outRng.Cells(jLoop, 1).Value
WSum1 = WSum1 + outRng.Cells(jLoop, 3).Value
WSum2 = WSum2 + outRng.Cells(jLoop + 1, 3).Value
WSum3 = WSum3 + outRng.Cells(jLoop + 2, 3).Value
outRng.Rows(jLoop + 2).Delete
outRng.Rows(jLoop + 1).Delete
outRng.Rows(jLoop).Delete
LastRow = LastRow - 3
Else ' check if bolts and washers are in reverse order
If outRng.Cells(jLoop, 1) <> "" And _
outRng.Cells(iLoop, 2) = outRng.Cells(jLoop, 2) And _
outRng.Cells(iLoop + 1, 2) = outRng.Cells(jLoop + 2, 2) And _
outRng.Cells(iLoop + 2, 2) = outRng.Cells(jLoop + 1, 2) Then
QSum = QSum + outRng.Cells(jLoop, 1).Value
WSum1 = WSum1 + outRng.Cells(jLoop, 3).Value
WSum2 = WSum2 + outRng.Cells(jLoop + 2, 3).Value
WSum3 = WSum3 + outRng.Cells(jLoop + 1, 3).Value
outRng.Rows(jLoop + 2).Delete
outRng.Rows(jLoop + 1).Delete
outRng.Rows(jLoop).Delete
LastRow = LastRow - 3
End If
End If
Next jLoop
outRng.Cells(iLoop, 1).Value = QSum
outRng.Cells(iLoop, 3).Value = WSum1
outRng.Cells(iLoop + 1, 3).Value = WSum2
outRng.Cells(iLoop + 2, 3).Value = WSum3
End If
Next iLoop
For iLoop = 1 To 3
outRng.Columns(iLoop).ColumnWidth = inRng.Columns(iLoop).ColumnWidth
Next iLoop
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
End With
End Sub

VBA - Change the target Column to another Column

I have this code in one of the part of my script count the data from Column A if the data have duplicate value for 3 consecutive months it will be tag as "Selected" and "Updated"
Output would be like this:
Column A | Column B | Column C | Column D |
243899 | 1/20/2016 | | |
243899 | 2/10/2016 | | |
243899 | 3/15/2016 | Selected | Updated |
Note:
Column B is where the month value
Column C and D is where the data will be tag as "Selected" and "Updated"
I have 3 months of data
My problem is that i'm going to change all the target Column in the example above
Column A to Column T
Column B to Column BS
Column C and D to Column CH and CI
My code:
Public Sub Selection()
Dim file2 As Excel.Workbook
Dim Sheet2 As Worksheet, data(), i&
Dim myRangeColor As Variant, myRangeMonthValue
Dim MstrSht As Worksheet
Dim DataArr As Variant
Dim ColorArr As Variant
Dim MonthCol As Collection
Dim CloseToDate As Date
Dim MaxDate As Date
Dim c As Long
Set Sheet2 = Workbooks.Open(TextBox2.Text).Sheets(1)
'Load Data into Array
DataArr = Sheet2.Range("A2:D" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row)
Find distinct colors
ColorArr = ReturnDistinct(Sheet2.Range("A2:A" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row))
Remove any values in the arrays third column
For i = LBound(DataArr, 1) To UBound(DataArr, 1)
DataArr(i, 4) = ""
Next i
'Loop Each Color
For c = LBound(ColorArr) To UBound(ColorArr)
Set MonthCol = New Collection
MaxDate = 0
For i = LBound(DataArr, 1) To UBound(DataArr, 1)
If DataArr(i, 1) = ColorArr(c) Then
'Load the colors months into a collection
On Error Resume Next
MonthCol.Add Month(DataArr(i, 2)), CStr(Month(DataArr(i, 2)))
On Error GoTo 0
'Find Max Date
If DataArr(i, 2) Then
MaxDate = Application.WorksheetFunction.Max(MaxDate, DataArr(i, 2))
End If
End If
Next i
'If the color were found in three or more seperate months then the row with date closest to CloseToDate gets flagged
If MonthCol.Count > 2 Then
For i = LBound(DataArr, 1) To UBound(DataArr, 1)
If DataArr(i, 1) = ColorArr(c) And DataArr(i, 2) = MaxDate Then
DataArr(i, 3) = "Selected"
DataArr(i, 4) = "Updated"
End If
Next i
End If
Next c
'Print results to sheet
Sheet2.Range("A2:D" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row) = DataArr
End Sub
Function ReturnDistinct(InpRng As Range) As Variant
Dim Cell As Range
Dim i As Integer
Dim DistCol As New Collection
Dim DistArr()
'Add all values to collection
For Each Cell In InpRng
On Error Resume Next
DistCol.Add Cell.Value, CStr(Cell.Value)
On Error GoTo 0
Next Cell
'Write collection to array
ReDim DistArr(1 To DistCol.Count)
For i = 1 To DistCol.Count Step 1
DistArr(i) = DistCol.Item(i)
Next i
ReturnDistinct = DistArr
End Function
I got my code here so im not really familiar to this code.. Is it possible to change the column in my script? I've done lots of trial and error on this one i can't seem to figure it out,. Any help, tips or suggestion i would gladly appreciate it!
In my previous comment, I had something in mind as follows. I tested this using columns A,B,C,D, but not using the more widely dispersed columns.
As a side note, I also had some trouble with your WorksheetFunction.Max call - I had to use CDate to get the comparison to work.
Public Sub Selection()
Dim file2 As Excel.Workbook
Dim Sheet2 As Worksheet, data(), i&
Dim myRangeColor As Variant, myRangeMonthValue
Dim MstrSht As Worksheet
Dim DataArr() As Variant
Dim TempArr1 As Variant, TempArr2 As Variant
Dim TempArr3 As Variant, TempArr4 As Variant
Dim ColorArr As Variant
Dim MonthCol As Collection
Dim CloseToDate As Date
Dim MaxDate As Date
Dim c As Long
Dim nRows As Long, nCols As Long
Dim iLoop As Long
' Set Sheet2 = Workbooks.Open(TextBox2.Text).Sheets(1)
Set Sheet2 = Sheets("Sheet2")
'Load Data into Array
' DataArr = Sheet2.Range("A2:D" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row)
TempArr1 = Sheet2.Range("T2:T" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row)
TempArr2 = Sheet2.Range("BS2:BS" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row)
TempArr3 = Sheet2.Range("CH2:CH" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row)
TempArr4 = Sheet2.Range("CI2:CI" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row)
nRows = UBound(TempArr1)
nCols = 4
ReDim Preserve DataArr(1 To nRows, 1 To nCols)
For iLoop = 1 To nRows - 1
DataArr(iLoop, 1) = TempArr1(iLoop, 1)
DataArr(iLoop, 2) = TempArr2(iLoop, 1)
DataArr(iLoop, 3) = TempArr3(iLoop, 1)
DataArr(iLoop, 4) = TempArr4(iLoop, 1)
Next iLoop
'Find distinct colors
ColorArr = ReturnDistinct(Sheet2.Range("A2:A" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row))
'Remove any values in the arrays third column
For i = LBound(DataArr, 1) To UBound(DataArr, 1)
DataArr(i, 3) = ""
Next i
'Loop Each Color
For c = LBound(ColorArr) To UBound(ColorArr)
Set MonthCol = New Collection
MaxDate = 0
For i = LBound(DataArr, 1) To UBound(DataArr, 1)
If DataArr(i, 1) = ColorArr(c) Then
'Load the colors months into a collection
On Error Resume Next
MonthCol.Add Month(DataArr(i, 2)), CStr(Month(DataArr(i, 2)))
On Error GoTo 0
'Find Max Date
If DataArr(i, 2) > 0 Then
MaxDate = Application.WorksheetFunction.Max(CDate(MaxDate), CDate(DataArr(i, 2)))
End If
End If
Next i
'If the color were found in three or more seperate months then the row with date closest to CloseToDate gets flagged
If MonthCol.Count > 2 Then
For i = LBound(DataArr, 1) To UBound(DataArr, 1)
If DataArr(i, 1) = ColorArr(c) And DataArr(i, 2) = MaxDate Then
DataArr(i, 3) = "Selected"
DataArr(i, 4) = "Updated"
End If
Next i
End If
Next c
'Print results to sheet
'Sheet2.Range("A2:D" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row) = DataArr
For iLoop = 1 To nRows - 1
TempArr1(iLoop, 1) = DataArr(iLoop, 1)
TempArr2(iLoop, 1) = DataArr(iLoop, 2)
TempArr3(iLoop, 1) = DataArr(iLoop, 3)
TempArr4(iLoop, 1) = DataArr(iLoop, 4)
Next iLoop
Sheet2.Range("T2:" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row).Value2 = TempArr1
Sheet2.Range("BS2:BS" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row).Value2 = TempArr2
Sheet2.Range("CH2:CH" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row).Value2 = TempArr3
Sheet2.Range("CI2:CI" & Sheet2.Cells(Rows.Count, 1).End(xlUp).Row).Value2 = TempArr3
End Sub