【问题标题】:Converting weekly data in a table to monthly data using VBA使用 VBA 将表中的每周数据转换为每月数据
【发布时间】:2021-10-05 11:32:56
【问题描述】:

我有一个小时表与周(一周的开始是星期日)。每周数据最多可达 12-16 个月,具体取决于用户输入。我想创建一个 VBA 宏,它将遍历这个每周小时数据表并将列转换为每月数据。

示例: 所有 2021 年 10 月相关的列都将合并为 1 列,称为 Oct-21。这也将结合小时数。下图中的第 2 行等于 4+3+4+0=,因此新组合列的第 2 行中的值将是 11。

我目前的想法是计算开始日期和最后日期之间的星期日:

Dim d As Date, format As String, w As Long, FirstSunday As String
format = format(lastMonth, "Medium Date")
d = DateSerial(Year(format), Month(format), 1)
w = Weekday(d, vbSunday)
FirstSunday = d + IIf(w <> 1, 8 - w, 0)

关于如何做到这一点的任何想法?

【问题讨论】:

  • “将列转换为月度数据”究竟是什么意思?根据您的问题,我可以理解您尝试添加特定月份的小时数(在标题中)。如果我的理解有误,请解释“下图中的第 2 行等于 4+3+4+0= 因此值为 11”是什么意思。那么,每一行的含义是什么?你想总结每一行吗?您是否尝试汇总所有行的值?
  • @FaneDuru 基本上我希望将一个月中的所有周合并为 1 列,而不是当前格式,其中 1 列持续 1 周。以 2021 年 10 月为例,前 4 列是周,将合并为 1 列。合并时,这 4 列中的所有小时数将加在一起。这就是我所追求的。
  • 这不涉及任何转换...您应该根据列标题月份汇总所有列内容(我假设标题格式为日期)。那么,你喜欢这样的代码返回吗?从您的图片中,我们无法理解标题在哪一行。需要帮助时,您应该穿上我们的鞋子...
  • @FaneDuru 我解释了它是什么......这是几个小时而不是几周,每一行都是不同的任务。目前的数据是每周。我想要一个宏(VBA)通过组合周数据将当前周表转换为月表。这个问题真的不难理解。。
  • 为什么 10 月 31 日不包括在 10 月?

标签: excel vba


【解决方案1】:

不确定如何将周分组为月,因为某些月将有 5 周。此代码在月份更改时插入一列,然后用相关周列的总和公式填充它。它假设日期在第 1 行,任务编号在第 1 列,第一周在第 2 列。

Option Explicit

Sub ByMonth()

    Dim wb As Workbook, ws As Worksheet
    Dim LastCol As Long, LastRow As Long, c As Long, n As Long
    Dim dt As Date

    Set wb = ThisWorkbook
    Set ws = wb.Sheets("Sheet1")
    
    LastCol = ws.Cells(1, Columns.Count).End(xlToLeft).Column
    LastRow = ws.Cells(Rows.Count, 1).End(xlUp).Row

    ' scan cols from right to left insert new columns
    Application.ScreenUpdating = False
    For c = LastCol + 1 To 3 Step -1
        ' add columns on month change
        If Month(ws.Cells(1, c)) <> Month(ws.Cells(1, c - 1)) Then
             ws.Columns(c).Insert
             With ws.Columns(c)
                .HorizontalAlignment = xlCenter
                '.Interior.Color = RGB(255, 255, 200)
                .Font.Bold = True
                .Cells(1).NumberFormat = "@"
             End With
        End If
    Next

    ' scan left to right filling new cols with sum() formula
    ' hide weekly columns
    LastCol = ws.Cells(1, Columns.Count).End(xlToLeft).Column
    n = 0
    For c = 2 To LastCol + 1
       If ws.Cells(1, c) = "" Then
          dt = ws.Cells(1, c - 1)
          ws.Cells(1, c) = MonthName(Month(dt), True) & "  " & Year(dt)
          ws.Cells(2, c).Resize(LastRow - 1).FormulaR1C1 = "=SUM(RC[-" & n & "]:RC[-1])"
          n = 0
       Else
          ws.Columns(c).EntireColumn.Hidden = True
          n = n + 1
       End If
    Next

    ' copy visible month columns to sheet2
    ws.Cells.SpecialCells(xlCellTypeVisible).Copy
    With wb.Sheets("Sheet2")
        .Activate
        .Range("A1").PasteSpecial xlPasteValues
        .Range("A1").Select
    End With
      
    ' end
    ws.Columns.Hidden = False
    Application.ScreenUpdating = True
    Application.CutCopyMode = False
    MsgBox "Done"

End Sub

【讨论】:

  • 经过一些编辑,这是一个很好的解决方案,非常感谢!
【解决方案2】:

请尝试下一个代码。它假设在 A:A 列中,从第 6 行开始,有(未排序的)任务。如果它们被排序,代码也将毫无问题地运行。它使用数组和字典,主要在内存中工作,对于大范围应该非常快:

