In excel VBA create multidimensional dictionary - excel

I am trying to sort through a couple hundred rows in a workbook to pull information based on progressive keys. First, create a list of all unique names, then for each unique name find all associated product codes, and finally create a list of each quantity of product. What it should look like:
'Name1
'-----product1
'-------------quantity1
'-------------quantity2
'-----product2
'-------------quantity1
'-------------quantity2
'name2
'-----product1
'-------------quantity1
'-------------quantity2
'-----product2
'-------------quantity1
'-------------quantity2
I tried using a dictionary but can't figure out how to get it to return more than the first entry per unique name. This is the code I have so far:
Sub CreateNameList2()
Application.ScreenUpdating = False
Dim wb As Workbook: Set wb = ThisWorkbook
Dim sws As Worksheet: Set sws = wb.Worksheets("Label-Mod Data")
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim AssociateName As String
Dim ColAssociateNames As Integer
Dim ColCurrentLabels As Integer
Dim ColPTSCodes As Integer
Dim ColRegionCodes As Integer
Dim nbRows As Long
Dim iRow As Integer
Dim i As Integer
Dim k As Variant
ColAssociateNames = 8
ColCurrentLabels = 9
ColPTSCodes = 14
ColRegionCodes = 15
nbRows = 155
i = 2
For iRow = 2 To nbRows
AssociateName = sws.Cells(iRow, ColAssociateNames).Value2
If Not dict.Exists(AssociateName) Then
dict.Add Key:=AssociateName, Item:=Array(sws.Cells(i, ColPTSCodes).Value2, sws.Cells(i, ColCurrentLabels).Value2, sws.Cells(i, ColRegionCodes).Value2)
i = i + 1
End If
Next iRow
iRow = 2
For Each k In dict.Keys
With sws
.Cells(iRow, 18).Value2 = k
.Cells(iRow, 19).Value2 = dict.Item(k)(0)
.Cells(iRow, 20).Value2 = dict.Item(k)(1)
.Cells(iRow, 21).Value2 = dict.Item(k)(2)
End With
iRow = iRow + 1
Next k
Set dict = Nothing
Debug.Print
Application.ScreenUpdating = True
End Sub
Can this be done with a dictionary?
For privacy reasons I can't show the data but I will try to explain it.
My raw data comes in 3 columns and varies in number of rows, todays is 155. Column 1 has a name, column 2 has a product ID and column 3 has a quantity. There are currently 48 possible names, 12 possible product ID's and undetermined quantity amounts. Looks Like this:
Name1 | product 3 | 25
Name1 | product 1 | 12
Name5 | product 9 | 171
Name4 | product 3 | 48
Name1 | product 7 | 23
Name42 | product 9 | 9
Name5 | product 1 | 22
Name4 | product 3 | 42
What I need to do is change it to:
Name1 | product 1 | 12
| product 3 | 25
| product 7 | 23
Name4 | product 3 | 90
(combine above quantity with matching name and product)
Name5 | product 1 | 22
| product 9 | 171
Name42 | product 9 | 9

Like this would work:
Sub Tester()
Dim dict As Object, rw As Range, Q, kN, kP
Set dict = CreateObject("scripting.dictionary")
Set rw = Range("A1:C1") 'first row of data
Do While Application.CountA(rw) = 3 'while row data is complete
kN = rw.Cells(1).Value 'name key
kP = rw.Cells(2).Value 'product key
Q = rw.Cells(3).Value 'quantity
'add keys if missing...
If Not dict.exists(kN) Then dict.Add kN, CreateObject("scripting.dictionary")
If Not dict(kN).exists(kP) Then dict(kN).Add kP, 0
dict(kN)(kP) = dict(kN)(kP) + Q 'sum quantity for this key combination
Set rw = rw.Offset(1) 'next row
Loop
'output to Immediate pane
For Each kN In dict
Debug.Print "---" & kN & "---"
For Each kP In dict(kN)
Debug.Print " " & kP & " = " & dict(kN)(kP)
Next
Next kN
End Sub

