How to SUM / merge similar rows in Excel using VBA and save as each sheet to CSV ?

Mark Jo

New Member
Joined
Jun 28, 2023
Messages
2
Office Version
  1. 2016
Platform
  1. Windows
I'm trying to build a simplemacro in VBA for Excel that would SUM [merge] all the rows that have the same name (value in the first columns) and save as each sheet to CSV file. So for example :

sheet1
Apple20
Banana50
Banana10
Apple80

Sheet2
Orange10
Melon15
Orange25
Melon10

the result should be like this :
sheet1
Apple100
Banana60

Sheet2
Orange35
Melon25

and finnaly l want save as each sheet to CSV file :
sheet1.csv
sheet2.csv
 

Excel Facts

How can you automate Excel?
Press Alt+F11 from Windows Excel to open the Visual Basic for Applications (VBA) editor.
I gave this a try. This can be modified if necessary. The workbook is HERE.

VBA Code:
Option Explicit

' ----------------------------------------------------------------
' Procedure Name: ProcessResults
' Purpose: Summarize items and quantities then export to a csv file.
' Procedure Kind: Sub
' Procedure Access: Public
' Author: Jim
' Date: 7/2/2023
' ----------------------------------------------------------------

Sub ProcessResults()

'   Worksheet containing the data to export.
    Dim wsData As Worksheet
    
'   Cell that is the "Anchor" for the data to process.
    Dim rAnchorCellData As Range
    
'   Cell that is the "Anchor" for the results.
    Dim rAnchorCellResults As Range

'   Used to iterate through data.
    Dim rCell As Range
    
'   Contains the name of the file to save
    Dim sFileNameLabel As String
    
'   Contains the name of the csv file.
    Dim sFileName As String

'   Contains the label for the data range.
    Dim sDataLabel As String
    
'   Contains the label for the results range.
    Dim sResultsLabel As String
    
'   Used with dictionary to keep track of each item.
    Dim sItemName As String
    
'   Used with dictionary to keep track of items' quantity.
    Dim iItemNextQuantity As Long
    Dim iItemExistingQuantity As Long
    
'   Count of rows of results
    Dim iResultsRowsCount As Long
    
'   Used to keep track of count of rows processed within Do Loop.
    Dim iDataRow As Long
    
'   Used to iterate through all entries in the dictionary
    Dim vKey As Variant
    
'   Create dictionary
    Dim dicData As Object
    Set dicData = CreateObject("Scripting.Dictionary")

'   Code looks for this word to know where the file name is located.
    sFileNameLabel = "File Name"
    
'   Code looks for this word to know where the data starts.
    sDataLabel = "Data"
    
'   Code looks for this word to know where the results start.
    sResultsLabel = "Results"
    
    Set wsData = ActiveSheet
    
'   Look for cell that is the row header label for the file name.
    Set rCell = FindCell(wsData, sFileNameLabel)
    
    If rCell Is Nothing _
     Then
        MsgBox "Could not find the label cell for the file name (" & sFileNameLabel & ").", vbInformation
        Exit Sub
    Else
'       File name is in the cell one tho the right of the cell where the file name label is found.
        sFileName = rCell.Offset(0, 1).Value
    End If
    
    'sFileNameLabel
    
'   Get anchor cell for data. It is two cells below the cell whose label is in sDataLabel.
    Set rAnchorCellData = FindCell(wsData, sDataLabel)
        
    If rAnchorCellData Is Nothing _
     Then
        MsgBox "Could not find the label cell for data (" & sDataLabel & ").", vbInformation
        Exit Sub
    End If
    
    Set rAnchorCellData = rAnchorCellData.Cells.Offset(2)
    
'   Get anchor cell for results. It is two cells below the cell whose label is in sResultsLabel.
    Set rAnchorCellResults = FindCell(wsData, sResultsLabel)
    
    If rAnchorCellResults Is Nothing _
     Then
        MsgBox "Could not find the label cell for results (" & sResultsLabel & ").", vbInformation
        Exit Sub
    End If
    
    Set rAnchorCellResults = rAnchorCellResults.Cells.Offset(2)
    