Sub SumWeeksMonths()
  Dim sh As Worksheet, sh1 As Worksheet, lastR As Long, arrWk, arrMonths, arrTasks
  Dim i As Long, k As Long, j As Long, El, arr, arrFin, dict As New Scripting.Dictionary
  
  Set sh = ActiveSheet 'use there the sheet to be processed
  Set sh1 = sh.Next    'use here the sheet where the processed result to be returned
  lastR = sh.Range("A" & sh.rows.count).End(xlUp).row 'last row (in column A:A)
  arrWk = sh.Range(sh.Range("B5"), sh.cells(5, sh.Columns.count).End(xlToLeft)).Value 'place the Week headers in a 2D array
  ReDim arrMonths(UBound(arrWk, 2) - 1)'redim the 1D array to keep the unique munths, at a maximum size
  For i = 1 To UBound(arrWk, 2) - 1    'create the array of (only) months:
        If month(DateValue(arrWk(1, i))) <> month(DateValue(arrWk(1, i + 1))) Then
            k = k + 1: arrMonths(k) = Format(DateValue(arrWk(1, i + 1)), "mmm-yyyy")
        Else
            arrMonths(k) = Format(DateValue(arrWk(1, i)), "mmm-yyyy")
        End If
  Next i
  ReDim Preserve arrMonths(k) 'preserve only the existing Date elements
  For Each El In sh.Range("A4:A" & lastR).Value
     dict(El) = 1 'extract the unique tasks (only to count them for ReDim the necessary array)
 Next El
  'place all the range to be processed in an array (for faster iteration):
  arr = sh.Range("A5", sh.cells(lastR, sh.cells(5, sh.Columns.count).End(xlToLeft).Column)).Value
  ReDim arrFin(1 To UBound(dict.Keys) + 1, 1 To UBound(arrMonths) + 2) 'reDim the final array to keep processed data
  ReDim arrTasks(UBound(arrMonths))  'redim the array to temporarily keep the array of each task summ
  dict.RemoveAll: k = 0  'clear the dictionary and reitinialize the K variable
  
  For i = 2 To UBound(arr)              'iterate between the main array elements:
     If Not dict.Exists(arr(i, 1)) Then 'if the Task key does not exist:
        For Each El In arrMonths        'iterate between each month in arrMonths:
            For j = 2 To UBound(arr, 2) 'iterate between all arr columns for the i row:
                If month(DateValue(arr(1, j))) = month(El) Then 'if column months is a specific arrMonths column:
                    arrTasks(k) = arrTasks(k) + arr(i, j)               'sumarize everything in the arrTask each element
                End If
         Next j
         k = k + 1                      'increment k, for the next month
       Next El
       dict.Add arr(i, 1), arrTasks     'create the dictionary key with the tasks array as item
       ReDim arrTasks(UBound(arrMonths)): k = 0 'reinitialize arrTasks and k variable
    Else                                        'if dictionary (task) key exists:
        For Each El In arrMonths
            For j = 2 To UBound(arr, 2)
                If month(DateValue(arr(1, j))) = month(El) Then
                    arrTasks(k) = dict(arr(i, 1))(k) + arr(i, j) 'add the sum to the allready existing elements
                End If
         Next j
         k = k + 1
       Next El
       dict(arr(i, 1)) = arrTasks                     'make the item the updaded array
       ReDim arrTasks(UBound(arrMonths)): k = 0       'reinitialize arrTasks and k variable
    End If
  Next i

  'place the processed values in final array (arrFin):
  For i = 0 To UBound(arrMonths) 'firstly the headers:
        arrFin(1, i + 2) = arrMonths(i)
  Next i
  'Extract the tasks value for each month and place in the final array appropriate columns:
  For i = 0 To dict.count - 1           'iterate between the dictionary elements:
      arrFin(i + 2, 1) = dict.Keys(i)   'place the task in the array first column, starting from the second row
     For j = 0 To UBound(dict.items(i)) 'iterate between the dictionary item array elements
        arrFin(i + 2, j + 2) = dict.items(i)(j) 'place the appropriate array elements in the final array (arrFin)
     Next j
  Next i
  'drop the final array at once and make some formatting:
  With sh1.Range("A1").Resize(UBound(arrFin), UBound(arrFin, 2))
    .Value = arrFin
    With .rows(1)
        .Font.Bold = True
        .Interior.ColorIndex = 20
        .BorderAround 1
    End With
    .EntireColumn.AutoFit
    .BorderAround 1
 End With
 sh1.Activate 'to see the processing result...
 MsgBox "Ready..."
End Sub

请进行测试并发送一些反馈。

【讨论】:

    猜你喜欢
    • 2013-05-02
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 2020-07-06
    • 2021-04-28
    • 1970-01-01
    • 1970-01-01
    • 2020-06-13
    相关资源
    最近更新 更多