Based on the additional information provided, it looks like you can make use of a composite key comprised of the Name and Product identifiers. Doing so can support a solution that uses an AssociateName-to-nameProductQuantityMap Dictionary where nameProductQuantityMap is also a Dictionary that associates Quantity totals to each Name + Product composite key.
Option Explicit
'May need a more elaborate delimiter if "." used in the Names or Products
Const compositeKeyDelimiter As String = "."
Private Type TColumns
Associate As Long
Name As Long
Product As Long
Quantity As Long
End Type
Sub CreateNameList2Answer()
Application.ScreenUpdating = False
Dim wb As Workbook: Set wb = ThisWorkbook
Dim sws As Worksheet: Set sws = wb.Worksheets("Label-Mod Data")
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim AssociateName As String
Dim ColAssociateNames As Integer
Dim ColCurrentLabels As Integer
Dim ColPTSCodes As Integer
Dim ColRegionCodes As Integer
Dim nbRows As Long
Dim iRow As Integer
Dim i As Integer
ColAssociateNames = 8
ColCurrentLabels = 9
ColPTSCodes = 14
ColRegionCodes = 15
nbRows = 155
i = 2
'Modify if these are not mapped to the correct column names
Dim xColumns As TColumns
xColumns.Associate = ColAssociateNames
xColumns.Name = ColCurrentLabels
xColumns.Product = ColPTSCodes
xColumns.Quantity = ColRegionCodes
Dim xAssociateName As Variant
Dim xName As String
Dim xProduct As String
Dim xQuantity As Long
Dim xCompositeKey As String
Dim nameProductQuantityMap As Object
For iRow = 2 To nbRows
AssociateName = sws.Cells(iRow, xColumns.Associate).Value2
xName = sws.Cells(i, xColumns.Name).Value2
xProduct = sws.Cells(i, xColumns.Product).Value2
xQuantity = CLng(sws.Cells(i, xColumns.Quantity).Value2)
xCompositeKey = CreateCompositeKey(xName, xProduct)
If Not dict.Exists(AssociateName) Then
dict.Add Key:=AssociateName, Item:=CreateObject("Scripting.Dictionary")
End If
Set nameProductQuantityMap = dict.Item(AssociateName)
If Not nameProductQuantityMap.Exists(xCompositeKey) Then
nameProductQuantityMap.Add xCompositeKey, 0
End If
nameProductQuantityMap.Item(xCompositeKey) _
= nameProductQuantityMap.Item(xCompositeKey) + xQuantity
i = i + 1
Next iRow
iRow = 2
Dim xKey As Variant
For Each xAssociateName In dict.Keys
Set nameProductQuantityMap = dict.Item(xAssociateName)
For Each xKey In nameProductQuantityMap
LoadContent sws, iRow, CStr(xAssociateName), _
CStr(xKey), _
nameProductQuantityMap.Item(xKey)
iRow = iRow + 1
Next
Next xAssociateName
Set dict = Nothing
Set nameProductQuantityMap = Nothing
Debug.Print
Application.ScreenUpdating = True
End Sub
Private Sub LoadContent(ByVal pWksht As Worksheet, ByVal pRow As Long, _
ByVal pAssociate As String, _
ByVal pCompositeKey As String, _
ByVal pQuantity As Long)
Dim xName As String
Dim xProduct As String
ExtractNameAndProductFromKey pCompositeKey, xName, xProduct
With pWksht
.Cells(pRow, 18).Value2 = pAssociate
.Cells(pRow, 19).Value2 = xName
.Cells(pRow, 20).Value2 = xProduct
.Cells(pRow, 21).Value2 = pQuantity
End With
End Sub
Private Function CreateCompositeKey(ByVal pName As String, ByVal pProduct As String) As String
CreateCompositeKey = pName & compositeKeyDelimiter & pProduct
End Function
Private Sub ExtractNameAndProductFromKey(ByVal pCompositeKey As String, ByRef pOutName As String, ByRef pOutProduct As String)
Dim xKeyParts As Variant
xKeyParts = Split(pCompositeKey, compositeKeyDelimiter)
pOutName = xKeyParts(0)
pOutProduct = xKeyParts(1)
End Sub