'   Get count of DATA rows in the results range. Minus one to skip headers.
    iResultsRowsCount = rAnchorCellResults.CurrentRegion.Rows.Count - 1
    
'   Clear the existing results
    If iResultsRowsCount <> 0 _
     Then rAnchorCellResults.Offset(1).Resize(iResultsRowsCount, 2).Clear

    With rAnchorCellData
    
        iDataRow = 0
        
        Do
  
            sItemName = .Offset(iDataRow + 1, 0).Value
            
            iItemNextQuantity = .Offset(iDataRow + 1, 1).Value
            
            If Not dicData.Exists(sItemName) _
             Then
                
'               The item IS NOT in the dictionary so add it, with quantity.
                dicData.Add sItemName, iItemNextQuantity
            
            Else
            
'               Item IS in the dictionary. Get the existing value, add it to the
'               next quantity then put updated values into the dictinary.
                iItemExistingQuantity = dicData(sItemName)
    
                iItemNextQuantity = iItemNextQuantity + iItemExistingQuantity
                
                dicData(sItemName) = iItemNextQuantity
            
            End If
            
            iDataRow = iDataRow + 1

        Loop Until .Offset(iDataRow + 1) = ""
    
    End With
    
    iDataRow = 0
    
'   Put results into the result area in the worksheet.
    With rAnchorCellResults
    
        For Each vKey In dicData.Keys
            
            iDataRow = iDataRow + 1
            
            With .Offset(iDataRow, 0)
                .Value = vKey
            End With
        
            With .Offset(iDataRow, 1)
                .Value = dicData(vKey)
            End With
        
        Next vKey
    
    End With
    
'   Create the csv file
    Call ExportRangeToCSVFile(rAnchorCellResults.CurrentRegion, sFileName)
    
End Sub

VBA Code:
Option Explicit


' ----------------------------------------------------------------
' Procedure Name: ExportRangeToCSVFile
' Purpose: Export the specified range to a csv file.
' Procedure Kind: Function
' Procedure Access: Public
' Parameter prDataRange (Range): Range to export as csv file.
' Parameter psFileName (String): Name of the csv file to create.
' Parameter psPath (String): Optional path for the new csv file.
' Parameter psDelimiter (String): Character used as the delimiter between range cells.
' Parameter pbIncludeFirstRow (Boolean): Optional flag indicating whether to include the first data row.
' Return Type: String
' Author: Jim
' Date: 7/2/2023
' ----------------------------------------------------------------

Function ExportRangeToCSVFile( _
    prDataRange As Range, _
    psFileName As String, _
    Optional psPath As String = "", _
    Optional psDelimiter As String = ",", _
    Optional pbIncludeFirstRow As Boolean = True) _
As String

'   Used to test for new row variable.
    Dim iHoldRow As Long

'   Used to hold the text to export including delimiter.
    Dim sExportString As String

'   Used to iterate through each cell in the range.
    Dim rCell As Range

'   Handle file name extension.
    If UCase(Right(psFileName, 4)) <> ".CSV" _
    Then psFileName = psFileName & ".csv"

'   Handle path. Add trailing slash if necessary.
    If psPath = "" Then psPath = ThisWorkbook.Path

    If Right(psPath, 1) <> "\" Then psPath = psPath & "\"

'   Check the path exists. If not, tell user then bail out.
    If Dir(psPath, vbDirectory) = "" _
     Then
        MsgBox "The path specified cannot be found." & Chr(10) & psPath, vbInformation
        Exit Function

    End If

'   If boolean flag parameter = false then do not include the first data row.
    If Not pbIncludeFirstRow _
     Then
        Dim iRows As Long

        Dim iCols As Long

        iRows = prDataRange.Rows.Count - 1

        iCols = prDataRange.Columns.Count

        Set prDataRange = prDataRange.Offset(1).Resize(iRows, iCols)
    End If

'   Get first row in the range.
    iHoldRow = prDataRange.Row

'   Loop through range variable.
    For Each rCell In prDataRange

        If iHoldRow <> rCell.Row Then

