Search code examples
excelvbasplitassign

How to get to this result?


I want to achieve the following result in D:F

4 digit numbers are assigned to the correct number in column D, and every percentage in column F is assigned to the correct value in column E.

enter image description here

I used for the splitting part (fe. 40218 gets to 40 in column D & 0218 in column E) the following code, of course with a lot of help by this forum. The code is a called sub by a precending sub. I can not use both in combination anymore because i had change the precending code by its advanced filter (first it was filtered just on the output which you can see in column H, i adapted it and therefore column I & J were also submitted to the output range). Anyway it fine for me if i use H:J as my starting point. This just as a quick explanation why the sub splitByChars includes Paramaters ByRef & ByVal

So Range H:J is the new Start Point Zero.

Sub splitByChars( _
        ByRef rg As Range, _
        ByVal Chars As Long)
    
    Dim Data As Variant: Data = rg.Value
    Dim rCount As Long: rCount = UBound(Data, 1)
    Dim cCount As Long: cCount = 1
    
    Dim cSize As Long
    Dim r As Long, c As Long
    Dim iLen As Long, fLen As Long, rLen As Long
    Dim iString As String, rString As String
    

    For r = 1 To rCount
        iString = CStr(Data(r, 1))
        iLen = Len(iString)
        If iLen >= Chars Then
            fLen = iLen Mod Chars
            Data(r, 1) = Left(iString, fLen)
            rLen = iLen - fLen
            cSize = rLen / Chars + 1
            rString = Mid(iString, fLen + 1, rLen)
            If cSize > cCount Then
                cCount = cSize
                ReDim Preserve Data(1 To rCount, 1 To cSize)
            End If
            For c = 2 To cSize
                Data(r, c) = Mid(rString, (c - 2) * Chars + 1, Chars)
                Debug.Print r, c, Data(r, c)
            Next c
            
            Else
            Data(r, 1) = ""
            
        End If
    Next r
    
    With rg.Resize(, cCount)
        .NumberFormat = "@"
        .Value = Data
    End With
    
    On Error Resume Next
    
     With rg
    .Value = .Value
    .SpecialCells(xlCellTypeBlanks).EntireRow.Delete
     End With
     
End Sub

The problem with this code is that all vales are missing in D which have less digits than 2. Chars were declared in the precending code =4 because in column E the number is always 4 digits length

  • so Problem 1 arise: Not all values in D are shown up, because not all have 4 digits behind at least one digit in column H

  • The second problem which arise is that even values in D which are unique show differences by their values in column I, so i can not sum up all values for example from 4 to 4, because the percentage from for example 40218 is 15% instead of 50% like for the other ones which are assigned to 4.

Its just really important for me that you all know that i really do not want to use your time for finding that one code which solves all. I'm a beginner, i understand day by day a bit more but this here is way over my undertanding and skills on logical and knwoledge level of VBA.

If there is any chance that you think this is quite easy, i really appreciate your help. If you say, "Guy this is impossible" also fine because then i can put it down and waste not more hours on that project. Also this hint helps more than you maybe can imagine.

Update 21.05.21 precending code on which splitByChars works

Sub Unique_Values_Worksheet_Variables()

'1 Code + Sub splitByChars
    
    Const Chars As Long = 4
    
    Dim wb As Workbook: Set wb = ThisWorkbook
    Dim sws As Worksheet: Set sws = wb.Worksheets("export")
    Dim dws As Worksheet:
    Set dws = wb.Worksheets.Add(After:=wb.Sheets(wb.Sheets.Count))
    
    sws.Range("C:C").AdvancedFilter _
        Action:=xlFilterCopy, _
        CopyToRange:=dws.Range("A:A"), _
        Unique:=True
          
    dws.Columns("A:B").EntireColumn.AutoFit
    Dim rng As Range:
    Set rng = dws.Range("A1:B1", dws.Cells(dws.Rows.Count, 1).End(xlUp))
    rng.Borders(xlDiagonalDown).LineStyle = xlNone
    rng.HorizontalAlignment = xlCenter
    
    
    With rng.Borders()
        .LineStyle = xlContinuous
        .ColorIndex = 0
        .TintAndShade = 0
        .Weight = xlThin
        
    End With
    
    Cells(1, 1).Value = "Produktgruppe"
    Cells(1, 2).Value = "Serie"
    
    'folgend setzt Sub SplitByChars auf dieser Prozedur Unique_Values_Workesheet_Variables auf

    splitByChars rng.Resize(rng.Rows.Count - 1).Offset(1), Chars
    
    ActiveWindow.DisplayGridlines = False