Related

Not sure why results are transposing from RecordSet in Excel Sheet

I am a newbie at using Recordsets and need some assistance as to why the data is transposing from the Database to Excel. I believe its in the following code, but not sure.
For iRow = 0 To intNumReturned - 1
For iCol = 0 To intNumColumns - 1
Debug.Print rsRecords(iCol, iRow)
Next iCol
Next iRow
Result from SQL String, which is expected:
PROCESSOR | ACCOUNT NUMBER | LOAN AMOUNT | ORIGNATION DATE
ZJE xxxxxxx XXXXXX.XX 2018-01-01
ZJE xxxxxxx XXXXXX.XX 2018-02-06
How the data gets placed into the Excel Sheet:
PROCESSOR | ACCOUNT NUMBER | LOAN AMOUNT | ORIGNATION DATE
ZJE ZJE ZJE ZJE
acct no acct no acct no acct no
loan amt loan amt loan amt loan amt
Org Date Org Date Org Date org date
Below is the code i am currently using minus the Connection and SQL Strings:
Sub RunSearch()
Dim conn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim connStr As String, strSQL As String
Dim begQ1 As String, endQ1 As String, begQ2 As String, endQ2 As String, begQ3 As String, endQ3 As String, begQ4 As String, endQ4 As String
Dim ctrl As Control
Dim rsRecords As Variant
Dim intNumReturned As Long, intNumColumns As Long, iCol As Long, iRow As Long, fldCount As Long, i As Long, rsCount As Long
Set wb = ThisWorkbook
Set wsVol = wb.Sheets("Volume By Processor")
Set wsDE = wb.Sheets("DE")
Set quarterYear = wsDE.Range("Quarter_Year")
Set Q = wsDE.Range("Quarter")
Set fDate = wsDE.Range("From_Date")
Set tDate = wsDE.Range("To_Date")
Do While Not rs.EOF
rs.MoveNext
rsRecords = rs.GetRows
intNumReturned = UBound(rsRecords, 2) + 1
intNumColumns = UBound(rsRecords, 1) + 1
Loop
For iRow = 0 To intNumReturned - 1
For iCol = 0 To intNumColumns - 1
Debug.Print rsRecords(iCol, iRow)
Next iCol
Next iRow
'\\\NEED TO FIGURE OUT NUMBER OF RECORDS TO
'\\\DEFINE LAST ROW/COLUMN OF RANGE???? PROBABLY A
'\\\SEPARATE QUESTION ON SO
wsVol.Range("B3:E1800") = rsRecords
rs.Close
Set rs = Nothing
conn.Close
Set conn = Nothing
I am expecting the result in the Excel sheet to match the the output from the SQL String any assistance is greatly appreciated.
If you want go to another "entry" of the recordset you can use ".MoveNext" method.
It will be something like this:
rsRecords.MoveFirst 'In this line you will back for the first entry
For iRow = 0 To intNumReturned - 1
For iCol = 0 To intNumColumns - 1
Debug.Print rsRecords(iCol)
Next iCol
rsRecords.MoveNext 'In this line you move to the next "row" as you want
Next iRow

VB Script for Excel taking very long time to calculate the values