'       Add linebreak and remove extra delimeter
        sExportString _
            = Left(sExportString, Len(sExportString) - 1) _
            & vbCrLf & rCell.Text & psDelimiter

        iHoldRow = rCell.Row

        Else
            sExportString = sExportString & rCell.Text & psDelimiter
        End If

    Next rCell

'   Trim extra delimiter
    sExportString = Left(sExportString, Len(sExportString) - 1)

'   Kill the file if it already exists
    If Len(Dir(psPath & psFileName)) > 0 Then
        Kill psPath & psFileName
    End If

    Open psPath & psFileName For Append As #1    'write the new file
    Print #1, sExportString
    Close #1

End Function

VBA Code:
Option Explicit

Function FindCell( _
    ByVal pwsSearchSheet As Worksheet, _
    ByVal psSearchString As String, _
    Optional ByVal bByRows As Boolean = True, _
    Optional ByVal bMatchCase As Boolean = True, _
    Optional ByVal bDoPart As Boolean = False, _
    Optional ByRef iRow As Long = 0, _
    Optional ByRef iCol As Long = 0) _
As Range

'   Used to set the SearchOrder criterion.
    Dim xlOrder As Long

'   Used to set the LookAt criterion.
    Dim xlWholeOrPart As Long

'   Used to get the row # using Split and R1C1 addressing.
    Dim Cell_Split_R() As String

'   Used to get the column # using Split and R1C1 addressing.
    Dim Cell_Split_C() As String

'   Set the SearchOrder criterion based on boolean parameter bByRows.
    If bByRows Then xlOrder = xlByRows Else xlOrder = xlByColumns

'   Set the LookAt criterion based on boolean parameter bDoPart.
    If bDoPart Then xlWholeOrPart = xlPart Else xlWholeOrPart = xlWhole

    Set FindCell = Nothing

    With pwsSearchSheet.Cells

        Set FindCell = _
            .Find(What:=psSearchString, _
                  LookAt:=xlWholeOrPart, _
                  LookIn:=xlValues, _
                  MatchCase:=bMatchCase, _
                  SearchOrder:=xlOrder, _
                  SearchFormat:=False)
    
    End With
    
    If Not FindCell Is Nothing Then
        Cell_Split_R = Split(FindCell.Address(ReferenceStyle:=xlR1C1), "R")
        Cell_Split_C = Split(Cell_Split_R(1), "C")
        iCol = CInt(Cell_Split_C(0))
        iRow = CInt(Cell_Split_C(1))
    End If

End Function
 
Upvote 0
Solution
I gave this a try. This can be modified if necessary. The workbook is HERE.

VBA Code:
Option Explicit

' ----------------------------------------------------------------
' Procedure Name: ProcessResults
' Purpose: Summarize items and quantities then export to a csv file.
' Procedure Kind: Sub
' Procedure Access: Public
' Author: Jim
' Date: 7/2/2023
' ----------------------------------------------------------------

Sub ProcessResults()

'   Worksheet containing the data to export.
    Dim wsData As Worksheet
   
'   Cell that is the "Anchor" for the data to process.
    Dim rAnchorCellData As Range
   
'   Cell that is the "Anchor" for the results.
    Dim rAnchorCellResults As Range

'   Used to iterate through data.
    Dim rCell As Range
   
'   Contains the name of the file to save
    Dim sFileNameLabel As String
   
'   Contains the name of the csv file.
    Dim sFileName As String

'   Contains the label for the data range.
    Dim sDataLabel As String
   
'   Contains the label for the results range.
    Dim sResultsLabel As String
   
'   Used with dictionary to keep track of each item.
    Dim sItemName As String
   
'   Used with dictionary to keep track of items' quantity.
    Dim iItemNextQuantity As Long
    Dim iItemExistingQuantity As Long
   
'   Count of rows of results
    Dim iResultsRowsCount As Long
   
'   Used to keep track of count of rows processed within Do Loop.
    Dim iDataRow As Long
   
'   Used to iterate through all entries in the dictionary
    Dim vKey As Variant
   
'   Create dictionary
    Dim dicData As Object
    Set dicData = CreateObject("Scripting.Dictionary")

'   Code looks for this word to know where the file name is located.
    sFileNameLabel = "File Name"
   
