I have a macro (Courtesy of another questions i posted on here) which works perfectly. It searched through data and rearranged it to my needs. It also only outputted information that matched a criteria given (E.g. A cell equals the specified string).
However, now i have added a list box in where the user can select multiple criteria. But i am not sure how i can use VBA to search through the data and only output the data which matches ANY of the list box selections. As shown in the image, if the user selects CONACC and CONPEND, the code should search through the data and then output the values where the cell equals either CONACC or CONPEND.
Any ideas?
Userform Screenshot
'type to manage data we use from each row
Type dataRow
notif As Variant
variable As Variant
sht As Variant
zone As Variant
End Type
Sub DoPivot()
Const SEP As String = "<>"
Dim rngData As Range, data, r As Long
Dim colDict As Object, rowDict As Object, comboDict As Object
Dim rd As dataRow, rngOutput As Range, col As Long, rw As Long, k
Dim k2, arr, dictCounts As Object
Dim wsOut As Worksheet, num As Long
Dim DataWS, OutputtedWS As Worksheet
Dim WS As Worksheet
Dim DataWb, OutputtedWb As Workbook
Dim DataFileName, DataSheetName, DataSheetFilter As String
Set colDict = CreateObject("scripting.dictionary")
Set rowDict = CreateObject("scripting.dictionary")
Set comboDict = CreateObject("scripting.dictionary")
Set dictCounts = CreateObject("scripting.dictionary")
DataFileName = DataFilter.FileLocationTextbox.Value
DataSheetName = DataFilter.SheetNameTextBox.Value
Set OutputtedWb = ThisWorkbook
Set DataWb = Workbooks.Open(DataFileName)
Set DataWS = DataWb.Sheets(DataSheetName)
Set OutputtedWS = Sheets.Add(After:=Sheets(Sheets.Count))
Dim DataWsLastCol As Long, DataWsLastColLet As String
Dim DataWsLastRow As Long
DataWsLastCol = DataWS.Cells(1, DataWS.Columns.Count).End(xlToLeft).Column
DataWsLastColLet = Split(Cells(1, DataWsLastCol).Address, "$")(1)
DataWsLastRow = DataWS.Range("A:" & DataWsLastColLet).SpecialCells(xlCellTypeLastCell).Row
data = DataWS.Range("A2:" & DataWsLastColLet & DataWsLastRow).Value 'source data
Set rngOutput = OutputtedWS.Range("A1") 'top-left cell for output
Set wsOut = rngOutput.Parent
rngOutput.Resize(5000, 5000).ClearContents
rngOutput.Resize(1, 6).Value = Array("Sheet", "Zone", "Feature Code", "Feature Description", "-TEN OGV KH73126 tolerance", "-TEN OGV KH73126 tolerance")
OutputtedWS.Cells(2, 5) = "Nominal"
OutputtedWS.Cells(2, 6) = "Tolerance"
Set notif = DataWS.Rows("1:1").Find(What:="Notification", lookat:=xlWhole)
Set variable = DataWS.Rows("1:1").Find(What:="Extent Var.", lookat:=xlWhole)
Set sht = DataWS.Rows("1:1").Find(What:="Sheet", lookat:=xlWhole)
Set zone = DataWS.Rows("1:1").Find(What:="Zone", lookat:=xlWhole)
Set CodeGroup = DataWS.Rows("1:1").Find(What:="Code group", lookat:=xlWhole)
notifcol = notif.Column
variablecol = variable.Column
shtcol = sht.Column
zonecol = zone.Column
CodeGroupCol = CodeGroup.Column
col = rngOutput.Column + 6 'start for notification# headers
rw = rngOutput.Row + 2
'first pass - assess data variables
For r = 1 To UBound(data, 1)
If data(r, CodeGroupCol) = [[[[[[THIS IS WHERE THE SELECTION WOULD GO]]]]]] Then
rd = rowData(data, r, notifcol, variablecol, shtcol, zonecol)
k = Join(Array(rd.sht, rd.zone, rd.notif), SEP) 'tracking how many unique combinations of these
comboDict(k) = comboDict(k) + 1 'increment count
'manage column header positions for unique notification numbers
If Not colDict.exists(rd.notif) Then
colDict.Add rd.notif, col 'store the column
rngOutput.EntireRow.Cells(1, col).Value = rd.notif 'add the header
col = col + 1
End If
End If
Next r
'figure out # of rows for each sheet-Zone pair
For Each k In comboDict.keys
arr = Split(k, SEP)
k2 = Join(Array(arr(0), arr(1)), SEP) 'sheet<>zone
'is this more rows than any previous same k2 value?
dictCounts(k2) = Application.Max(dictCounts(k2), comboDict(k))
Next k
'create the row headers
For Each k In dictCounts.keys
num = dictCounts(k)
rowDict(k) = rw 'record start row for each sheet<>zone combo
wsOut.Cells(rw, rngOutput.Column).Resize(num, 2).Value = Split(k, SEP)
dictCounts(k) = 0 'reset so we can track while adding data
rowDict(k) = rw
rw = rw + num
Next k
'last pass - populate the data based on the dictionaries
For r = 1 To UBound(data, 1)
If data(r, CodeGroupCol) = [[[[[[THIS IS WHERE THE SELECTION WOULD GO]]]]]] Then
rd = rowData(data, r, notifcol, variablecol, shtcol, zonecol)
k = Join(Array(rd.sht, rd.zone, rd.notif), SEP) '3-field combo
k2 = Join(Array(rd.sht, rd.zone), SEP) 'row key
wsOut.Cells(rowDict(k2) + (dictCounts(k)), _
colDict(rd.notif)).Value = rd.variable
dictCounts(k) = dictCounts(k) + 1 'increment this unique combo
End If
Next r
End Sub
'populate a Type instance for a given row
Function rowData(data, r As Long, notficationcol, variablecol, sheetcol, zonecol) As dataRow
Dim rv As dataRow
rv.notif = IfEmpty(data(r, notficationcol))
rv.variable = IfEmpty(data(r, variablecol))
rv.sht = IfEmpty(data(r, sheetcol))
rv.zone = IfEmpty(data(r, zonecol))
rowData = rv
End Function
'substitute EMPTY for zero-length value
Function IfEmpty(v)
IfEmpty = IIf(Len(v) = 0, "EMPTY", v)
End Function
Add another dictionary
Dim i As Integer, dictFilter As Object
Set dictFilter = CreateObject("scripting.dictionary")
With DataFilter.ListBox1
For i = 0 To .ListCount - 1
If .Selected(i) = True Then
dictFilter.Add .List(i), 1
End If
Next
End With
If dictFilter.exists(data(r, CodeGroupCol)) Then
'
'
End If
Related
I got 2 tabs in excel and i am kinda new to VBA:
"Operations":
"Details":
You can download the workbook and sampledata from here: Workbook
My CODE actually does this:
VBA SHOULD FIND EVERY TRANSACTION INSIDE "DESCRIPTION" CELL FROM TAB "OPERATIONS". IN THIS CASE THE FIRST ROW CONTAINS ONE TRANSACTION, ROW 2 CONTAINS ONE TRANSACTION AND ROW 3 CONTAINS 2 TRANSACTIONS AND ONLY CONSIDER THE OPERATION CODES WITHIN 11 DIGITS
IT SHOULD COPY THE NUMBER FROM TAB "OPERATIONS" AND PASTE IT INSIDE COLUMN "LINKED" FROM TAB "DESCRIPTION"
FIND ALL THE REPEATED VALUES INSIDE "DESCRIPTION" FIELD, I MEAN THOSE VALUES WITH SAME DESCRIPTION AND TYPE: FAC and N/C AND PERFORM THIS:
IF THE REPEATED VALUES CONTAIN A TYPE "FC" AND "N/C" IT MUST PUT THE VALUE OF THE CELL "NUMBER" FROM TAB "OPERATIONS" OF TYPE "N/C" AND PLACE IT INSIDE "LINKED" COLUMN FROM TAB "DETAILS" AND THEN, WRITE WORD: "DONE" INSIDE "NOTE" FIELD AND FINALLY COPY THE VALUE OF CELL "MONEY" FROM TAB "DETAILS" AND PASTE IT INSIDE CELL "MONEY" FROM TAB "OPERATIONS"
After running my code i got this:
The thing is that after pasting the value inside column MONEY from tab OPERATIONS i want to copy the NUMBER of the N/C and paste it inside column LINKED from tab DETAILS for that specific operation number.
Take a look, this is the expected result:
VBA code (a macro) in excel:
Sub M_snb()
Const VAL_NC As String = "N/C"
Const VAL_FAC As String = "FAC"
'column positions - ops
Const COL_OPS_TYPE As Long = 8
Const COL_OPS_NUMBER As Long = 9
Const COL_OPS_DESCR As Long = 11
Const COL_OPS_MONEY As Long = 12
'column positions - details
Const COL_DET_OPS_NUM As Long = 5
Const COL_DET_MONEY As Long = 6
Const COL_DET_LINKED As Long = 7
Const COL_DET_NOTE As Long = 8
Dim wsOps As Worksheet, wsDets As Worksheet
Dim c As Range, col As Collection, v, m
Dim rngOps As Range, rngDets As Range, rO As Long, rD As Long, rw
Dim dict As Object, colRows As Collection
Dim bFAC As Boolean, bNC As Boolean, amt, typ
Set dict = CreateObject("scripting.dictionary")
Set wsOps = ThisWorkbook.Worksheets("Operations")
Set wsDets = ThisWorkbook.Worksheets("Details")
Set rngOps = wsOps.Range("A1").CurrentRegion
Set rngDets = wsDets.Range("A1").CurrentRegion
'Loop over ops data and find all unique 11-digit numbers,
' and store the rows they're found on in a collection per number
For rO = 2 To rngOps.Rows.Count
Set col = AllNumbers(rngOps.Cells(rO, COL_OPS_DESCR).Value)
For Each v In col
If Not dict.exists(v) Then dict.Add v, New Collection 'new number?
dict(v).Add rO 'store current row number
Next v
Next rO
For Each v In dict.keys 'loop the unique numbers
Set colRows = dict(v) 'all Operations rows which contain this number...
bFAC = False
bNC = False
For Each rw In colRows 'loop rows and check "types"
Select Case rngOps.Cells(rw, COL_OPS_TYPE).Value
Case VAL_NC: bNC = True
Case VAL_FAC: bFAC = True
End Select
If bFAC And bNC Then Exit For 'already found both
Next rw
'loop over Details and see what rows can be matched to this number
' you'll need to figure out the details here...
For rD = 2 To rngDets.Rows.Count
If CStr(rngDets.Cells(rD, COL_DET_OPS_NUM).Value) = v Then
rngDets.Cells(rD, COL_DET_LINKED).Value = rngOps.Cells(colRows(1), COL_OPS_NUMBER).Value
'dataOps(rO, 4) = dataOps(rO, 4) + dataDets(rD, 2) 'fix this
If bNC And bFAC Then 'have both types?
rngDets.Cells(rD, COL_DET_NOTE).Value = "DONE"
End If
'copy the "money" value from Details back to Operations
amt = rngDets.Cells(rD, COL_DET_MONEY).Value
For Each rw In colRows
If rngOps.Cells(rw, COL_OPS_TYPE).Value = VAL_NC Then
rngOps.Cells(rw, COL_OPS_MONEY).Value = amt
End If
Next rw
End If
Next rD
Next v
End Sub
'return all 11-digit strings in v as a Collection
Function AllNumbers(v) As Collection
Const NUM_DIGITS As Long = 11
Dim m As Object, mc As Object, col As New Collection, txt, i As Long, patt, ss
txt = " " & v & " "
patt = String(NUM_DIGITS, "#")
i = 2
For i = 2 To Len(txt) - NUM_DIGITS
ss = Mid(txt, i, 11)
If ss Like patt Then
If Not Mid(txt, i - 1, 1) Like "#" Then
If Not Mid(txt, i + NUM_DIGITS, 1) Like "#" Then
col.Add ss
End If
End If
End If
Next i
Set AllNumbers = col
End Function
I was able to solve this by myself:
Sub M_snb()
Const VAL_NC As String = "N/C"
Const VAL_FAC As String = "FAC"
'column positions - ops
Const COL_OPS_TYPE As Long = 8
Const COL_OPS_NUMBER As Long = 9
Const COL_OPS_DESCR As Long = 11
Const COL_OPS_MONEY As Long = 12
Const COL_OPS_WEB As Long = 15
'column positions - details
Const COL_DET_OPS_NUM As Long = 5
Const COL_DET_MONEY As Long = 6
Const COL_DET_LINKED As Long = 7
Const COL_DET_NOTE As Long = 8
Dim wsOps As Worksheet, wsDets As Worksheet
Dim c As Range, col As Collection, v, m
Dim rngOps As Range, rngDets As Range, rO As Long, rD As Long, rw
Dim dict As Object, colRows As Collection
Dim bFAC As Boolean, bNC As Boolean, amt, typ
Set dict = CreateObject("scripting.dictionary")
Set wsOps = ThisWorkbook.Worksheets("Operations")
Set wsDets = ThisWorkbook.Worksheets("Details")
Set rngOps = wsOps.Range("A1").CurrentRegion
Set rngDets = wsDets.Range("A1").CurrentRegion
'Loop over ops data and find all unique 11-digit numbers,
' and store the rows they're found on in a collection per number
For rO = 2 To rngOps.Rows.Count
Set col = AllNumbers(rngOps.Cells(rO, COL_OPS_DESCR).Value)
For Each v In col
If Not dict.exists(v) Then dict.Add v, New Collection 'new number?
dict(v).Add rO 'store current row number
Next v
Next rO
For Each v In dict.keys 'loop the unique numbers
Set colRows = dict(v) 'all Operations rows which contain this number...
bFAC = False
bNC = False
For Each rw In colRows 'loop rows and check "types"
Select Case rngOps.Cells(rw, COL_OPS_TYPE).Value
Case VAL_NC: bNC = True
Case VAL_FAC: bFAC = True
End Select
If bFAC And bNC Then Exit For 'already found both
Next rw
'loop over Details and see what rows can be matched to this number
' you'll need to figure out the details here...
For rD = 2 To rngDets.Rows.Count
If CStr(rngDets.Cells(rD, COL_DET_OPS_NUM).Value) = v Then
rngDets.Cells(rD, COL_DET_LINKED).Value = rngOps.Cells(colRows(1), COL_OPS_NUMBER).Value
'dataOps(rO, 4) = dataOps(rO, 4) + dataDets(rD, 2) 'fix this
If bNC And bFAC Then 'have both types?
rngDets.Cells(rD, COL_DET_NOTE).Value = "Done"
End If
'copy the "money" value from Details back to Operations
amt = rngDets.Cells(rD, COL_DET_MONEY).Value
For Each rw In colRows
If rngOps.Cells(rw, COL_OPS_TYPE).Value = VAL_NC Then
rngOps.Cells(rw, COL_OPS_WEB).Value = amt
amt2 = rngOps.Cells(rw, COL_OPS_NUMBER).Value
'rngOps.Cells(rw, COL_OPS_WEB).Value = amt2
rngDets.Cells(rD, COL_DET_LINKED).Value = amt2
End If
Next rw
End If
Next rD
Next v
End Sub
'return all 11-digit strings in v as a Collection
Function AllNumbers(v) As Collection
Const NUM_DIGITS As Long = 11
Dim m As Object, mc As Object, col As New Collection, txt, i As Long, patt, ss
txt = " " & v & " "
patt = String(NUM_DIGITS, "#")
i = 2
For i = 2 To Len(txt) - NUM_DIGITS
ss = Mid(txt, i, 11)
If ss Like patt Then
If Not Mid(txt, i - 1, 1) Like "#" Then
If Not Mid(txt, i + NUM_DIGITS, 1) Like "#" Then
col.Add ss
End If
End If
End If
Next i
Set AllNumbers = col
End Function
I am trying to copy data from one workbook to another based on the values contained in cells in the source workbook that matches the same values in the target workbook. For example, I have a table (Table1) that has four columns say, A1:D5. One of these columns (column A) contains account numbers that match similar account numbers located on another workbook (also in column A). I am trying to find a code that looks through the table (Table1) in the source workbook via the account number column, and if the account number matches the account number in the target workbook, copy and paste the cells on that row in specific locations to the target workbook. Is this possible?
I hope that makes sense. I have looked all over on how to structure such a code, and I was not able to find anything to start the process for this logic.
Any help will be very appreciative.
Thank you
Even if your question is about doing this in VBA, I'm just going to mention that what you are trying to do seems like it could also be done with Power Query.
That being said, if you were to use VBA for this, you would have to use the Match function to find where your rows match and then copy the data from the source to the destination table.
I've adapted the code I provided to this question to better serve your specific needs. One of the things I've done is to add an optional argument called DoOverwrite and set it to false. This will make sure that the information from one row won't be overwritten by another row later down the road.
Sub TableJoinTest()
'Those table columns will have to match for the 2 lines to be a match
Dim MandatoryHeaders() As Variant
MandatoryHeaders = Array("Account Number")
Dim SourceTableAnchor As Range
Set SourceTableAnchor = Workbooks("SourceWorkbook.xlsx").Sheets("Sheet1").Range("A1")
Dim TargetTableAnchor As Range
Set TargetTableAnchor = Workbooks("TargetWorkbook.xlsx").Sheets("Sheet1").Range("A1")
TableJoin _
SourceTableAnchor:=SourceTableAnchor, _
TargetTableAnchor:=TargetTableAnchor, _
MandatoryHeaders:=MandatoryHeaders, _
AddIfMissing:=False, _
IsLogging:=False, _
DoOverwrite:=False
End Sub
Sub TableJoin( _
SourceTableAnchor As Range, _
TargetTableAnchor As Range, _
MandatoryHeaders As Variant, _
Optional OtherHeaders As Variant, _
Optional AddIfMissing As Boolean = False, _
Optional IsLogging As Boolean = False, _
Optional DoOverwrite As Boolean = True)
'''''''''''''''''''''''''''''''''''''''
'Definitions
'''''''''''''''''''''''''''''''''''''''
Dim srng As Range, trng As Range
Set srng = SourceTableAnchor.CurrentRegion
Set trng = TargetTableAnchor.CurrentRegion
Dim sHeaders As Range, tHeaders As Range
Set sHeaders = srng.Rows(1)
Set tHeaders = trng.Rows(1)
'Store in Arrays
Dim sArray() As Variant 'prefix s is for Source
sArray = ExcludeRows(srng, 1).Value2
Dim tArray() As Variant 'prefix t is for Target
tArray = ExcludeRows(trng, 1).Value2
Dim sArrayHeader As Variant
sArrayHeader = sHeaders.Value2
Dim tArrayHeader As Variant
tArrayHeader = tHeaders.Value2
'Find Column correspondance
Dim sMandatoryHeadersColumn As Variant
ReDim sMandatoryHeadersColumn(LBound(MandatoryHeaders) To UBound(MandatoryHeaders))
Dim tMandatoryHeadersColumn As Variant
ReDim tMandatoryHeadersColumn(LBound(MandatoryHeaders) To UBound(MandatoryHeaders))
Dim k As Long
For k = LBound(MandatoryHeaders) To UBound(MandatoryHeaders)
sMandatoryHeadersColumn(k) = Application.Match(MandatoryHeaders(k), sArrayHeader, 0)
tMandatoryHeadersColumn(k) = Application.Match(MandatoryHeaders(k), tArrayHeader, 0)
Next k
Dim sOtherHeadersColumn As Variant
ReDim sOtherHeadersColumn(LBound(OtherHeaders) To UBound(OtherHeaders))
Dim tOtherHeadersColumn As Variant
ReDim tOtherHeadersColumn(LBound(OtherHeaders) To UBound(OtherHeaders))
For k = LBound(OtherHeaders) To UBound(OtherHeaders)
sOtherHeadersColumn(k) = Application.Match(OtherHeaders(k), sArrayHeader, 0)
tOtherHeadersColumn(k) = Application.Match(OtherHeaders(k), tArrayHeader, 0)
Next k
'Merge mandatory headers into one column (aka the helper column method)
Dim i As Long, j As Long
Dim sHelperColumn() As Variant
ReDim sHelperColumn(LBound(sArray, 1) To UBound(sArray, 1), 1 To 1)
For i = LBound(sArray, 1) To UBound(sArray, 1)
For j = LBound(MandatoryHeaders) To UBound(MandatoryHeaders)
sHelperColumn(i, 1) = sHelperColumn(i, 1) & sArray(i, sMandatoryHeadersColumn(j))
Next j
Next i
Dim tHelperColumn() As Variant
ReDim tHelperColumn(LBound(tArray, 1) To UBound(tArray, 1), 1 To 1)
For i = LBound(tArray, 1) To UBound(tArray, 1)
For j = LBound(MandatoryHeaders) To UBound(MandatoryHeaders)
tHelperColumn(i, 1) = tHelperColumn(i, 1) & tArray(i, tMandatoryHeadersColumn(j))
Next j
Next i
'Find all matches
Dim MatchList() As Variant
Dim LoggingColumn() As String
ReDim LoggingColumn(LBound(tArray, 1) To UBound(tArray, 1), 1 To 1)
For i = LBound(sArray, 1) To UBound(sArray, 1)
ReDim MatchList(LBound(tArray, 1) To UBound(tArray, 1))
For j = LBound(tArray, 1) To UBound(tArray, 1)
If sHelperColumn(i, 1) = tHelperColumn(j, 1) Then
MatchList(j) = 1
End If
Next j
'Get the row number for the match
Dim MatchRow As Long
Select Case Application.Sum(MatchList)
Case Is > 1
'Need to do more matching
Dim MatchingScoresList() As Long
ReDim MatchingScoresList(1 To UBound(tArray, 1))
Dim m As Long
For k = LBound(OtherHeaders) To UBound(OtherHeaders)
For m = LBound(tArray, 1) To UBound(tArray, 1)
If tArray(m, sOtherHeadersColumn(k)) = sArray(i, sOtherHeadersColumn(k)) Then
MatchingScoresList(m) = MatchingScoresList(m) + 2 ^ (UBound(OtherHeaders) - k)
End If
Next m
Next k
'Get the max score position
Dim MyMax As Long
MyMax = Application.Max(MatchingScoresList)
If Application.Count(Application.Match(MatchingScoresList(), Array(MyMax), 0)) > 1 Then
MsgBox "Error: can't determine how to match row " & i & " in source table"
Exit Sub
Else
MatchRow = Application.Match(MyMax, MatchingScoresList, 0)
End If
Case Is = 1
MatchRow = Application.Match(1, MatchList, 0)
Case Else
Dim nArray() As Variant, Counter As Long
If AddIfMissing Then
MatchRow = 0
Counter = Counter + 1
ReDim nArray(1 To Counter, 1 To UBound(tArray, 2))
For k = LBound(MandatoryHeaders) To UBound(MandatoryHeaders)
nArray(Counter, tMandatoryHeadersColumn(k)) = sArray(i, sMandatoryHeadersColumn(k))
Next k
For k = LBound(OtherHeaders) To UBound(OtherHeaders)
nArray(Counter, tOtherHeadersColumn(k)) = sArray(i, sOtherHeadersColumn(k))
Next k
Else
MsgBox "Error: Couldn't find a match for data row #" & i
Exit Sub
End If
End Select
'Logging and assigning values
If MatchRow > 0 Then
For k = LBound(OtherHeaders) To UBound(OtherHeaders)
If tArray(MatchRow, tOtherHeadersColumn(k)) <> sArray(i, sOtherHeadersColumn(k)) Then
'Logging
If IsLogging And DoOverwrite Then LoggingColumn(MatchRow, 1) = LoggingColumn(MatchRow, 1) & _
IIf(LoggingColumn(MatchRow, 1) <> "", ", ", "") & _
tHeaders.Cells(1, tOtherHeadersColumn(k)) & " : " & _
tArray(MatchRow, tOtherHeadersColumn(k)) & _
" -> " & sArray(i, sOtherHeadersColumn(k))
'Assign new value
If DoOverwrite Or tArray(MatchRow, tOtherHeadersColumn(k)) = VbNullString Then
tArray(MatchRow, tOtherHeadersColumn(k)) = sArray(i, sOtherHeadersColumn(k))
End If
End If
Next k
End If
Next i
'Write arrays to sheet
ExcludeRows(trng, 1).Value2 = tArray
With trng.Parent
If IsArrayInitialised(nArray) And AddIfMissing Then
.Cells(trng.Cells(1, 1).Row + trng.Rows.Count, trng.Cells(1, 1).Column).Resize(UBound(nArray, 1), UBound(nArray, 2)).Value2 = nArray
End If
If IsLogging Then
.Cells(trng.Cells(1, 1).Row, trng.Cells(1, 1).Column + trng.Columns.Count) = "Changes"
.Cells(trng.Cells(2, 1).Row, trng.Cells(1, 1).Column + trng.Columns.Count).Resize(UBound(LoggingColumn, 1), 1).Value2 = LoggingColumn
End If
End With
End Sub
And also add these functions inside your VBA project to as they are used in the procedure above.
Function IsArrayInitialised(ByRef A() As Variant) As Boolean
On Error Resume Next
IsArrayInitialised = IsNumeric(UBound(A))
On Error GoTo 0
End Function
Function ExcludeRows(MyRng As Range, StartRow As Long, Optional EndRow As Long = -1) As Range
'PURPOSE: Exclude one or more consecutives rows from an existing range
Dim Afterpart As Range, BeforePart As Range
If StartRow < 1 Or EndRow > MyRng.Rows.Count Then Set ExcludeRows = Nothing
If StartRow = 1 And EndRow = MyRng.Rows.Count Then Set ExcludeRows = Nothing
If EndRow = -1 Then EndRow = StartRow
If EndRow < MyRng.Rows.Count Then
With MyRng.Parent
Set Afterpart = .Range(MyRng.Cells(EndRow + 1, 1), MyRng.Cells(MyRng.Rows.Count, MyRng.Columns.Count))
End With
End If
If StartRow > 1 Then
With MyRng.Parent
Set BeforePart = .Range(MyRng.Cells(1, MyRng.Cells(1, 1).Column), MyRng.Cells(StartRow - 1, MyRng.Columns.Count))
End With
End If
Set ExcludeRows = Union2(True, BeforePart, Afterpart)
End Function
Public Function Union2(IgnoreEmptyRange As Boolean, ParamArray RangeArray() As Variant) As Range
'PURPOSE: Samae as Application.Union but allows some range object to be Empty
Dim V As Variant
Dim Rng As Range
For Each V In RangeArray
Do
If VarType(V) = vbEmpty Then Exit Do
Set Rng = V
If Not Union2 Is Nothing Then
Set Union2 = Union(Union2, Rng)
ElseIf Not Rng Is Nothing Then
Set Union2 = Rng
End If
Loop While False
Next
End Function
I am trying to convert the data based on the max repeated values.
I have truck numbers in col A and "Truck types" in column in B col.
For each unique truck number, the truck type should be same.(This is the expected result)
This can be achieved, by counting the maximum no. of truck types for the unique "truck no", and that cell to be updated with the Max. repeated "Truck type".
If there is equal no. of "Truck types" are available, It should be updated with the first available truck type.
Like this, there are thousands of rows to be updated. This can be
better understand by seeing the attached image.
I have attached the image & expected result is in the column C.
I have googled a lot, but I was unable to find the relevant solution.
Please help.
You do not say anything...
Please, test the next code. It works with assumption that the columns are sorted as we can see in the picture. It is very fast, since the result is put in an array and dropped on the sheet at once:
Sub findMaxCountVehType_Array()
Dim sh As Worksheet, lastRow As Long, rngVeh As Range, rngTemp As Range, arrFin As Variant
Dim i As Long, j As Long, w As Long, count As Long, maxCount As Long, ar As Long, maxStr As String
Set sh = ActiveSheet 'use here your sheet
lastRow = sh.Range("A" & Rows.count).End(xlUp).row
Set rngVeh = sh.Range("A2:C" & lastRow)
ReDim arrFin(1 To lastRow, 1 To 1)
arrFin(1, 1) = "Result": ar = 1
For i = 2 To lastRow
If sh.Range("A" & i).Value = sh.Range("A" & i + 1).Value Then
For j = i To j + 1000 'create a range of type cells for the same vehicle no
If sh.Range("A" & j).Value = sh.Range("A" & i).Value Then
If rngTemp Is Nothing Then
Set rngTemp = sh.Range("B" & j)
Else
Set rngTemp = Union(rngTemp, sh.Range("B" & j))
End If
Else
Exit For
End If
Next j
If rngTemp Is Nothing Then
ar = ar + 1: arrFin(ar, 1) = sh.Range("B" & i)
Else
For w = 1 To rngTemp.Cells.count 'determine the max occurrences string
count = WorksheetFunction.CountIf(rngTemp, rngTemp.Cells(w, 1).Value)
If count > maxCount Then maxCount = count: maxStr = rngTemp.Cells(w, 1).Value
Next
For w = 1 To rngTemp.Cells.count
ar = ar + 1: arrFin(ar, 1) = maxStr 'fill the max count in the array
Next
End If
Set rngTemp = Nothing: maxCount = 0: count = 0 'reinitialize variables
i = i + w - 2 'move the iteration to the following vehicle
Else
ar = ar + 1: arrFin(ar, 1) = sh.Range("B" & i)
End If
Next i
'drop the result array at once
sh.Range("C1").Resize(UBound(arrFin, 1), UBound(arrFin, 2)).Value = arrFin
End Sub
Here is a VBA routine that uses:
A class object which has
key:= Vehicle number
item:= dictionary of associated vehicle types
key:= vehicle type
item:= count of the vehicle types
After collecting the information, we merely need to cycle through the dictionary and extract, for any given vehicle ID, the vehicle type that has the largest count.
This routine, since it works entirely with VBA arrays, should run pretty fast, even with large amounts of data.
Also, with this method, no sorting is required.
ASSUMES the data starts in cell A1 (could be changed if necessary)
ASSUMES results are as you show with Desired Result in column C
Be sure to set a reference (Tools/References) to Microsoft Scripting Runtime
Class Module (rename this module cVehicle)
Option Explicit
Private pVehicleType As String
Private pVehicleTypes As Dictionary
Public Property Get VehicleType() As String
VehicleType = pVehicleType
End Property
Public Property Let VehicleType(Value As String)
pVehicleType = Value
End Property
Public Property Get VehicleTypes() As Dictionary
Set VehicleTypes = pVehicleTypes
End Property
Public Function addVehicleTypesItem(Value)
If pVehicleTypes.Exists(Value) Then
pVehicleTypes(Value) = pVehicleTypes(Value) + 1
Else
pVehicleTypes.Add Key:=Value, Item:=1
End If
End Function
Private Sub Class_Initialize()
Set pVehicleTypes = New Dictionary
pVehicleTypes.CompareMode = TextCompare
End Sub
Regular Module
'Set Reference to Microsoft Scripting Runtime
Option Explicit
Sub vehicle()
Dim dV As Dictionary, cV As cVehicle
Dim wsData As Worksheet, vData As Variant, rRes As Range
Dim V As Variant, I As Long, sKey As String, cKey As String, Cnt As Long
'set data worksheet
'read data into vba array
Set wsData = Worksheets("Sheet3")
With wsData
'add extra column for the "desired results"
vData = .Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp)).Resize(columnsize:=3)
Set rRes = .Cells(1, 1)
End With
'loop through the data and count the types
'no sorting necessary
Set dV = New Dictionary
For I = 2 To UBound(vData, 1)
Set cV = New cVehicle
With cV
sKey = vData(I, 1)
.VehicleType = vData(I, 2)
If Not dV.Exists(sKey) Then
.addVehicleTypesItem .VehicleType
dV.Add sKey, cV
Else
dV(sKey).addVehicleTypesItem .VehicleType
End If
End With
Next I
'Output the data
I = 1
'Header
vData(I, 3) = "Desired Result"
'Data
For I = 2 To UBound(vData, 1)
sKey = vData(I, 1)
With dV(sKey)
'which type has the highest count
Cnt = 0
For Each V In .VehicleTypes.Keys
If .VehicleTypes(V) > Cnt Then
Cnt = .VehicleTypes(V)
cKey = V
End If
Next V
vData(I, 3) = cKey
End With
Next I
'write the results
Set rRes = rRes.Resize(UBound(vData, 1), UBound(vData, 2))
rRes = vData
End Sub
I am using a macro to re-organise 4600 lines of data into a more efficient layout. Currently, i have a macro but it misses data or puts data in the wrong place.
From the old data, the column A is notification number, column FO is sheet number and GB is zone number. Whilst column C is the data that is wanting to be inputted. So currently (as the photo shows, the data is very unorganisedand unreadable.
In the outputted sheet, the notification number is put in Row 1 in columns F on wards (No duplicates). In Column B and C is zone and sheet number respectively (No duplicates). Then, using the old data, plot Column C values in the correct column(Depending on notification number) and the correct row (depending on zone and sheet number).
I have achieved half of this, but not all values are not be inputted correctly.
I currently use range.find to see if the zone number exists, and if it doesn't add the zone value and sheet number into the last used row. However, if the zone number is found but the corresponding sheet number is different, then add these values and then also add the values from column C. However, if the correct cell is filled, find the next available cell in column that is empty and input value.
But, I cant find a better way to check these values than using range.find but i feel it is missing values and not comparing both values correctly.
Sub GenerateTable()
Application.ScreenUpdating = False
Dim RawDataWsNotificationRng, ModifiedDataWsNotificationRng As Variant
Dim cell As Range
Dim RawDataWsNotificationlrow, ModifiedDataWsNotificationlcolnum, ModifiedDataWsZoneLrow As Long
Dim ModifiedDataWsNotificationlcol As String
Dim serverfilename, DataSheetName, Newsheetname As String
Dim wkbk1, wkbk2 As Workbook
Dim RawDataWs, ModifiedDataWs As Worksheet
Dim FindNotificationNumber As Variant
serverfilename = InputBox("Please input name of dummy workbook (file must be open, include .xlsx")
If serverfilename = "" Then Exit Sub
Set wb1 = ThisWorkbook
Set wb2 = Workbooks(serverfilename)
DataSheetName = InputBox("Please enter name of sheet where data is stored")
If DataSheetName = "" Then Exit Sub
Set RawDataWs = wb2.Sheets(DataSheetName)
Set ModifiedDataWs = Sheets.Add(After:=Sheets(Sheets.Count))
Newsheetname = InputBox("Please enter name of new sheet")
ModifiedDataWs.Name = Newsheetname
RawDataWsNotificationlrow = RawDataWs.Range("A" & Rows.Count).End(xlUp).Row
ModifiedDataWsZoneLrow = ModifiedDataWs.Range("B" & Rows.Count).End(xlUp).Row
ModifiedDataWsNotificationlcolnum = ModifiedDataWs.Cells(1, Columns.Count).End(xlToLeft).Column + 1
ModifiedDataWsNotificationlcol = Split(Cells(1, ModifiedDataWsNotificationlcolnum).Address, "$")(1)
Set RawDataWsNotificationRng = RawDataWs.Range("A2:A" & RawDataWsNotificationlrow)
Set ModifiedDataWsNotificationRng = ModifiedDataWs.Range("F1:" & ModifiedDataWsNotificationlcol & "1")
'------------------------------------TableFeatures---------------------------------------------
With ModifiedDataWs
.Cells(1, "A").Value = "Feature Code"
.Cells(1, "B").Value = "Zone"
.Cells(1, "C").Value = "Sheet"
.Cells(1, "D").Value = "Feature Description"
.Cells(1, "E").Value = "'-TEN OGV KH73126 tolerance"
.Cells(1, "F").Value = "'-TEN OGV KH73126 tolerance"
.Cells(2, "E").Value = "Nominal"
.Cells(2, "F").Value = "Tolerance"
'------------------------------------NotificationColumns---------------------------------------------
For Each cell In RawDataWsNotificationRng
Set ModifiedDataWsNotificationRng = .Range("G1:" & ModifiedDataWsNotificationlcol & "1")
Set FindNotificationNumber = ModifiedDataWsNotificationRng.Find(what:=RawDataWs.Cells(cell.Row, "A"), lookat:=xlWhole)
If FindNotificationNumber Is Nothing Then
ModifiedDataWsNotificationlcolnum = .Cells(1, Columns.Count).End(xlToLeft).Column + 1
ModifiedDataWsNotificationlcol = Split(Cells(1, ModifiedDataWsNotificationlcolnum).Address, "$")(1)
Cells(1, ModifiedDataWsNotificationlcol).Value = cell.Value
End If
Next cell
'------------------------------------ZoneandSheetValues---------------------------------------------
Dim RawDataWsZoneRng As Variant: Set RawDataWsZoneRng = RawDataWs.Range("GB2:GB" & RawDataWsNotificationlrow)
Dim ModifiedDataWsZoneRng As Variant: Set ModifiedDataWsZoneRng = ModifiedDataWs.Range("B:B")
Dim ModifiedDataWssheetRng As Variant: Set ModifiedDataWssheetRng = ModifiedDataWs.Range("C:C")
Dim RawDataWsExtentRng As Variant: Set RawDataWsExtentRng = RawDataWs.Range("C2:C" & RawDataWsNotificationlrow)
Dim cel As Range
Dim ColumnLetterLRow, LR As Long, ColumnLetter As String, FindSheetinModifiedWs As Variant
ModifiedDataWsZoneLrow = ModifiedDataWs.Range("B" & Rows.Count).End(xlUp).Row
For Each cell In RawDataWsZoneRng
Set FindNotificationNumber = ModifiedDataWsNotificationRng.Find(what:=RawDataWs.Cells(cell.Row, "A"), lookat:=xlWhole)
Set FindZoneInModifiedWs = ModifiedDataWsZoneRng.Find(what:=cell.Value, lookat:=xlWhole)
Set FindSheetinModifiedWs = ModifiedDataWssheetRng.Find(what:=RawDataWs.Cells(cell.Row, "FO"), lookat:=xlWhole)
If RawDataWs.Cells(cell.Row, "H").Value = "CONACC" Then
If FindZoneInModifiedWs Is Nothing Then
LR = .Range("A:" & ModifiedDataWsNotificationlcol).SpecialCells(xlCellTypeLastCell).Row + 1
.Cells(LR, FindNotificationNumber.Column).Value = RawDataWs.Cells(cell.Row, "C").Value
.Cells(LR, "B").Value = RawDataWs.Cells(cell.Row, "GB").Value
.Cells(LR, "C").Value = RawDataWs.Cells(cell.Row, "FO").Value
Else
If Not FindZoneInModifiedWs Is Nothing And FindSheetinModifiedWs Is Nothing Then
LR = .Range("A:" & ModifiedDataWsNotificationlcol).SpecialCells(xlCellTypeLastCell).Row + 1
.Cells(LR, FindNotificationNumber.Column).Value = RawDataWs.Cells(cell.Row, "C").Value
.Cells(LR, "B").Value = RawDataWs.Cells(cell.Row, "GB").Value
.Cells(LR, "C").Value = RawDataWs.Cells(cell.Row, "FO").Value
Else
If cell.Value <> vbNullString Then
ColumnLetter = Split(Cells(1, FindNotificationNumber.Column).Address, "$")(1)
If (.Cells(FindZoneInModifiedWs.Row, ColumnLetter) = vbNullString) Then
ColumnLetterLRow = FindZoneInModifiedWs.Row
Else
Set ColumnLetterRow = .Range(ColumnLetter & FindZoneInModifiedWs.Row & ":" & ColumnLetter & "30000").Find(what:="", lookat:=xlWhole)
ColumnLetterLRow = ColumnLetterRow.Row
End If
.Cells(ColumnLetterLRow, FindNotificationNumber.Column).Value = RawDataWs.Cells(cell.Row, "C").Value
.Cells(ColumnLetterLRow, "B").Value = RawDataWs.Cells(cell.Row, "GB").Value
.Cells(ColumnLetterLRow, "C").Value = RawDataWs.Cells(cell.Row, "FO").Value
End If
End If
End If
End If
ModifiedDataWsZoneLrow = ModifiedDataWs.Range("B" & Rows.Count).End(xlUp).Row
Next cell
'--------------------------Loop through zones and find input all values for zones-----------------
ModifiedDataWsZoneLrow = ModifiedDataWs.Range("B" & Rows.Count).End(xlUp).Row
Set ModifiedDataWsZoneRng = ModifiedDataWs.Range("B3:B" & ModifiedDataWsZoneLrow)
Dim nextrow As Long
For Each cell In ModifiedDataWsZoneRng
For Each cel In RawDataWsZoneRng
If cel.Value = cell.Value Then
Set FindNotificationNumber = ModifiedDataWsNotificationRng.Find(What:=RawDataWs.Cells(cel.Row, "A"), lookat:=xlWhole)
Set FindZoneInModifiedWs = ModifiedDataWsZoneRng.Find(What:=cell.Value, lookat:=xlWhole)
If IsEmpty(.Cells(FindZoneInModifiedWs.Row, FindNotificationNumber.Column).Value) = True Then
.Cells(FindZoneInModifiedWs.Row, FindNotificationNumber.Column).Value = RawDataWs.Cells(cel.Row, "C").Value
.Cells(FindZoneInModifiedWs.Row, "B").Value = RawDataWs.Cells(cel.Row, "GB").Value
.Cells(FindZoneInModifiedWs.Row, "C").Value = RawDataWs.Cells(cel.Row, "FO").Value
Else
End If
End If
Next cel
Next cell
any ideas would be greatly appreciated! sorry i am new to VBA!
Old Data Sheet
New Sheet
Link to workbook
Link to workbook
Well, that more more complex than i'd thought but here goes:
'type to manage data we use from each row
Type dataRow
notif As Variant
variable As Variant
sht As Variant
zone As Variant
End Type
Sub DoPivot()
Const SEP As String = "<>"
Dim rngData As Range, data, r As Long
Dim colDict As Object, rowDict As Object, comboDict As Object
Dim rd As dataRow, rngOutput As Range, col As Long, rw As Long, k
Dim k2, arr, dictCounts As Object
Dim wsOut As Worksheet, num As Long
Set colDict = CreateObject("scripting.dictionary")
Set rowDict = CreateObject("scripting.dictionary")
Set comboDict = CreateObject("scripting.dictionary")
Set dictCounts = CreateObject("scripting.dictionary")
data = Sheet9.Range("A2:D4788").Value 'source data
Set rngOutput = Sheet9.Range("H1") 'top-left cell for output
Set wsOut = rngOutput.Parent
rngOutput.Resize(5000, 5000).ClearContents
rngOutput.Resize(1, 2).Value = Array("Sheet", "Zone")
col = rngOutput.Column + 2 'start for notification# headers
rw = rngOutput.row + 1
'first pass - assess data variables
For r = 1 To UBound(data, 1)
rd = rowData(data, r)
k = Join(Array(rd.sht, rd.zone, rd.notif), SEP) 'tracking how many unique combinations of these
comboDict(k) = comboDict(k) + 1 'increment count
'manage column header positions for unique notification numbers
If Not colDict.exists(rd.notif) Then
colDict.Add rd.notif, col 'store the column
rngOutput.EntireRow.Cells(1, col).Value = rd.notif 'add the header
col = col + 1
End If
Next r
'figure out # of rows for each sheet-Zone pair
For Each k In comboDict.keys
arr = Split(k, SEP)
k2 = Join(Array(arr(0), arr(1)), SEP) 'sheet<>zone
'is this more rows than any previous same k2 value?
dictCounts(k2) = Application.Max(dictCounts(k2), comboDict(k))
Next k
'create the row headers
For Each k In dictCounts.keys
num = dictCounts(k)
rowDict(k) = rw 'record start row for each sheet<>zone combo
wsOut.Cells(rw, rngOutput.Column).Resize(num, 2).Value = Split(k, SEP)
dictCounts(k) = 0 'reset so we can track while adding data
rowDict(k) = rw
rw = rw + num
Next k
'last pass - populate the data based on the dictionaries
For r = 1 To UBound(data, 1)
rd = rowData(data, r)
k = Join(Array(rd.sht, rd.zone, rd.notif), SEP) '3-field combo
k2 = Join(Array(rd.sht, rd.zone), SEP) 'row key
wsOut.Cells(rowDict(k2) + (dictCounts(k)), _
colDict(rd.notif)).Value = rd.variable
dictCounts(k) = dictCounts(k) + 1 'increment this unique combo
Next r
End Sub
'populate a Type instance for a given row
Function rowData(data, r As Long) As dataRow
Dim rv As dataRow
rv.notif = IfEmpty(data(r, 1))
rv.variable = IfEmpty(data(r, 2))
rv.sht = IfEmpty(data(r, 3))
rv.zone = IfEmpty(data(r, 4))
rowData = rv
End Function
'substitute EMPTY for zero-length value
Function IfEmpty(v)
IfEmpty = IIf(Len(v) = 0, "EMPTY", v)
End Function
EDIT: if you want to filter out certain rows then you need to modify the loops which iterate over data
For r = 1 To UBound(data, 1)
If data(r, colHere) <> "X" Then '<< add your filter here
rd = rowData(data, r)
'rest of code as before...
End If
Next r
I'm currently working on a project which needs to build graph regarding to a table of analyses to check if the products work with time.
The user starts to choose which products he want to check and the code create a table regarding that.
The two main values are the date and the result which need to be on the graph and the third one is the batch number which needs to be the name of each chart series.
After that the code creates a 2D array with the table.
For Each elementReo In Range("tabReorganize[Date]")
ReDim Preserve tabReo(2, r)
tabReo(0, r) = elementReo
tabReo(1, r) = 0 & elementReo.Offset(0, 1)
tabReo(2, r) = elementReo.Offset(0, 2)
r = r + 1
Next elementReo
And after that I want to create the graph regarding to the number of different batch number that I have.
'This part create the Chart and set the title
Set ChartObj = ActiveSheet.ChartObjects.Add(Left:=2979.75, Width:=550, Top:=358.5, Height:=325)
ChartObj.Chart.ChartType = xlLine
ChartObj.Chart.SetElement (msoElementChartTitleAboveChart)
ChartObj.Chart.ChartTitle.Text = "Humidite"
Dim tabNBN() As String
Dim NBN As Integer
Dim checkNBN As Boolean
ReDim tabNBN(NBN)
Dim SeriesI As Integer
NBN = 0
SeriesI = 0
'Add value in tabNBN regarding to the number of different batch number
For r2 = 0 To r - 1 Step 1
checkNBN = False
For Each elementNBN In tabNBN
If elementNBN = tabReo(1, r2) Then
checkNBN = True
End If
Next elementNBN
If checkNBN = False Then
ReDim Preserve tabNBN(NBN)
tabNBN(NBN) = tabReo(1, r2)
NBN = NBN + 1
End If
Next r2
So I need something to add the series regarding of the number of different batch number and insert the value and the date there.
I'm a beginner with charts in VBA.
if my understanding of the objective is correct then congratulation for a good & challenging question. Assuming the objective is to create a single chart with multiple series representing each batch listed in the range. If assumed result is like the following
then may try the test code (obviously after modifying the range, sheet etc to requirement). The code used Dictionary object, so please add Tools-> Reference to "Microsoft Scripting Runtime". Though I am not fully satisfied with the code regarding some multiple looping etc (degrading the performance) but would work OK with normal data assuming 100/200 rows. I invite experts response for more efficient code in this regard
Option Explicit
Sub test3()
Dim Cht As Chart, ChartObj As ChartObject
Set ChartObj = ActiveSheet.ChartObjects.Add(Left:=10, Width:=550, Top:=10, Height:=325)
'Set ChartObj = ActiveSheet.ChartObjects("Chart 4")
Set Cht = ChartObj.Chart
Cht.ChartType = xlLine
Cht.HasTitle = True
Cht.ChartTitle.Text = "Humidite"
Dim Rw As Long, Dic As Dictionary, DataArr As Variant, OutArr() As Variant, BatchArr() As Variant, DateArr As Variant
Dim Rng As Range, SeriesNo As Long, Dmax As Date, Dmin As Date, dt As Date
Dim X As Long, i As Long, Xbatch As Variant, Batch As Variant
Dim Cnt As Long, Xval As Variant, PrvDt As Date, C As Range, DayCnt As Long
Dim firstAddress As String
Set Dic = CreateObject("Scripting.dictionary")
Set Rng = ThisWorkbook.ActiveSheet.Range("A2:A100") 'Modify to requireMent
DataArr = ThisWorkbook.ActiveSheet.Range("A2:C100") 'Modify to requireMent
SeriesNo = 0
'Create dictionary reference to unique Batch name from the list
For Rw = 1 To UBound(DataArr, 1)
Batch = DataArr(Rw, 2)
If Dic.Exists(Batch) = False Then
SeriesNo = SeriesNo + 1
Dic.Add Batch, SeriesNo
End If
Next
Dmax = Application.WorksheetFunction.Max(Range(Rng(1, 1), Rng(Rng.Rows.Count, 1)))
Dmin = Application.WorksheetFunction.Min(Range(Rng(1, 1), Rng(Rng.Rows.Count, 1)))
DayCnt = Dmax - Dmin + 1
ReDim BatchArr(1 To DayCnt)
ReDim DateArr(1 To DayCnt)
ReDim OutArr(1 To SeriesNo, 1 To DayCnt)
'Populate DateArr for dates
For X = 1 To DayCnt
DateArr(X) = Dmin + X - 1
Next
'Populate OutArr(Series,DayCnt) with existing Values, Non existing values are kept empty
For X = 1 To DayCnt
dt = DateArr(X)
With Rng
Set C = .Find(dt)
If Not C Is Nothing Then
firstAddress = C.Address
Do
OutArr(Dic(C.Offset(0, 1).Value), X) = C.Offset(0, 2).Value
'Debug.Print C.Value, C.Offset(0, 1).Value, C.Offset(0, 2).Value
Set C = .FindNext(C)
Loop While Not C Is Nothing And C.Address <> firstAddress
End If
End With
Next
With Cht
'delete If any automatically added series
For i = Cht.SeriesCollection.Count To 1 Step -1
.SeriesCollection(i).Delete
Next
'Create Series and Set Values & Xvalues from OutArr
Dim Srs As Series
For X = 1 To SeriesNo
Batch = Dic.Keys(X - 1)
For Cnt = 1 To DayCnt
BatchArr(Cnt) = OutArr(Dic(Batch), Cnt)
'If IsEmpty(BatchArr(Cnt)) = False Then Debug.Print X, Cnt, BatchArr(Cnt), DateArr(Cnt)
Next
Cht.SeriesCollection.NewSeries
Set Srs = Cht.SeriesCollection(X)
With Srs
.Values = BatchArr
.XValues = DateArr
.Name = Dic.Keys(X - 1)
End With
Next
Dim Cat As Axis
Set Cat = Cht.Axes(xlCategory)
Cat.TickLabels.NumberFormat = "dd/mm/yy"
End With
End Sub
Please comment if it suits your need
This code should create a table regarding to another table (the one with all different batch numbers and values) and the user selection and after build the chart with it.
I can send you the full file by mail if needed.
Thanks in advance.
Best regards
colin
Private Sub BtnGraph2_Click()
Dim tabBNumber() As String
Dim tabHumidite() As Double
Dim tabDate() As String
Dim tabReo() As String
Dim y As Integer
Dim h As Integer
Dim d As Integer
Dim a As Integer
Dim w As Integer
Dim w2 As Integer
Dim r As Integer
h = 0
y = 0
d = 0
w = 1
w2 = 1
r = 0
ReDim tabHumidite(h)
ReDim tabBNumber(y)
ReDim tabDate(d)
Range("tabReorganize[#data]") = ""
ListObjects("tabReorganize").Resize Range(Range("tabReorganize[#headers]").Address, Range("tabReorganize[#headers]").Offset(1).Address)
For i6 = ListBox1.ListCount - 1 To 0 Step -1
If ListBox1.Selected(i6) = True Then
ReDim Preserve tabBNumber(y)
tabBNumber(y) = ListBox1.List(i6)
y = y + 1
End If
Next i6
For Each delement In tabBNumber
For Each delement2 In Range("tabGraph[Date]")
If "0" & delement2.Offset(0, 2) = delement Or delement2.Offset(0, 2) = delement Then
ReDim Preserve tabDate(d)
tabDate(d) = delement2
d = d + 1
End If
Next delement2
Next delement
For Each Oelement In tabDate
Range("tabReorganize[Date]").Cells(w) = Format(Oelement, "mm/dd/yyyy")
w = w + 1
Next Oelement
If BtnHumidite = True Then
For Each element In tabBNumber
h = 0
a = 0
ReDim tabHumidite(h)
For Each Gelement In Range("tabGraph[Humidite]")
If "0" & Gelement.Offset(0, -1) = element Or Gelement.Offset(0, -1) = element Then
ReDim Preserve tabHumidite(h)
tabHumidite(h) = Gelement
h = h + 1
End If
Next Gelement
For Each O2element In tabHumidite
Range("tabReorganize[Humidite]").Cells(w2) = Format(O2element, "###0.00")
Range("tabReorganize[Batch Number]").Cells(w2) = Format(element, "00000000")
w2 = w2 + 1
Next O2element
Next element
End If
Range("tabReorganize").Sort Key1:=Range("tabReorganize[[#All],[Date]]"), Order1:=xlAscending, Header:=xlYes
For Each elementReo In Range("tabReorganize[Date]")
ReDim Preserve tabReo(2, r)
tabReo(0, r) = elementReo
tabReo(1, r) = 0 & elementReo.Offset(0, 1)
tabReo(2, r) = elementReo.Offset(0, 2)
r = r + 1
Next elementReo
'''' Chart part
Dim Cht As Chart, ChartObj As ChartObject
Set ChartObj = ActiveSheet.ChartObjects.Add(Left:=2979.75, Width:=550, Top:=358.5, Height:=325)
Set Cht = ChartObj.Chart
Cht.ChartType = xlLine
Cht.HasTitle = True
Cht.ChartTitle.Text = "Humidite"
Dim Rw As Long, Dic As Dictionary, DataArr As Variant, OutArr() As Variant, BatchArr() As Variant, DateArr As Variant
Dim Rng As Range, SeriesNo As Long, Dmax As Date, Dmin As Date, dt As Date
Dim X As Long, i As Long, Xbatch As Variant, Batch As Variant
Dim Cnt As Long, Xval As Variant, PrvDt As Date, C As Range, DayCnt As Long
Dim firstAddress As String
Set Dic = CreateObject("Scripting.dictionary")
Set Rng = ThisWorkbook.ActiveSheet.Range("AP13:AP42") 'Modify to requireMent
'Set Rng = ThisWorkbook.ActiveSheet.Range("tabReorganize[Date]")
DataArr = ThisWorkbook.ActiveSheet.Range("AP13:AR42") 'Modify to requireMent
'DataArr = ThisWorkbook.ActiveSheet.Range("tabReorganize[#data]")
SeriesNo = 0
'Create dictionary reference to unique Batch name from the list
For Rw = 1 To UBound(DataArr, 1)
Batch = DataArr(Rw, 2)
If Dic.Exists(Batch) = False Then
SeriesNo = SeriesNo + 1
Dic.Add Batch, SeriesNo
End If
Next
Dmax = Application.WorksheetFunction.max(Range(Rng(1, 1), Rng(Rng.Rows.Count, 1)))
Dmin = Application.WorksheetFunction.Min(Range(Rng(1, 1), Rng(Rng.Rows.Count, 1)))
DayCnt = Dmax - Dmin + 1
ReDim BatchArr(1 To DayCnt)
ReDim DateArr(1 To DayCnt)
ReDim OutArr(1 To SeriesNo, 1 To DayCnt)
'Populate DateArr for dates
For X = 1 To DayCnt
DateArr(X) = Dmin + X - 1
Next
'Populate OutArr(Series,DayCnt) with existing Values, Non existing values are kept empty
For X = 1 To DayCnt
dt = DateArr(X)
With Rng
Set C = .Find(dt)
If Not C Is Nothing Then
firstAddress = C.Address
Do
OutArr(Dic(C.Offset(0, 1).Value), X) = C.Offset(0, 2).Value
'Debug.Print C.Value, C.Offset(0, 1).Value, C.Offset(0, 2).Value
Set C = .FindNext(C)
Loop While Not C Is Nothing And C.Address <> firstAddress
End If
End With
Next
With Cht
'delete If any automatically added series
For i = Cht.SeriesCollection.Count To 1 Step -1
.SeriesCollection(i).Delete
Next
'Create Series and Set Values & Xvalues from OutArr
Dim Srs As Series
For X = 1 To SeriesNo
Batch = Dic.Keys(X - 1)
For Cnt = 1 To DayCnt
BatchArr(Cnt) = OutArr(Dic(Batch), Cnt)
'If IsEmpty(BatchArr(Cnt)) = False Then Debug.Print X, Cnt, BatchArr(Cnt), DateArr(Cnt)
Next
Cht.SeriesCollection.NewSeries
Set Srs = Cht.SeriesCollection(X)
With Srs
.Values = BatchArr
.XValues = DateArr
.Name = Dic.Keys(X - 1)
End With
Next
Dim Cat As Axis
Set Cat = Cht.Axes(xlCategory)
Cat.TickLabels.NumberFormat = "mm/dd/yy"
End With