I am writing a vb script on excel below is my problem.
I have more than 20 sheets in excel and one main sheet (All Programs with 200 names). Each sheet has column with Names and 24months(Jan18 to Dec18, Jan19 to Dec20).
Each sheet names is subset of main sheet
Main sheet(All Programs) has 200 Names and 24 months (values to be calculated based on other sheets )
Other sheet has names and values for each month respective to main sheet
I need to take each name in main sheet and search the name all other sheet, if present sum all same column values and insert in main sheet .
For 1 name i need to do calculation on 34 cells (For 200 names * 34 cells = 6800 cells) . Its taking almost 20minutes with my above code. Is there any other way i can do it or any modification which improves the performance?
Below is my code and example
Thanks in advance.
Example :
Main Sheet has name "employee1"
Sheet1
Sheet2
Value on the main sheet should be calculated respect to months
Dim sheetCount As Integer
Dim datatoFind
Private Sub CommandButton1_Click()
Dim mainSheet As String: mainSheet = "All Programs"
Dim nameColumnStart As String: nameColumnStart = "A"
Dim namesStart As Integer: namesStart = 1
Dim namesEnd As Integer: namesEnd = 200
Dim startColumn As Integer: startColumn = 10 'J Column'
Dim EndColumn As Integer: EndColumn = 33 'AG Column'
namesStart = InputBox("Please enter start value")
namesEnd = InputBox("Please enter end value")
Dim temp_str As String
Dim total As Single
On Error Resume Next
Sheets(mainSheet).Activate
lastRow_main = ActiveCell.SpecialCells(xlLastCell).Row
lastCol_main = 34
For vRow = namesStart To namesEnd
temp_str = Sheets(mainSheet).Cells(vRow, "A").Text
datatoFind = StrConv(temp_str, vbLowerCase)
For vCol = startColumn To EndColumn
total = Find_Data(vCol)
Worksheets(mainSheet).Cells(vRow, vCol).Value = total
Next vCol
Next vRow
Sheets(mainSheet).Activate
'MsgBox ("Calculated all values")'
End Sub
Private Function Find_Data(ByVal ColumnName As Integer) As Single
Dim counter As Integer
Dim currentSheet As Integer
Dim sheetCount As Integer
Dim str As String
Dim lastRow As Long
Dim lastCol As Long
Dim val As Single
Find_Data = 0
currentSheet = ActiveSheet.Index
If datatoFind = "" Then Exit Function
sheetCount = ActiveWorkbook.Sheets.Count
For counter = 2 To sheetCount
Sheets(counter).Activate
lastRow = ActiveCell.SpecialCells(xlLastCell).Row
lastCol = ActiveCell.SpecialCells(xlLastCell).Column
For vRow = 1 To lastRow
str = Sheets(counter).Cells(vRow, "A").Text
If InStr(1, StrConv(str, vbLowerCase), datatoFind) Then
val = Sheets(counter).Cells(vRow, ColumnName).Value
Find_Data = Find_Data + val
End If
Next vRow
Next counter
End Function
Why not collect the data on one sheet instead of different sheets?
Instead of sheets use a filter in column A!
And then use a pivot table to sum up everything!
The calculation is done in seconds!
Please try replace this code:
For vRow = namesStart To namesEnd
temp_str = Sheets(mainSheet).Cells(vRow, "A").Text
datatoFind = StrConv(temp_str, vbLowerCase)
For vCol = startColumn To EndColumn
total = Find_Data(vCol)
Worksheets(mainSheet).Cells(vRow, vCol).Value = total
Next vCol
Next vRow
With:
With Sheets(mainSheet)
For vRow = namesStart To namesEnd
temp_str = .Cells(vRow, "A").Text
datatoFind = StrConv(temp_str, vbLowerCase)
For vCol = startColumn To EndColumn
total = Find_Data(vCol)
.Cells(vRow, vCol).Value = total
Next vCol
Next vRow
End With
And this code:
For vRow = 1 To lastRow
str = Sheets(counter).Cells(vRow, "A").Text
If InStr(1, StrConv(str, vbLowerCase), datatoFind) Then
val = Sheets(counter).Cells(vRow, ColumnName).Value
Find_Data = Find_Data + val
End If
Next vRow
replace with:
With Sheets(counter)
For vRow = 1 To lastRow
str = .Cells(vRow, "A").Text
If InStr(1, StrConv(str, vbLowerCase), datatoFind) Then
val = .Cells(vRow, ColumnName).Value
Find_Data = Find_Data + val
End If
Next vRow
End With

Loop through a range to create a tree of nested data