'   Code looks for this word to know where the data starts.
    sDataLabel = "Data"
   
'   Code looks for this word to know where the results start.
    sResultsLabel = "Results"
   
    Set wsData = ActiveSheet
   
'   Look for cell that is the row header label for the file name.
    Set rCell = FindCell(wsData, sFileNameLabel)
   
    If rCell Is Nothing _
     Then
        MsgBox "Could not find the label cell for the file name (" & sFileNameLabel & ").", vbInformation
        Exit Sub
    Else
'       File name is in the cell one tho the right of the cell where the file name label is found.
        sFileName = rCell.Offset(0, 1).Value
    End If
   
    'sFileNameLabel
   
'   Get anchor cell for data. It is two cells below the cell whose label is in sDataLabel.
    Set rAnchorCellData = FindCell(wsData, sDataLabel)
       
    If rAnchorCellData Is Nothing _
     Then
        MsgBox "Could not find the label cell for data (" & sDataLabel & ").", vbInformation
        Exit Sub
    End If
   
    Set rAnchorCellData = rAnchorCellData.Cells.Offset(2)
   
'   Get anchor cell for results. It is two cells below the cell whose label is in sResultsLabel.
    Set rAnchorCellResults = FindCell(wsData, sResultsLabel)
   
    If rAnchorCellResults Is Nothing _
     Then
        MsgBox "Could not find the label cell for results (" & sResultsLabel & ").", vbInformation
        Exit Sub
    End If
   
    Set rAnchorCellResults = rAnchorCellResults.Cells.Offset(2)
   
'   Get count of DATA rows in the results range. Minus one to skip headers.
    iResultsRowsCount = rAnchorCellResults.CurrentRegion.Rows.Count - 1
   
'   Clear the existing results
    If iResultsRowsCount <> 0 _
     Then rAnchorCellResults.Offset(1).Resize(iResultsRowsCount, 2).Clear

    With rAnchorCellData
   
        iDataRow = 0
       
        Do
 
            sItemName = .Offset(iDataRow + 1, 0).Value
           
            iItemNextQuantity = .Offset(iDataRow + 1, 1).Value
           
            If Not dicData.Exists(sItemName) _
             Then
               
'               The item IS NOT in the dictionary so add it, with quantity.
                dicData.Add sItemName, iItemNextQuantity
           
            Else
           
'               Item IS in the dictionary. Get the existing value, add it to the
'               next quantity then put updated values into the dictinary.
                iItemExistingQuantity = dicData(sItemName)
   
                iItemNextQuantity = iItemNextQuantity + iItemExistingQuantity
               
                dicData(sItemName) = iItemNextQuantity
           
            End If
           
            iDataRow = iDataRow + 1

        Loop Until .Offset(iDataRow + 1) = ""
   
    End With
   
    iDataRow = 0
   
'   Put results into the result area in the worksheet.
    With rAnchorCellResults
   
        For Each vKey In dicData.Keys
           
            iDataRow = iDataRow + 1
           
            With .Offset(iDataRow, 0)
                .Value = vKey
            End With
       
            With .Offset(iDataRow, 1)
                .Value = dicData(vKey)
            End With
       
        Next vKey
   
    End With
   
'   Create the csv file
    Call ExportRangeToCSVFile(rAnchorCellResults.CurrentRegion, sFileName)
   
End Sub

VBA Code:
Option Explicit


' ----------------------------------------------------------------
' Procedure Name: ExportRangeToCSVFile
' Purpose: Export the specified range to a csv file.
' Procedure Kind: Function
' Procedure Access: Public
' Parameter prDataRange (Range): Range to export as csv file.
' Parameter psFileName (String): Name of the csv file to create.
' Parameter psPath (String): Optional path for the new csv file.
' Parameter psDelimiter (String): Character used as the delimiter between range cells.
' Parameter pbIncludeFirstRow (Boolean): Optional flag indicating whether to include the first data row.
' Return Type: String
' Author: Jim
' Date: 7/2/2023
' ----------------------------------------------------------------

Function ExportRangeToCSVFile( _
    prDataRange As Range, _
    psFileName As String, _
    Optional psPath As String = "", _
    Optional psDelimiter As String = ",", _
    Optional pbIncludeFirstRow As Boolean = True) _
