此代码需要添加对 Microsoft Scripting Runtime 库(用于字典)的引用。我将这段代码建立在几个假设之上:
此代码不是直接将信息从一张纸移动到另一张纸,而是将数据收集到字典中;然后将该数据提取回最终工作表。这也将数据从 Sheet1 列 B 获取到 Sheet2 列 D、E、F
Sub search_and_extract()
Dim datasheet As Worksheet
Dim reportsheet As Worksheet
Dim SearchString As String
Dim i As Integer
Dim j As Integer
Set datasheet = Sheet1
Set reportsheet = Sheet2
Dim chNum As String
Dim rptNum As String
Dim ChangeNumbers As New Dictionary
Dim dictKey1 As Variant
Dim dictKey2 As Variant
reportsheet.Range("A1:H200").ClearContents
finalrow = datasheet.Cells(datasheet.Rows.Count, 1).End(xlUp).Row
For i = 1 To finalrow
SearchString = datasheet.Range("A" & i)
If InStr(1, SearchString, "Change Number") Then
chNum = datasheet.Cells(i, 1)
ChangeNumbers.Add chNum, New Dictionary 'For report numbers
ElseIf InStr(1, SearchString, "Report-") Then
rptNum = datasheet.Cells(i, 1)
ChangeNumbers.Item(chNum).Add rptNum, New Dictionary 'For details
For j = 0 To 2
ChangeNumbers.Item(chNum).Item(rptNum).Add j, datasheet.Cells(i, 1).Offset(j, 1) ' the details
Next j
End If
Next i
i = 1
For Each dictKey1 In ChangeNumbers.Keys
reportsheet.Cells(i, 1) = dictKey1
If ChangeNumbers.Item(dictKey1).Count > 0 Then
For Each dictKey2 In ChangeNumbers.Item(dictKey1).Keys
reportsheet.Cells(i, 2) = dictKey2
For j = 0 To 2
reportsheet.Cells(i, 4 + j) = ChangeNumbers.Item(dictKey1).Item(dictKey2).Item(j)
Next j
i = i + 1 'moves to new row for new report (or next change number
Next dictKey2
Else
i = i + 1 'no reports, so moves down to prevent overwriting change number
End If
Next dictKey1
End Sub
编辑:
如果需要,包括更改主题的示例。这假设(除上述之外):
- 变更主题始终在相关报告之前
- 不会有任何报告而不更改主题
- 更改主题将进入 C 列。(例如,可以通过将
reportsheet.Cells(i, 3) 更改为 reportsheet.Cells(i, 7) 将其编辑到 G 列)
细节循环部分也进行了一些更改,以适应不断变化的细节数量。此代码的结构使得每个详细信息类型将始终放在同一列中(即需求列、开发列等)
详细循环部分的主要更改来自以下内容:
For j = 0 To 2
ChangeNumbers.Item(chNum).Item(chSub).Item(rptNum).Add j, datasheet.Cells(i, 1).Offset(j, 1) ' the details
Next j
对此(仅包括两种示例类型的详细信息。另请注意,目前,目标列号是硬编码的——最好为所需的列号制作常量,以使代码更具可读性——能够且更易于维护。):
j = 0
Do While IsEmpty(datasheet.Cells(i + j, 1)) Or datasheet.Cells(i + j, 1) = rptNum
If InStr(1, datasheet.Cells(i + j, 2), "Specified") Then
' The 4 after ".Add" is the column number for this detail in sheet2
ChangeNumbers.Item(chNum).Item(chSub).Item(rptNum).Add 4, datasheet.Cells(i + j, 2) ' the details
ElseIf InStr(1, datasheet.Cells(i + j, 2), "Total Workload") Then
' The 5 after ".Add" is the column number for this detail in sheet2
ChangeNumbers.Item(chNum).Item(chSub).Item(rptNum).Add 5, datasheet.Cells(i + j, 2) ' the details
End If
j = j + 1
Loop
由此而来:
For j = 0 To 2
reportsheet.Cells(i, 4 + j) = ChangeNumbers.Item(dictKey1).Item(dictKey2).Item(dictKey3).Item(j)
Next j
对此(请注意所需的附加变量):
Dim dictKey4
For each dictKey4 In ChangeNumbers.Item(dictKey1).Item(dictKey2).Item(dictKey3).Keys
reportsheet.Cells(i, dictKey4) = ChangeNumbers.Item(dictKey1).Item(dictKey2).Item(dictKey3).Item(dictKey4)
Next dictKey4
Sub search_and_extract()
Dim datasheet As Worksheet
Dim reportsheet As Worksheet
Dim SearchString As String
Dim i As Integer
Dim j As Integer
Set datasheet = Sheet1
Set reportsheet = Sheet2
Dim chNum As String
Dim chSub as String
Dim rptNum As String
Dim ChangeNumbers As New Dictionary
Dim dictKey1 As Variant
Dim dictKey2 As Variant
Dim dictKey3 As Variant
Dim dictKey4 As Variant
reportsheet.Range("A1:H200").ClearContents
finalrow = datasheet.Cells(datasheet.Rows.Count, 1).End(xlUp).Row
For i = 1 To finalrow
SearchString = datasheet.Range("A" & i)
If InStr(1, SearchString, "Change Number") Then
chNum = datasheet.Cells(i, 1)
ChangeNumbers.Add chNum, New Dictionary 'For report numbers
ElseIf InStr(1, SearchString, "Change Subject") Then
chSub = datasheet.Cells(i, 1)
ChangeNumbers.Item(chNum).Add chSub, New Dictionary 'For report numbers
ElseIf InStr(1, SearchString, "Report-") Then
rptNum = datasheet.Cells(i, 1)
ChangeNumbers.Item(chNum).Item(chSub).Add rptNum, New Dictionary 'For details
j = 0
'Verifies that the details belong to the current report
'String checks are included after locating a report to maintain a connection between the report and its details
Do While IsEmpty(datasheet.Cells(i + j, 1)) Or datasheet.Cells(i + j, 1) = rptNum
If InStr(1, datasheet.Cells(i + j, 2), "Specified") Then
' The 4 after ".Add" is the column number for this detail in sheet2
ChangeNumbers.Item(chNum).Item(chSub).Item(rptNum).Add 4, datasheet.Cells(i + j, 2) ' the details
ElseIf InStr(1, datasheet.Cells(i + j, 2), "Total Workload") Then
' The 5 after ".Add" is the column number for this detail in sheet2
ChangeNumbers.Item(chNum).Item(chSub).Item(rptNum).Add 5, datasheet.Cells(i + j, 2) ' the details
End If
j = j + 1
Loop
End If
Next i
i = 1
For Each dictKey1 In ChangeNumbers.Keys
reportsheet.Cells(i, 1) = dictKey1 'Change Number
If ChangeNumbers.Item(dictKey1).Count > 0 Then
For Each dictKey2 In ChangeNumbers.Item(dictKey1).Keys
reportsheet.Cells(i, 3) = dictKey2 'Change Subject; assuming in column C on same row as Change Number
If ChangeNumbers.Item(dictKey1).Item(dictKey2).Count > 0 Then
For Each dictKey3 In ChangeNumbers.Item(dictKey1).Item(dictKey2).Keys 'Report Number
reportsheet.Cells(i, 2) = dictKey3
'reportsheet.Cells(i, 3) = dictKey2 'Uncomment if you want change subject in every row w/ matching report
For each dictKey4 In ChangeNumbers.Item(dictKey1).Item(dictKey2).Item(dictKey3).Keys
reportsheet.Cells(i, dictKey4) = ChangeNumbers.Item(dictKey1).Item(dictKey2).Item(dictKey3).Item(dictKey4)
Next dictKey4
i = i + 1 'moves to new row for new report (or next change number
Next dictKey3
Else
i = i + 1 'no reports, so moves down to prevent overwriting change number
End If
Next dictKey2
Else
i = i + 1 'no change subject, so moves down to prevent overwriting change number
End If
Next dictKey1
End Sub