英文:
Is it possible to create nested dictionaries from this sample data?
问题
I understand your request, and here's the translated content:
我熟悉集合和数组,但现在我正在尝试扩展到字典。我有一个工作的Excel VBA应用程序,用于创建和存储建筑付款申请。这个应用程序因为我自己的原因而显得笨重且编码质量不佳。自从我编写这个程序以来,我学到了很多东西,现在是时候进行性能和结构方面的改进了。
与处理原始数据的一些挑战包括...
- 创建新的付款申请
- 新的付款申请需要来自先前申请的信息
- 编辑现有付款申请
- 存档旧的付款申请
数据位于SharePoint上的Excel文件中,前端Excel文件在不同用户之间共享。
我觉得字典可能会极大地简化我的代码,但不确定我所寻求的是否可能。如果可能的话,我只需要一个指导方向的提示。如果不行,您是否有建议如何最好地解决这个问题?我可以控制数据,因此如果需要的话,我可以将其分成单独的表格。我宁愿避免在后端使用Access或SQL Server。
我的示例数据如下:
项目号 | 申请号 | 项目条目号 | 描述 | 金额 |
---|---|---|---|---|
23-001 | 1 | 1 | 文本1 | 金额1 |
23-001 | 1 | 2 | 文本2 | 金额2 |
23-001 | 2 | 1 | 文本1 | 金额1 |
23-001 | 2 | 2 | 文本2 | 金额2 |
23-002 | 1 | 1 | 文本1 | 金额1 |
23-002 | 1 | 2 | 文本2 | 金额2 |
23-002 | 2 | 1 | 文本1 | 金额1 |
23-002 | 2 | 2 | 文本2 | 金额2 |
希望这个示例数据足以传达模式。我的实际数据在项目号之后有几千行,以及11列的内容。
当字典放入观察器并展开时,我希望看到如下所示的字典结构:
23-001
1
1
描述
金额
2
描述
金额
23-002
1
1
描述
金额
2
描述
金额
就像透视表的输出一样...
英文:
I'm familiar with Collections and arrays, but now I'm trying to expand into dictionaries. I have a working excel VBA app that creates and stores construction pay applications. It's clunky and poorly coded thanks to yours truly. I've learned much since I wrote this program, and it's time to make needed performance and structural improvements.
Some of the challenges of working with the raw data is...
Creating new pay applications
New pay applications need information from previous application
Editing existing pay applications
Archiving old pay applications
The data is located in excel file on SharePoint and front end excel file is shared among different users.
I feel dictionaries could greatly simplify my code, but not sure if what I'm looking to do is possible. If it is possible, I really only need a nudge in the right direction. If not, do you have a suggestion of the best way to tackle this? I control the data, so I could break it up into separate tables if needed. I'd prefer to stay away from Access or SQL Server on the back end.
My sample data looks like this:
Project # | Application # | Item # | Description | Value |
---|---|---|---|---|
23-001 | 1 | 1 | TEXT1 | CURRENCY1 |
23-001 | 1 | 2 | TEXT2 | CURRENCY2 |
23-001 | 2 | 1 | TEXT1 | CURRENCY1 |
23-001 | 2 | 2 | TEXT2 | CURRENCY2 |
23-002 | 1 | 1 | TEXT1 | CURRENCY1 |
23-002 | 1 | 2 | TEXT2 | CURRENCY2 |
23-002 | 2 | 1 | TEXT1 | CURRENCY1 |
23-002 | 2 | 2 | TEXT2 | CURRENCY2 |
Hopefully this sample data is enough to communicate the pattern. My actual data is a couple thousand rows and 11 columns after Item #.
I'd like to see the dictionary look like this when the dictionary is put into a watch and expanded:
23-001
1
1
Description
Value
2
Description
Value
23-002
1
1
Description
Value
2
Description
Value
Looks just like the output of a pivot table...
答案1
得分: 3
以下是您提供的代码的翻译部分:
不需要访问或使用SQL服务器就可以在Excel数据上使用SQL。以下代码将使用ADO连接到工作簿,并将Sheet1
中的数据作为输入。之后,您可以使用SQL来处理数据,甚至可以使用SQL语句更新数据。
Option Explicit
Sub AdoTest_UpdateExcelFile()
Dim xlFile As String
xlFile = ThisWorkbook.FullName
' 您需要在引用中添加Active Data Objects
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim fld As Field
Dim sSQL As String
With cn
.Provider = "Microsoft.ACE.OLEDB.16.0"
.ConnectionString = "Data Source=" & xlFile & "; Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
.Open
End With
sSQL = "SELECT * FROM [Sheet1$]"
rs.Open sSQL, cn
rs.MoveFirst
Worksheets.Add
Dim i As Long
For i = 0 To rs.Fields.Count - 1
Cells(1, i + 1).Value = rs.Fields(i).Name
Next i
Range("A2").CopyFromRecordset rs
rs.Close
Sheet1.Activate
' 也可以更新数据
cn.Execute "Update [Sheet1$] SET Project='23-100' WHERE Project ='23-001';"
' 在以下语句中,我省略了列'value',因为它与SQL关键字VALUE冲突
' 不过,您可以绕过这个问题
cn.Execute "Insert Into [Sheet1$] (Project, Application, Item, Description) Values('23-003', 3, 1, 'TEXT4')"
End Sub
更多阅读材料
英文:
You do not need to have Access or use an SQL server in order to use SQL on Excel data. The following code will use ADO, connect to the workbook and take the data from Sheet1
as input. Afterwards you can use SQL to work with the data, you can even update your data with SQL statements
Option Explicit
Sub AdoTest_UpdateExcelFile()
Dim xlFile As String
xlFile = ThisWorkbook.FullName
' You have to add Active Data Objects to the referencss
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim fld As Field
Dim sSQL As String
With cn
.Provider = "Microsoft.ACE.OLEDB.16.0"
.ConnectionString = "Data Source=" & xlFile & "; Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
.Open
End With
sSQL = "SELECT * FROM [Sheet1$]"
rs.Open sSQL, cn
rs.MoveFirst
Worksheets.Add
Dim i As Long
For i = 0 To rs.Fields.Count - 1
Cells(1, i + 1).value = rs.Fields(i).Name
Next i
Range("A2").CopyFromRecordset rs
rs.Close
Sheet1.Activate
' One can also update data
cn.Execute "Update [Sheet1$] SET Project='23-100' WHERE Project ='23-001' ;"
' In the following statement I left out the column 'value' because that is in conflict woth the SQL Key word VALUE
' You can circumvent that though
cn.Execute "Insert Into [Sheet1$] (Project,Application ,Item,Description) Values('23-003',3,1,'TEXT4')"
End Sub
Further reading
答案2
得分: 2
Dictionaries, Collections, and Classes
- 外部字典的键保存了 "Projects",而每个相关联的项保存了另一个内部字典。每个内部字典的键保存了 "Application",而每个相关联的项保存了包含3(11)个属性,如 "Item"、"Description"、"Currency" 等的属性集合。
- 顺便说一下,VBE除非使用数组,否则永远不会显示所需的结构。您需要想象它。
在标准模块中的示例,例如 Module1
Option Explicit
Sub DictDataTEST()
Dim dict As Object: Set dict = DictData
Dim Prop As cProps, pKey, aKey
For Each pKey In dict.Keys
Debug.Print "Product: " & pKey
For Each aKey In dict(pKey)
Debug.Print "Application: " & aKey
Debug.Print "Properties:"
For Each Prop In dict(pKey)(aKey)
Debug.Print Prop.pId, Prop.pDescription, Prop.pCurrency
Next Prop
Next aKey
Next pKey
Debug.Print "1st Properties of 2nd Application of 1st Project (""23-001""):"
Set Prop = dict("23-001")(2)(1)
Debug.Print Prop.pId, Prop.pDescription, Prop.pCurrency
Debug.Print "2nd Properties of 1st Application of 2nd Project (""23-002""):"
Set Prop = dict("23-002")(1)(2)
Debug.Print Prop.pId, Prop.pDescription, Prop.pCurrency
End Sub
标准模块中的函数,例如 Module1
Function DictData() As Object
Dim wb As Workbook: Set wb = ThisWorkbook ' 包含此代码的工作簿
Dim ws As Worksheet: Set ws = wb.Sheets("Sheet1")
Dim rg As Range, rCount As Long
With ws.Range("A1").CurrentRegion
rCount = .Rows.Count - 1 ' 排除标题
Set rg = .Resize(rCount).Offset(1)
End With
Dim Data(): Data = rg.Value
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim Prop As cProps, r As Long, Project As String, App As Long
For r = 1 To rCount
Project = CStr(Data(r, 1))
If Not dict.Exists(Project) Then
Set dict(Project) = CreateObject("Scripting.Dictionary")
End If
App = Data(r, 2)
If Not dict(Project).Exists(App) Then
Set dict(Project)(App) = New Collection
End If
Set Prop = New cProps
Prop.pId = Data(r, 3)
Prop.pDescription = Data(r, 4)
Prop.pCurrency = Data(r, 5)
dict(Project)(App).Add Prop
Next r
Set DictData = dict
End Function
类模块 cProps
Option Explicit
Private m_pId As Long
Private m_pDescription As String
Private m_pCurrency As String
'
Public Property Get pId() As Long
pId = m_pId
End Property
Property Let pId(pId As Long)
m_pId = pId
End Property
Public Property Get pDescription() As String
pDescription = m_pDescription
End Property
Property Let pDescription(pDescription As String)
m_pDescription = pDescription
End Property
Public Property Get pCurrency() As String
pCurrency = m_pCurrency
End Property
Property Let pCurrency(pCurrency As String)
m_pCurrency = pCurrency
End Property
结果
Product: 23-001
Application: 1
Properties:
1 TEXT1 Currency1
2 TEXT2 Currency2
Application: 2
Properties:
1 TEXT3 Currency3
2 TEXT4 Currency4
Product: 23-002
Application: 1
Properties:
1 TEXT5 Currency5
2 TEXT6 Currency6
Application: 2
Properties:
1 TEXT7 Currency7
2 TEXT8 Currency8
1st Properties of 2nd Application of 1st Project ("23-001"):
1 TEXT3 Currency3
2nd Properties of 1st Application of 2nd Project ("23-002"):
2 TEXT6 Currency6
英文:
Dictionaries, Collections, and Classes
- The outer dictionary's keys hold the
Projects
while each associated item holds another inner dictionary. Each inner dictionary's key holds theApplication
while each associated item holds a collection of theProperty
objects containing the 3 (11) properties likeItem
,Description
,Currency
... etc. - BTW, VBE will never show the structure required unless you use arrays. You will have to imagine it.
An Example in a Standard Module e.g. Module1
<!-- language: lang-vb -->
Option Explicit
Sub DictDataTEST()
Dim dict As Object: Set dict = DictData
Dim Prop As cProps, pKey, aKey
For Each pKey In dict.Keys
Debug.Print "Product: " & pKey
For Each aKey In dict(pKey)
Debug.Print "Application: " & aKey
Debug.Print "Properties:"
For Each Prop In dict(pKey)(aKey)
Debug.Print Prop.pId, Prop.pDescription, Prop.pCurrency
Next Prop
Next aKey
Next pKey
Debug.Print "1st Properties of 2nd Application of 1st Project (""23-001""):"
Set Prop = dict("23-001")(2)(1)
Debug.Print Prop.pId, Prop.pDescription, Prop.pCurrency
Debug.Print "2nd Properties of 1st Application of 2nd Project (""23-002""):"
Set Prop = dict("23-002")(1)(2)
Debug.Print Prop.pId, Prop.pDescription, Prop.pCurrency
End Sub
The Function in a Standard Module e.g. Module1
Function DictData() As Object
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Sheets("Sheet1")
Dim rg As Range, rCount As Long
With ws.Range("A1").CurrentRegion
rCount = .Rows.Count - 1 ' exclude headers
Set rg = .Resize(rCount).Offset(1)
End With
Dim Data(): Data = rg.Value
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim Prop As cProps, r As Long, Project As String, App As Long
For r = 1 To rCount
Project = CStr(Data(r, 1))
If Not dict.Exists(Project) Then
Set dict(Project) = CreateObject("Scripting.Dictionary")
End If
App = Data(r, 2)
If Not dict(Project).Exists(App) Then
Set dict(Project)(App) = New Collection
End If
Set Prop = New cProps
Prop.pId = Data(r, 3)
Prop.pDescription = Data(r, 4)
Prop.pCurrency = Data(r, 5)
dict(Project)(App).Add Prop
Next r
Set DictData = dict
End Function
Class Module cProps
<!-- language: lang-vb -->
Option Explicit
Private m_pId As Long
Private m_pDescription As String
Private m_pCurrency As String
'
Public Property Get pId() As Long
pId = m_pId
End Property
Property Let pId(pId As Long)
m_pId = pId
End Property
Public Property Get pDescription() As String
pDescription = m_pDescription
End Property
Property Let pDescription(pDescription As String)
m_pDescription = pDescription
End Property
Public Property Get pCurrency() As String
pCurrency = m_pCurrency
End Property
Property Let pCurrency(pCurrency As String)
m_pCurrency = pCurrency
End Property
The Result
Product: 23-001
Application: 1
Properties:
1 TEXT1 Currency1
2 TEXT2 Currency2
Application: 2
Properties:
1 TEXT3 Currency3
2 TEXT4 Currency4
Product: 23-002
Application: 1
Properties:
1 TEXT5 Currency5
2 TEXT6 Currency6
Application: 2
Properties:
1 TEXT7 Currency7
2 TEXT8 Currency8
1st Properties of 2nd Application of 1st Project ("23-001"):
1 TEXT3 Currency3
2nd Properties of 1st Application of 2nd Project ("23-002"):
2 TEXT6 Currency6
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论