As String

'   Used to test for new row variable.
    Dim iHoldRow As Long

'   Used to hold the text to export including delimiter.
    Dim sExportString As String

'   Used to iterate through each cell in the range.
    Dim rCell As Range

'   Handle file name extension.
    If UCase(Right(psFileName, 4)) <> ".CSV" _
    Then psFileName = psFileName & ".csv"

'   Handle path. Add trailing slash if necessary.
    If psPath = "" Then psPath = ThisWorkbook.Path

    If Right(psPath, 1) <> "\" Then psPath = psPath & "\"

'   Check the path exists. If not, tell user then bail out.
    If Dir(psPath, vbDirectory) = "" _
     Then
        MsgBox "The path specified cannot be found." & Chr(10) & psPath, vbInformation
        Exit Function

    End If

'   If boolean flag parameter = false then do not include the first data row.
    If Not pbIncludeFirstRow _
     Then
        Dim iRows As Long

        Dim iCols As Long

        iRows = prDataRange.Rows.Count - 1

        iCols = prDataRange.Columns.Count

        Set prDataRange = prDataRange.Offset(1).Resize(iRows, iCols)
    End If

'   Get first row in the range.
    iHoldRow = prDataRange.Row

'   Loop through range variable.
    For Each rCell In prDataRange

        If iHoldRow <> rCell.Row Then

'       Add linebreak and remove extra delimeter
        sExportString _
            = Left(sExportString, Len(sExportString) - 1) _
            & vbCrLf & rCell.Text & psDelimiter

        iHoldRow = rCell.Row

        Else
            sExportString = sExportString & rCell.Text & psDelimiter
        End If

    Next rCell

'   Trim extra delimiter
    sExportString = Left(sExportString, Len(sExportString) - 1)

'   Kill the file if it already exists
    If Len(Dir(psPath & psFileName)) > 0 Then
        Kill psPath & psFileName
    End If

    Open psPath & psFileName For Append As #1    'write the new file
    Print #1, sExportString
    Close #1

End Function

VBA Code:
Option Explicit

Function FindCell( _
    ByVal pwsSearchSheet As Worksheet, _
    ByVal psSearchString As String, _
    Optional ByVal bByRows As Boolean = True, _
    Optional ByVal bMatchCase As Boolean = True, _
    Optional ByVal bDoPart As Boolean = False, _
    Optional ByRef iRow As Long = 0, _
    Optional ByRef iCol As Long = 0) _
As Range

'   Used to set the SearchOrder criterion.
    Dim xlOrder As Long

'   Used to set the LookAt criterion.
    Dim xlWholeOrPart As Long

'   Used to get the row # using Split and R1C1 addressing.
    Dim Cell_Split_R() As String

'   Used to get the column # using Split and R1C1 addressing.
    Dim Cell_Split_C() As String

'   Set the SearchOrder criterion based on boolean parameter bByRows.
    If bByRows Then xlOrder = xlByRows Else xlOrder = xlByColumns

'   Set the LookAt criterion based on boolean parameter bDoPart.
    If bDoPart Then xlWholeOrPart = xlPart Else xlWholeOrPart = xlWhole

    Set FindCell = Nothing

    With pwsSearchSheet.Cells

        Set FindCell = _
            .Find(What:=psSearchString, _
                  LookAt:=xlWholeOrPart, _
                  LookIn:=xlValues, _
                  MatchCase:=bMatchCase, _
                  SearchOrder:=xlOrder, _
                  SearchFormat:=False)
   
    End With
   
    If Not FindCell Is Nothing Then
        Cell_Split_R = Split(FindCell.Address(ReferenceStyle:=xlR1C1), "R")
        Cell_Split_C = Split(Cell_Split_R(1), "C")
        iCol = CInt(Cell_Split_C(0))
        iRow = CInt(Cell_Split_C(1))
    End If

End Function
Hi OaklandJim, thanks a lot for the answer, you're the best
 
Upvote 0

Forum statistics

Threads
1,223,227
Messages
6,170,848
Members
452,361
Latest member
d3ad3y3

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top