I need to create a list of part numbers, which shows all other sub parts that are used to create that first part.
So for example part 12345 is built by combining abc and def.
I have a list of the top level parts, and a second list with two columns showing the top level on the left, and the sub part on the right.
e.g:
| Top Level Part | | Top Level Part | Sub Part |
| 123456 | | 123456 | abc |
| 234567 | | 123456 | def |
| 234567 | ghi |
| 234567 | jkl |
| abc | yyy |
| abc | zzz |
| yyy | 000000 |
I have used a for each loop to look through each part in the first table and compare it to the second, returning each sub part to the right. However I am struggling to go deeper than one level.
What I want to be able to do is once the sub part is found to loop back through the list looking for that part number and returning it's sub part. And continuing until the part is no longer found. Effectively giving me a tree.
-123456
--abc
---yyy
----000000
---zzz
--def
-234567
--ghi
--jkl
The loop I am using initially is this:
Dim topList as range, top as range
Dim lookupList as range, lookup as range
Dim i as integer
Set topList = .sheets("Sheet1").range("A2:A100")
set lookupList = .sheets("Sheet2").Range("A2:A1000")
i = 1
For Each top in topList
For Each lookup in lookupList
If (top = lookup) then
top.offset(0, i).value = lookup.offset(0, 1))
i = i + 1
End If
Next lookup
Next top
I have considered using a while loop inside of this which would re scan the list for the sub part, changing the variable to the new part number each time one is found, and stop running once the part doesn't exist in the list.
I can't come up with a working way to implement this though.
i tried using dictionaries and a recursive function to present the results. you can tweak it a bit to only show the top parts. Currently it shows every item that is in column A. Column C is the output.
The idea is that i am looping through the column A and i create a dictionary for each part and has entries in the dictionary the sub parts.
When i present the results if an entry in the dictionary is also an entry in my top level dictionary i present it again.
Public Sub sFindParts()
Dim topPartDict As New Dictionary, subPartDict As Dictionary, d As Dictionary
Dim topPartList As Range, part As Range
Dim outputLocation As Range
Dim i As Integer, indLvl As Integer
Dim k As Variant, p As Variant
Set outputLocation = Sheet2.Range("C1")
Set topPartList = Sheet2.Range("A2:A8")
For Each part In topPartList
If Not topPartDict.Exists(part.Value) Then
Set d = New Dictionary
d.Add Key:=part.Offset(0, 1).Value, item:=part.Offset(0, 1).Value
topPartDict.Add Key:=part.Value, item:=d
Set topPartDict(part.Value) = d
Else
Set d = topPartDict(part.Value)
d.Add Key:=part.Offset(0, 1).Value, item:=part.Offset(0, 1).Value
Set topPartDict(part.Value) = d
End If
Next part
indLvl = fPresentParts(outputLocation, topPartDict, topPartDict, 0)
End Sub
Private Function fPresentParts(ByRef location As Range, ByRef tpd As Dictionary, ByRef d As Dictionary, indLvl As Integer) As Integer
Dim k As Variant, v As Variant
Dim subPartsDict As Dictionary
For Each k In d.Keys()
If TypeOf d(k) Is Dictionary Then
Set v = d(k)
location.IndentLevel = indLvl
location.Value = k
Set location = location.Offset(1, 0)
indLvl = indLvl + 1
Set subPartsDict = v
indLvl = fPresentParts(location, tpd, subPartsDict, indLvl)
Else
If tpd.Exists(d(k)) And TypeOf tpd(d(k)) Is Dictionary Then
location.IndentLevel = indLvl
location.Value = d(k)
Set location = location.Offset(1, 0)
indLvl = indLvl + 1
indLvl = fPresentParts(location, tpd, tpd(d(k)), indLvl)
Else
location.IndentLevel = indLvl
location.Value = k
Set location = location.Offset(1, 0)
End If
End If
Next k
indLvl = indLvl - 1
fPresentParts = indLvl
End Function
I suggest looping through your list of Top Level Part and Sub Part and use the WorksheetFunction.Match Method to backwards trace the path of each entry.
Outgoing from this list Worksheets("List"):
It will return Worksheets("Output"):
Which only needs to be sorted by columns A B C and D to get the tree view character.
Option Explicit
Public Sub FindPathway()
Dim wsList As Worksheet
Set wsList = ThisWorkbook.Worksheets("List")
Dim wsOutput As Worksheet
Set wsOutput = ThisWorkbook.Worksheets("Output")
Dim LastRow As Long
LastRow = wsList.Cells(wsList.Rows.Count, "A").End(xlUp).Row
Dim OutputRow As Long, oCol As Long
OutputRow = 2
Dim PathCol As Collection
Dim FoundRow As Long
Dim iRow As Long, cRow As Long
For iRow = 2 To LastRow
cRow = iRow
Set PathCol = New Collection
PathCol.Add wsList.Cells(cRow, "B").Value
Do 'loop until a root item is found
FoundRow = 0
On Error Resume Next
FoundRow = WorksheetFunction.Match(wsList.Cells(cRow, "A"), wsList.Columns("B"), 0)
On Error GoTo 0
If FoundRow = 0 Then
'is a root
PathCol.Add wsList.Cells(cRow, "A").Value
For oCol = 0 To PathCol.Count - 1 'output all remembered items
wsOutput.Cells(OutputRow, oCol + 1).Value = PathCol.Item(PathCol.Count - oCol)
Next oCol
OutputRow = OutputRow + 1
Else
'is a child
PathCol.Add wsList.Cells(cRow, "A").Value 'remember item
cRow = FoundRow 'go for the next child item
End If
DoEvents 'prevent unresponsive Excel
Loop Until FoundRow = 0
Next iRow
End Sub
Note that this method is very basic and not the fastest, because it doesn't recognize already traced paths, instead it always does a full trace for every item.
Throwing my hat in the ring. The tgr sub can be customized for where to look for the data and where to output the results. It will also keep track of what is actually top level and only perform the recursive search for those items and their sub parts. The recursive search function is FindAllSubParts
Sub tgr()
Const sDataSheet As String = "Sheet2"
Const sResultSheet As String = "Sheet1"
Const sTopPartsCol As String = "A"
Const sSubPartsCol As String = "B"
Dim wb As Workbook
Dim wsData As Worksheet
Dim wsDest As Worksheet
Dim rTopParts As Range
Dim rSubParts As Range
Dim TopPartCell As Range
Dim rTest As Range
Dim hTopParts As Object
Set wb = ActiveWorkbook
Set wsData = wb.Sheets(sDataSheet)
Set wsDest = wb.Sheets(sResultSheet)
Set rTopParts = wsData.Range(sTopPartsCol & "2", wsData.Cells(wsData.Rows.Count, sTopPartsCol).End(xlUp))
Set rSubParts = Intersect(rTopParts.EntireRow, wsData.Columns(sSubPartsCol))
Set hTopParts = CreateObject("Scripting.Dictionary")
For Each TopPartCell In rTopParts.Cells
Set rTest = Nothing
Set rTest = rSubParts.Find(TopPartCell.Text, rSubParts.Cells(rSubParts.Cells.Count), xlValues, xlWhole, , xlNext, False)
If rTest Is Nothing And Not hTopParts.Exists(TopPartCell.Text) Then
hTopParts.Add TopPartCell.Text, TopPartCell.Text
wsDest.Cells(wsDest.Rows.Count, "A").End(xlUp).Offset(1).Value = TopPartCell.Text
FindAllSubParts TopPartCell.Text, 1, rTopParts, rSubParts, wsDest, sTopPartsCol
End If
Next TopPartCell
End Sub
Sub FindAllSubParts(ByVal arg_sTopPart As String, _
ByVal arg_lSubIndex As Long, _
ByVal arg_rTopParts As Range, _
ByVal arg_rSubParts As Range, _
ByVal arg_wsDest As Worksheet, _
ByVal arg_sTopPartsCol As String)
Dim rFound As Range
Dim sFirst As String
Dim sSubPart As String
Set rFound = arg_rTopParts.Find(arg_sTopPart, arg_rTopParts.Cells(arg_rTopParts.Cells.Count), xlValues, xlWhole, , xlNext, False)
If Not rFound Is Nothing Then
sFirst = rFound.Address
Do
sSubPart = arg_rSubParts.Parent.Cells(rFound.Row, arg_rSubParts.Column).Text
arg_wsDest.Cells(arg_wsDest.Rows.Count, arg_sTopPartsCol).End(xlUp).Offset(1).Value = String(arg_lSubIndex, "-") & sSubPart
FindAllSubParts sSubPart, arg_lSubIndex + 1, arg_rTopParts, arg_rSubParts, arg_wsDest, arg_sTopPartsCol
Set rFound = arg_rTopParts.Find(arg_sTopPart, rFound, xlValues, xlWhole, , xlNext, False)
Loop While rFound.Address <> sFirst
End If
End Sub

