可以从这个示例数据中创建嵌套字典吗?

huangapple go评论74阅读模式
英文:

Is it possible to create nested dictionaries from this sample data?

问题

I understand your request, and here's the translated content:

我熟悉集合和数组,但现在我正在尝试扩展到字典。我有一个工作的Excel VBA应用程序,用于创建和存储建筑付款申请。这个应用程序因为我自己的原因而显得笨重且编码质量不佳。自从我编写这个程序以来,我学到了很多东西,现在是时候进行性能和结构方面的改进了。

与处理原始数据的一些挑战包括...

  1. 创建新的付款申请
  2. 新的付款申请需要来自先前申请的信息
  3. 编辑现有付款申请
  4. 存档旧的付款申请

数据位于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

更多阅读材料

在VBA中使用ADO

英文:

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

Using ADO with VBA

答案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 the Application while each associated item holds a collection of the Property objects containing the 3 (11) properties like Item, 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 &quot;Product: &quot; &amp; pKey
        For Each aKey In dict(pKey)
            Debug.Print &quot;Application: &quot; &amp; aKey
            Debug.Print &quot;Properties:&quot;
            For Each Prop In dict(pKey)(aKey)
                Debug.Print Prop.pId, Prop.pDescription, Prop.pCurrency
            Next Prop
        Next aKey
    Next pKey

    Debug.Print &quot;1st Properties of 2nd Application of 1st Project (&quot;&quot;23-001&quot;&quot;):&quot;
    
    Set Prop = dict(&quot;23-001&quot;)(2)(1)
    Debug.Print Prop.pId, Prop.pDescription, Prop.pCurrency

    Debug.Print &quot;2nd Properties of 1st Application of 2nd Project (&quot;&quot;23-002&quot;&quot;):&quot;
    
    Set Prop = dict(&quot;23-002&quot;)(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 &#39; workbook containing this code
    Dim ws As Worksheet: Set ws = wb.Sheets(&quot;Sheet1&quot;)
    
    Dim rg As Range, rCount As Long
    
    With ws.Range(&quot;A1&quot;).CurrentRegion
        rCount = .Rows.Count - 1 &#39; exclude headers
        Set rg = .Resize(rCount).Offset(1)
    End With
    
    Dim Data(): Data = rg.Value
    
    Dim dict As Object: Set dict = CreateObject(&quot;Scripting.Dictionary&quot;)
    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(&quot;Scripting.Dictionary&quot;)
        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
&#39;
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 (&quot;23-001&quot;):
 1            TEXT3         Currency3
2nd Properties of 1st Application of 2nd Project (&quot;23-002&quot;):
 2            TEXT6         Currency6

huangapple
  • 本文由 发表于 2023年5月7日 09:02:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76191818.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定