End Sub

But like i said this precending code does not work anymore in combination with spliByChars because the Filter Method had to be adjusted

Sub Unique_Values_Worksheet_Variables()
    '1 Code + Sub splitByChars
    Const Chars As Long = 4
     
    'Dim wb As Workbook: Set wb = ThisWorkbook
    'Dim sws As Worksheet: Set sws = wb.Worksheets("export")
    
   ' Source
    Const sName As String = "export1"
    Const sUniqueColumn As String = "C"
    Const sCopyColumnsList As String = "C,I,J" ' exact order of the columns
    ' Destination (new worksheet)
    
    Const dFirst As String = "A1"
    ' Workbook
    Dim wb As Workbook: Set wb = ThisWorkbook
    Dim dws As Worksheet: Set dws = wb.Worksheets.Add(After:=wb.Sheets(wb.Sheets.Count))
    
    Dim sCopyColumns() As String: sCopyColumns = Split(sCopyColumnsList, ",")
    Dim dCell As Range: Set dCell = dws.Range(dFirst)
    
    Application.ScreenUpdating = False
    
    Dim rng As Range
    With wb.Worksheets(sName).Range("A1").CurrentRegion
        .Columns(sUniqueColumn).AdvancedFilter xlFilterInPlace, , , True
        Dim n As Long
        For n = 0 To UBound(sCopyColumns)
            .Columns(sCopyColumns(n)).Copy dCell
            Set dCell = dCell.Offset(, 1)
        Next n
        .Parent.ShowAllData
    End With
    
    Application.ScreenUpdating = True
      
    dws.Columns("A:J").EntireColumn.AutoFit
    
    Set rng = dws.Range(dCell, dws.Cells(dws.Rows.Count, 1).End(xlUp))
    rng.Borders(xlDiagonalDown).LineStyle = xlNone
    rng.HorizontalAlignment = xlCenter
   
    'folgend setzt Sub SplitByChars auf dieser Prozedur Unique_Values_Workesheet_Variables auf

    splitByChars rng.Resize(rng.Rows.Count - 1).Offset(1), Chars
    
    ActiveWindow.DisplayGridlines = False

End Sub

and exactly like this it has to work

enter image description here


Solution

  • Option Explicit
    
    Sub mymacro()
    
        Dim wb As Workbook, ws As Worksheet
        Dim iLastRow As Long, i As Long
        Dim sPcent As String, s As String, colD As String, colE As String
        Dim dict, key, ar
    
        Set wb = ThisWorkbook
        Set ws = wb.Sheets(1)
        Set dict = CreateObject("Scripting.Dictionary")
    
        ' process data
        iLastRow = ws.Cells(Rows.Count, "H").End(xlUp).Row
        For i = 3 To iLastRow
            s = ws.Cells(i, "H")
            sPcent = Format(ws.Cells(i, "I"), "0.00")
            If Len(s) > 4 Then
                colD = Left(s, Len(s) - 4)
                colE = Right(s, 4)
            Else
                colD = s
                colE = ""
            End If
            key = colD & vbTab & sPcent
    
            If dict.exists(key) Then
                If Len(colE) > 0 Then
                    dict(key) = dict(key) & "," & colE
                End If
            Else
                dict.Add key, colE
            End If
        Next
    
        ' output result
        ws.Range("D1:G1") = Array("a", "b", "c", "d")
        ws.Columns("D:G").NumberFormat = "@"
        i = 2
        For Each key In dict.keys
            ar = Split(key, vbTab) 'colD,pcent
            ws.Cells(i, "D") = ar(0)
            ws.Cells(i, "E") = dict(key)
            ws.Cells(i, "F") = ar(1)
            ws.Cells(i, "G") = "%"
            i = i + 1
        Next
        MsgBox "Done"
    
    End Sub