Excel VBA For each nested loop

I am trying to build an Excel workbook that takes data from one sheet, and fills out another based on a system name. I am having a problem with the first for next loop. It works for the first system, but if there are more than one item in the system it just stops working. The second for each loop works great. Is there a better way to run my first loop. I try an if the first For each variable matches the second for each then increment, but the code says the next Inspectcell does not have a for each. The system name is always in column C and starts at C7
Sub fillthereport()
Dim xx As Variant, ws As Variant, yy As Variant
Dim ws2 As Variant, xxx As Variant, yyy As Variant
Dim rowed As Integer, b As Integer
'Dim MySht As Worksheet
Dim MyPic As Shape
Dim MyLeft As Single, MyTop As Single
Dim conv As Variant
Dim item As Variant
Dim picnum As Variant
Dim mergecells As String, mergecells2 As String, mergecolor As String
Dim horstart As Variant, horend As Variant
Dim verstart As Variant, verend As Variant
Dim Inspectcell As Range, reportcell As Range
'worksheets loop operator
ws = 1
'worksheets loop operator to
ws2 = 6
'row designator from
xx = 7
'column designator from
yy = 3
'row designator to
xxx = 68
'column designator to
yyy = 37
'This is not the variable you are looking for
b = 0
'These are the variables you are looking for
yel = 0
bl = 0
re = 0
Folderpath = Application.ActiveWorkbook.Path
Application.ScreenUpdating = False
reportcell = Worksheets("inspection Data").Range("C7")
'make extra sheets in the report to be filled
For Each Inspectcell In Worksheets("inspection Data").Range("C7:C18")
If Inspectcell = reportcell Then
Worksheets(ws2).Select
Worksheets(ws2).Range("A60:AY114").Select
Selection.Copy
Sheets(ws2).Range("A115:AY169").Select
ActiveSheet.Paste
Sheets(ws2).Range("A170:AY224").Select
ActiveSheet.Paste
'reportcell = Inspectcell
For Each reportcell In Worksheets("inspection Data").Range("C7:C18")
If reportcell = Inspectcell Then
'(This is about 110 lines of code that work great)
xx = xx + 1
b = b + 1
'worksheets loop operator
'ws = ws + 1
'worksheets loop operator to
'ws2 = ws2 + 1
'column designator from
'yy = yy + 1
'row designator to
If Not b Mod 3 = 0 Then
xxx = xxx + 16
Else
xxx = xxx + 23
End If
Else 'If xx = 15 Then
Exit For
End If
'xxx = xxx + 22
Next reportcell
ws2 = ws2 + 1
'Else
'Exit for
End if
Next Inspectcell

