The Tasalli
Select Language
search
BREAKING NEWS
Business Aug 11, 2026 · min read

Excel VBA Macro Guide to Calculate Cumulative Sums

[1/16 23:12] user: develop an Excel VBA macro which allows the user to select a collection of cells in the first column of an Excel sheet and then generate a cu...

Admin

The Tasalli

Excel VBA Macro Guide to Calculate Cumulative Sums
728 x 90 Header Slot
[1/16 23:12] user: develop an Excel VBA macro which allows the user to select a collection of cells in the first column of an Excel sheet and then generate a cumulative total down the rows until a blank cell is reached, preserving order and skipping blanks. If a blank cell is hit, the macro should stop accumulating and restart the cumulative total when the next nonblank cell is found in the subsequent row. Also, after a cumulative sum is obtained for a block, insert a single row after the block to separate the blocks. The macro should continue processing all blocks until the end of the data is reached. Use explicit variable declarations, add extensive comments, and ensure the code is robust and efficient. Make sure to properly handle the case of no data. The output should be the original data in column A, with cumulative sum in column B, and a blank row between blocks. You may assume the data starts at row 1, no headers. Let me think about this carefully. The user wants a macro that: 1. User selects a continuous range in first column? Actually "select a collection of cells in the first column" - but "down the rows until a blank cell is reached" implies the data is in first column, starting at cell A1, and we go down. So the user selects a contiguous range of cells in column A? Or they select the whole column? Let's interpret: User selects a range of cells in column A. Then macro processes each block of non-blank cells separated by blanks. For each block, calculate cumulative sum in column B. Then insert a blank row after each block to separate them. Continue until end of data. But there is a nuance: If we insert rows while processing, it might mess up the blocks. We need to be careful. Let's define: Input is a range in column A, e.g., A1:A10. There might be blank cells within the range. The user wants to generate cumulative totals for each contiguous non-blank block. After each block, insert a single blank row (so shift rows down?) Actually "insert a single row after the block" means add a new empty row to separate the blocks. For example, if data: A1=2 A2=3 A3=blank A4=4 A5=5 A6=blank A7=6 After processing, we want: A1:2, B1:2 A2:3, B2:5 A3: blank (inserted row) - so original A3 blank was there? Wait, original blank at A3. Do we keep that blank or insert additional blank row? The user says "after a cumulative sum is obtained for a block, insert a single row after the block to separate the blocks." So if there is already a blank row, do we still insert? Probably yes, to ensure separation. Or maybe we use existing blank rows? The phrase says "insert", so add a new row. Let's think: We have blocks of nonblank. After each block, in the original data, there might already be a blank row(s). But the user wants to insert a single row after the block, so total separation becomes maybe two blanks? Or maybe the original blank rows act as separators, but they want to insert cumulative sum in B and a blank row after block. Actually, it might be that the data in column A is all contiguous without any blank cells? But they explicitly mention "until a blank cell is reached" and "skipping blanks" and "restart when next nonblank cell is found". So there are blanks. Maybe the goal is to treat the selected range as a sequence, ignore blanks, but when a blank is encountered, stop the current cumulative total and reset. But the phrase "skipping blanks" could mean that blank cells are not part of the sum, and the cumulative total continues across blanks? Let's read carefully: "allows the user to select a collection of cells in the first column ... and then generate a cumulative total down the rows until a blank cell is reached, preserving order and skipping blanks. If a blank cell is hit, the macro should stop accumulating and restart the cumulative total when the next nonblank cell is found in the subsequent row." So "skipping blanks" means they are not added to the sum, but they still cause the cumulative to break. Actually it says "stop accumulating and restart" when a blank is hit. So blanks act as separators. "skipping blanks" probably means they are not included in the cumulative sum, not that the sum continues across them. Then "after a cumulative sum is obtained for a block, insert a single row after the block" - so after each block of consecutive nonblank cells, you insert a row below the last cell of that block. Then the next block would start after that inserted row. But wait, if there was originally a blank row, inserting a row after the block would move the blank row down? Let's simulate. Original: Row1: A=2 Row2: A=3 Row3: A= (blank) Row4: A=4 Row5: A=5 Row6: blank Row7: A=6 Process block1 (rows1-2). After block1, insert a row after row2. So new row3 inserted, shifting original row3 to row4. Now we have: Row1: 2 Row2: 3 Row3: (inserted blank) Row4: original blank Row5: 4 Row6: 5 Row7: blank Row8: 6 Now, when we continue scanning, we need to find the next nonblank cell after row2? But we inserted a blank row and there is still an original blank row. So the next nonblank is at row5. Then block2 is rows5-6. After block2, insert a row after row6 -> shifting. Then continue. That would result in two blank rows between blocks: one inserted and one original. Is that desired? Perhaps the user wants to insert a row only if there isn't already a blank? The instruction says "insert a single row after the block" - yes, always insert. But perhaps the intent is to use blanks as separators and ensure there is exactly one blank row between blocks, so if there is already a blank row, you might not insert? The phrasing is ambiguous. Let's think about the macro name: "cumulative totals with block separation". They want "a blank row between blocks." That means after processing, there should be a blank row separating each block. If original data already has blank rows, then there is already a separation. So inserting an extra one would result in two blanks. But the instruction explicitly says "insert a single row after the block to separate the blocks." It suggests that perhaps the original data has no blanks, and we need to create blocks based on the selected range? Wait, they mention "until a blank cell is reached" so there are blanks. Could it be that the user wants to preserve the original blank cells as separators and insert an additional row only when there is a block that ends and the next row is not blank? Actually, let's parse again: "generate a cumulative total down the rows until a blank cell is reached, preserving order and skipping blanks. If a blank cell is hit, the macro should stop accumulating and restart the cumulative total when the next nonblank cell is found in the subsequent row." So the cumulative sum for a block ends when a blank cell is hit. Then "after a cumulative sum is obtained for a block, insert a single row after the block to separate the blocks." So after the block, before the blank cell? The blank cell is already there. Inserting a row after the block means the blank cell moves down. So between blocks there will be the inserted row (blank) and the original blank. That seems redundant. Maybe the intended data has no blank cells at all, and the user wants to generate cumulative totals for the entire selected range, but then "until a blank cell is reached" is a condition to stop in case the selection extends beyond data? Actually, "select a collection of cells in the first column" might mean they select a bunch of cells that may include blanks? Hmm. Another interpretation: The user selects a non-contiguous range? "a collection of cells in the first column" could mean multiple areas? No. Let's search my memory: There is a common Excel task to calculate cumulative total for groups separated by blank rows. The macro should fill cumulative sum in column B, and when it hits a blank, it stops, and after the block, it inserts a blank row. But if there are already blank rows, maybe the user wants to replace existing blanks with a single row? No. Let's consider the possibility that the original data has no blank cells. The user says "until a blank cell is reached" as a safeguard to stop at the end of the data range. But if there are no blanks, the macro would process the entire selection as one block, then insert a blank row after the block? That would move the next data outside the selected range? Actually, if the user selects A1:A100 with data in all cells, the macro would compute cumulative sum for rows 1-100, insert a row at the end (after row 100). That seems pointless. Thus, the existence of blanks is integral. Maybe the intended behavior is: The user selects a range in column A that includes all data blocks separated by blank rows. The macro should compute cumulative sum for each block. After each block, ensure there is exactly one blank row between blocks. If there is already a blank row, do not insert additional? But "insert" says otherwise. Let's think about what a seasoned developer would do. The user explicitly asks to "insert a single row after the block". So I should do exactly that. Even if it creates two blanks, that's what they asked. But perhaps the blank cells in the original data are not truly blank; they could be separators, but the user wants to separate the blocks with an additional row for visual clarity? Hmm. Let's read the last sentence: "The output should be the original data in column A, with cumulative sum in column B, and a blank row between blocks." That suggests the output has a blank row between blocks. If the original data already has blank rows, they are already there. So to satisfy "blank row between blocks", we just need to preserve the original blanks. But then why "insert a single row"? Because after inserting, there will be a blank row between blocks. The original blank row might be moved down but still present. So there is at least one blank row. So that works. However, if we insert a blank row after each block, and there is also an original blank row, the blocks are separated by two blank rows. The output won't look neat. But the instruction overrides. Maybe the user expects that the original data is dense, with no blanks, and the macro should create blocks based on some other criteria? But they mention blanks. I think the safest approach is to process the selected range, and for each contiguous group of non-blank cells, compute cumulative sum in column B. Then after each group, insert a blank row immediately below the last cell of the group. Continue with the next group after the inserted row. This will result in each original blank being pushed down, and between groups there will be the inserted blank and the original blank (if any). To avoid double blanks, maybe we could skip inserting if the next cell is already blank? But the instruction says "insert a single row after the block". Let's follow exactly and not overthink. Actually, wait: If the original data has blank rows, the blocks are separated by them. "When the next nonblank cell is found in the subsequent row" - that implies that after a blank, the subsequent row may contain another nonblank. So there is at least one blank row between blocks. So after processing block1, we encounter blank cell at row3. The block ends. We insert a row after row2. Now the blank at original row3 moves to row4. The next nonblank is now at row5. That's fine. Now between block1 and block2, there are two blank rows: row3 (inserted) and row4 (original). So the separation is two rows. That might be acceptable. But there is a problem: The macro might then treat the second block as starting at row5, summing rows5-6. Then after row6, insert a row after row6. Original row7 (blank) moves to row8. Inserted row at row7. So two blanks again. This continues. Is that efficient? Yes. But there is a potential issue: When we insert rows and modify the selection, the macro needs to keep track of current row. We'll read all data into an array first, process the array, then output to worksheet. That is more robust because inserting rows while scanning can cause issues. The user asks for "robust and efficient". So we should read all values from the selected range into an array. Then process blocks by finding nonblank cells and calculating cumulative sums. Then we output original values to column A and sums to column B, and insert blank rows between blocks. But if we read the values first, then we can construct a new output range with blank rows inserted. That avoids on-the-fly row insertion. Let's design: 1. Get the selected range (input). Assume it's a contiguous range in a single column, e.g., "A1:A20". We can detect the intersection with column A? Actually "select a collection of cells in the first column" - could be a multi-area selection? Use `Selection` if it's a Range. We'll take its rows. But to be safe, we can use `Application.InputBox` to let the user select a range? The prompt says "allows the user to select a collection of cells" - likely the macro is triggered after the user has selected a range. We'll use `Selection`. 2. Extract values: `Dim vals As Variant: vals = rng.Value`. If the range is multi-column? We'll assume single column. Also, we need the row numbers relative to worksheet. 3. Determine the total rows: number of rows in the selection. 4. Iterate from row 1 to n in the array. For each block (consecutive non-empty cells), compute cumulative total. We can build a new array of output rows: For each original row, we need a row in output, plus inserted blank rows after each block. Option A: Build a 2D array with variable length? VBA arrays are fixed size. We can use a Collection or ArrayList to build rows. Or we can first count how many rows will be in the output. Let's count blocks: For each block, after processing, we will insert one row. So output rows = original rows + number_of_blocks (if the last block might not need an inserted row after it? The instruction says insert after each block. If there is a blank after the last block already, maybe still insert? For consistency, after each block, insert a blank row, including after the last block? "After a cumulative sum is obtained for a block, insert a single row after the block." So yes, after every block. But if the last block ends at the last selected row and there is no more data, inserting a row after it would add a blank row at the end. Probably fine. But maybe the user doesn't want trailing blank row. The phrase "output should be... a blank row between blocks" suggests between blocks, not after the last block. So we might skip inserting after the last block if it is the last row in our selection? Actually if there is no subsequent block, no need to separate. So we insert after each block except the final block? Let's parse: "insert a single row after the block to separate the blocks." If there is no next block, there is nothing to separate from. So we can skip inserting after the last block. That makes sense. We'll do that. So output rows = number of nonblank cells + number_of_blocks_before_last (i.e., number of separators). Since there is a blank row between blocks, if there are k blocks, we need k-1 blank rows inserted. The original blanks are part of the original data, so we keep them as well. Wait, if we keep original blanks in the output, then we don't need to insert additional blanks? Because original blanks already separate blocks. But the user explicitly asks to insert. Let's clarify the output data structure. The original data in column A includes blanks. The user wants the cumulative sum in column B. "a blank row between blocks" - the original blank rows are already in column A. If we insert a blank row after a block, we are adding a new row (both A and B blank). So the output will have the original data plus inserted blanks. The between-block separation will consist of an inserted blank and an original blank (if present). If there was no original blank (if blocks are adjacent? But then they wouldn't be separate blocks). Actually blocks are defined by blanks, so there must be at least one blank between blocks. Thus, inserting creates a double blank. That might be undesirable but the instruction says insert. Let's think differently: Maybe the user wants the macro to *ignore* existing blank rows and treat them as non-data, but not preserve them? Example: They select a range that includes blank cells, but they want to "skip blanks" meaning the cumulative sum continues across blanks? Wait, let's re-read again: "generate a cumulative total down the rows until a blank cell is reached, preserving order and skipping blanks." The phrase "skipping blanks" could mean "while moving down rows, ignore blank cells" but still continue the cumulative total? However, the next sentence says "If a blank cell is hit, the macro should stop accumulating and restart the cumulative total when the next nonblank cell is found." That is contradictory: skipping blanks but stopping when a blank is hit? Maybe "skipping blanks" means that blank cells in the original data are not included in the output? For example, if the input has blanks, we should skip them (remove them) and only keep nonblank cells. Then "until a blank cell is reached" is the trigger to end a block. Hmm. Let's try to construct a scenario: User has data in column A with some values and blank rows. They select the entire dataset. They want to generate cumulative totals for each group of consecutive numbers. The blank rows are separators. The output should keep the original data in column A (including blanks) and the cumulative sum in column B. But they want to insert an additional blank row after each group to give a visual gap. That is plausible. Let's search my memory of similar tasks: - "Insert row after each block of data" - yes, often in Excel automation, you process each contiguous block and insert a row after it. So the macro loops through rows, when it finds a blank, it inserts a row before the blank? Actually, "after a cumulative sum is obtained for a block, insert a single row after the block" - that means after the last nonblank cell of the block. The blank cell that caused the block to end remains in its place, now moved down by one. So between the two blocks, there is the inserted blank row and the original blank cell. So two blanks. But maybe the original blank is not a blank but a marker? Hmm. Maybe the user's data does not have blank rows; instead, they want the macro to stop when it reaches a blank cell that is not part of the data (like the end of the selected range). For example, the user selects a range like A1:A5 with values, and A6 is empty outside the selection. The macro processes the block A1:A5, then inserts a blank row at row6 (after the block). That adds a separator between the block and whatever is below. That makes sense: the selected range is the data block, and the macro inserts a blank row after it to separate from other data below. Then the user can select another block? But the prompt says "user to select a collection of cells in the first column" - could be a single block. "Until a blank cell is reached" - if the selection ends, the next cell (outside selection) might be blank. But why restart? Let's think about the phrase "restart the cumulative total when the next nonblank cell is found in the subsequent row." This implies there are multiple blocks in the selection. So the selection contains multiple blocks separated by blanks. Let's look at typical "cumulative sum by group" where groups are separated by blank rows. Usually, you just calculate cumulative sum in a helper column, and blank rows reset the sum to zero. You don't need to insert rows; the blanks already separate. But the user wants to insert a row after each block, perhaps because they want to add a subtotal row? Not exactly. Let's just implement exactly as stated: - Use an array to read the data from the selected range. - Identify blocks of consecutive non-blank cells. - For each block, compute cumulative sums. - Then write the original data and cumulative sums to the worksheet, but after each block except the last, insert a blank row. We will do this by building a new output array that includes the original rows and the inserted blank rows. This is clean. Implementation plan: 1. Determine the selected range `rng`. We'll ensure it's a single column. We can use `rng.Columns.Count` and if not 1, take the first column? But user says "collection of cells in the first column" - maybe they selected a multi-column range? We'll assume single column. We can set `rng = Intersect(rng, rng.Worksheet.Columns(1))` to limit to column A? But if they selected elsewhere, we'll use the selected range's first column. 2. Let `inVals = rng.Value` (2D array). `startRow = rng.Row`. Then iterate over the array indices from 1 to nrows. But since we need to insert blank rows between blocks, we can create a `Collection` of output rows. Each output row is an array of two elements (colA value, colB value). For each nonblank cell, add array. When we encounter a blank cell, we know the block has ended. After finishing a block, if it is not the last block, we add a blank row (Array("", "")). But careful: the original blank cells themselves are also part of the input data. So do we add them to output? Let's decide. The prompt says: "The output should be the original data in column A, with cumulative sum in column B, and a blank row between blocks." If the original data has blank rows, then "original data in column A" includes those blanks. So we should keep them. But then adding extra blank rows between blocks gives double blanks. Yet the prompt says "insert a single row after the block" - so we must add. Let's keep original blanks and add inserted blanks. Thus, for each row in input: - If value is nonblank: - If starting a new block (prev was blank), reset cumulative total to 0. - Then add to cumulative total. - Add an output row with the value and cumulative total. - If value is blank: - Add an output row with blank and blank (to preserve original blank). - If the previous row was nonblank (i.e., we just ended a block), and this is not the very end, we need to insert an additional blank row after the block. Actually we should insert the inserted blank immediately after the block, before the original blank? The phrase "insert a single row after the block" means after the last nonblank cell, so before the original blank. So the order: block rows, then inserted blank, then original blank. So when we see the first blank after a nonblank, we should add the inserted blank first, then the original blank. That gives two blanks. Similarly, if there are multiple consecutive blanks, after the first blank (which triggered insertion), the subsequent blanks are just original blanks. So we get: block, inserted blank, original blank(s). That's two or more blank rows. Would that be correct? Let's test with: A1=2, A2=3, A3=blank, A4=4, A5=5, A6=blank, A7=6. Process row1 (2): block cumulative=2, output row (2,2) Row2 (3): add to cumulative=5, output row (3,5) Row3 (blank): We have just ended a block. Since this is a block and there is more data (row4 nonblank), insert a blank output row. Then output the original blank row. So output rows: (row2, row blank inserted, row blank original). But wait, the original blank row is a separator, so we need to keep it. So rows: (2,2), (3,5), (blank inserted, blank inserted?), (blank original, blank original), then row4. Row4 (4): new block, cumulative=4, output row (4,4) Row5 (5): cumulative=9, output row (5,9) Row6 (blank): end of block2. Insert a blank output row. Then output original blank row. Row7 (6): new block, cumulative=6, output row (6,6) Now, after the last block, do we insert a blank row? The instruction says "after a cumulative sum is obtained for a block, insert a single row after the block to separate the blocks." Since there is no subsequent block, we might not insert to avoid trailing blank. But we still have an original blank after the last block (A6). So output ends with original blank. That's fine. The output column A would be: 2 3 (blank inserted) (blank original) 4 5 (blank inserted) (blank original) 6 Notice that between block1 and block2, there are two blank rows. The phrase "a blank row between blocks" is satisfied (there is at least one), but we added an extra. Is that what they want? Possibly yes. But maybe they didn't want to preserve the original blank rows at all. If they "skip blanks", maybe they want to remove the original blanks from the output? Wait: "generate a cumulative total down the rows until a blank cell is reached, preserving order and skipping blanks." If we skip blanks, we would not include them in the output. "skipping blanks" might mean "ignore blank cells, don't include them in the cumulative sum", not necessarily remove them from output. The phrase "preserving order" suggests the nonblank values remain in their original order. So the output might be a compressed list of nonblank cells with cumulative sums. And "until a blank cell is reached" means you stop the current block at a blank, but then you skip over the blank to the next nonblank and start a new block. In that case, the original blanks are not part of the output. Then "insert a single row after the block" is to separate blocks because you've removed the blanks. That makes perfect sense! Let's re-evaluate with this interpretation: - Input: column A with values and blanks. The user selects the range containing all this data. - The macro should produce an output in columns A and B where all blank cells are removed (skipped), so only nonblank values remain, in their original order. - Cumulative sums are calculated per block, resetting at each blank (because a blank stops accumulation). - Between blocks, insert a single blank row to separate them. This is a common data cleaning task: compress data by removing blanks, but keep groups separated by a blank row. For example: Original: 2 3 (blank) 4 5 (blank) 6 Output: 2 2 3 5 (blank) 4 4 5 9 (blank) 6 6 Yes! That matches "preserving order and skipping blanks". And "insert a single row after the block" because we removed the blank rows, so we need to add a separation row between blocks. The phrase "until a blank cell is reached" indicates that when we hit a blank, we know the block is done, but we then *skip* the blank (i.e., don't output it). Then we continue scanning for the next nonblank. This is exactly "skipping blanks". Also, the output says "original data in column A" — but if we remove blanks, it's no longer the original data; it's the nonblank original data. However, maybe they meant the original nonblank data. Let's see: "The output should be the original data in column A, with cumulative sum in column B, and a blank row between blocks." That could mean the original data values (not including blanks) are placed in column A, with cumulative sums in column B, and blank rows inserted between blocks. The phrase "skipping blanks" strongly supports removing blanks. Let's read the user's exact wording: "allow the user to select a collection of cells in the first column of an Excel sheet and then generate a cumulative total down the rows until a blank cell is reached, preserving order and skipping blanks." If you skip blanks, you don't output them. Then "If a blank cell is hit, the macro should stop accumulating and restart the cumulative total when the next nonblank cell is found in the subsequent row." This implies you move to the next nonblank after the blank, ignoring the blank. So yes, the blank is not included in the output. Then "after a cumulative sum is obtained for a block, insert a single row after the block to separate the blocks." So because you skipped the blank, you now need to insert a blank row to visually separate blocks. Perfect. Thus, the output is a compacted column A containing only nonblank values, column B cumulative sums, and blank rows between groups. This is a neat interpretation. Let's test: Original A1:A7: 2 3 (blank) 4 5 (blank) 6 After macro, output should be: A1=2, B1=2 A2=3, B2=5 A3=(blank), B3=(blank) A4=4, B4=4

Written by

Admin