How to copy multiple times repeating cells?

I have a table
Name ID Salary Educ Exp Salary Educ Exp
Mike 1 100 5 12 200 12 23
Peter 2 200 6 12 300 3 32
Lily 3 150 3 13 200 5 2
...................
I need to transform this table into
Name ID Salary Educ Exp
Mike 1 100 5 12
Peter 2 200 6 12
Lily 3 150 3 13
Mike 1 200 12 23
Peter 2 300 3 32
Lily 3 200 5 2
..................
How can I do this using VBA ?
Here is what I tried so far
Sub test()
Dim rg1 As Range, rg2 As Range, rg3 As Range, shtDest As Worksheet
Dim lLoop As Long, lRowDest As Long
Set rg1 = Selection.Areas(1)
Set rg2 = Selection.Areas(2)
Set rg3 = Selection.Areas(3)
Set shtDest = Worksheets.Add
lRowDest = 1
For lLoop = 1 To rg1.Rows.Count
lRowDest = lRowDest + rg2.Rows.Count + rg3.Rows.Count
Next
End Sub
After looking at the comments, this will move N sets of data into a single set of columns. This assumes that each row contains data for one Name/ID combination, as in your example.
Sub moveData()
Dim x As Range
Dim data As Range
Dim i As Long
Dim origId As Range
Dim id As Range
Dim idColCount As Long
Dim setCount As Long
Dim setCol As Long
Dim headerRange As Range
Set headerRange = Range("1:1")
Set id = Range(Range("A2"), Range("B2").End(xlDown))
Set origId = id
idColCount = id.Columns.Count
setCount = Application.WorksheetFunction.CountIfs(headerRange, "salary")
setCol = 1
For i = 1 To setCount
With headerRange
Set x = .Find("Salary", .Cells(1, setCol))
Set data = x.Offset(1).Resize(x.End(xlDown).Row - x.Row, 3)
data.Copy
id.Cells(1, 1).Offset(id.rows.Count, idColCount).PasteSpecial xlPasteAll
origId.Copy
id.Cells(1, 1).Offset(id.rows.Count).PasteSpecial xlPasteAll
Set id = Range(id, id.End(xlDown))
End With
setCol = x.Column
Next i
setCol = 1
With headerRange
Set x = .Find("Salary", .Cells(1, setCol))
setCol = x.Column
Set x = .Find("Salary", .Cells(1, setCol))
End With
Range(x, x.End(xlToRight).End(xlDown)).Clear
End Sub
See if this works for you, it loops through each row finding each Salary/Educ/Exp entry until it doesn't find another, moving each one to the bottom with the corresponding Name/ID and cleans up everything nicely for you.
Private Sub SplitTable()
Dim rng As Range '' range we want to iterate through
Dim c As Range '' iterator object
Dim cc As Range '' check cell
Dim lc As Range '' last cell
Dim ws As Worksheet
Dim keepLooking As Boolean '' loop object
Dim firstTime As Boolean
Dim offset As Integer
Dim Name As String, ID As Integer, Salary As Integer, Educ As Integer, Exp As Integer
Set ws = ActiveSheet '' adjust this to the sheet you want or leave it as ActiveSheet
Set rng = ws.Range("A2", "A" & ws.Range("A" & ws.Rows.Count).End(xlUp).Row)
For Each c In rng
firstTime = True '' reset to true so we get an offset of five for the first entry
keepLooking = True
While keepLooking
If firstTime Then
Set cc = c.offset(, 5)
Else: Set cc = cc.offset(, 3)
End If
If cc <> "" Then '' if the salary has data in it, then grab what we expect to be Salaray/Educ/Exp
Name = c.Value
ID = c.offset(, 1).Value
Salary = cc.Value
Educ = cc.offset(, 1).Value
Exp = cc.offset(, 2).Value
'' Cleanup
cc.ClearContents
cc.offset(, 1).ClearContents
cc.offset(, 2).ClearContents
'' Move it to the bottom of columns A:E
Set lc = ws.Range("A" & ws.Rows.Count).End(xlUp).offset(1, 0)
lc.Value = Name
lc.offset(, 1).Value = ID
lc.offset(, 2).Value = Salary
lc.offset(, 3).Value = Educ
lc.offset(, 4).Value = Exp
Else: keepLooking = False
End If
firstTime = False '' set to false so we only get an offset of 3 from here on out
Wend
Next c
ws.Range("F1", ws.Range("A1").End(xlToRight)).ClearContents
End Sub

Resources