Home > Back-end >  Combine unique values from ranges (with condition) into another ranges
Combine unique values from ranges (with condition) into another ranges

Time:07-31

I need to combine unique values from ranges (with condition) into another ranges on the same rows.
Actually, I post a similar question two days ago enter image description here

Sub CombineRangesOneColumn_v2()

   Application.ScreenUpdating = False
   Application.Calculation = xlCalculationManual
'_________________________________________

   Dim sh As Worksheet, lastR As Long, arr, arrDict, dict As Object
   Dim arrDB, arrDC, mtch, arrFin, i As Long, j As Long, k As Long
   
   Set sh = ActiveSheet
   lastR = sh.Range("A" & sh.Rows.Count).End(xlUp).Row
   
   arr = sh.Range("A2:C" & lastR).Value2
   Set dict = CreateObject("Scripting.Dictionary")
   
   For i = 1 To UBound(arr)
        If Not dict.Exists(arr(i, 1)) Then
            dict.Add arr(i, 1), Array(arr(i, 2), arr(i, 3))   'Place the strings from columns "B" and "C"
        Else
            arrDict = dict(arr(i, 1))                         'extract the array from dict items (it cnnot be modified inside the item)
            arrDict(0) = arrDict(0) & "|" & arr(i, 2)         'place in the array first element the strings collected from B:B
            arrDC = Split(arrDict(1), vbLf)                   'try splitting the second array element (string(s) from C:C)
            If UBound(arrDC) = 0 Then                         'if only one element:
                If arrDC(0) <> arr(i, 3) Then
                   arrDict(1) = arrDict(1) & IIf(arr(i, 3) = "", "", vbLf & arr(i, 3)) 'add to it the value from C:C, separated by vbLf
                End If
            Else
                mtch = Application.Match(arr(i, 3), arrDC, 0) 'check unicity of the string from C:C
                If IsError(mtch) Then                         'only if not existing:
                    arrDict(1) = arrDict(1) & IIf(arr(i, 3) = "", "", vbLf & arr(i, 3))  'add it to the string to be used in the next step
                End If
            End If
            dict(arr(i, 1)) = arrDict                         'put back the array in the dictionary item
        End If
   Next i

   ReDim arrFin(1 To UBound(arr), 1 To 1): k = 1              'redim the final array and initialize k (used to fill the array)
   For i = 0 To dict.Count - 1                                'iterate between the dictionary keys/items:
        arrDict = dict.Items()(i)                             'place the item array in an array
        arrDB = Split(arrDict(0), "|")                        'obtain an array of B:B strins from the item first array element
        For j = 0 To UBound(arrDB)                            'how many unique keys exists 'place the dictionry key per each iteration
                arrFin(k, 1) = arrDB(j) & vbLf & arrDict(1)   'build the string of the second column
                k = k   1
        Next j
   Next i
   'Drop the processed result near the existing range (for easy visual comparison):
   sh.Range("D2").Resize(UBound(arrFin), 1).Value2 = arrFin
'_______________________________________________
   Application.ScreenUpdating = True
   Application.Calculation = xlCalculationAutomatic
End Sub    

CodePudding user response:

Please, try the next version. It should do what (I understood) you need:

Sub CombineRangesOneColumnEmptyRemoved()
   Dim sh As Worksheet, lastR As Long, arr, arrDict, dict As Object
   Dim arrDB, arrDC, mtch, arrFin, i As Long, j As Long, k As Long
   
   Set sh = ActiveSheet
   lastR = sh.Range("A" & sh.rows.count).End(xlUp).row
   
   arr = sh.Range("A2:C" & lastR).Value2
   Set dict = CreateObject("Scripting.Dictionary")
   
   For i = 1 To UBound(arr)
        If Not dict.Exists(arr(i, 1)) Then
            dict.Add arr(i, 1), Array(arr(i, 2), arr(i, 3))   'place the strings from columns B and C
        Else
            arrDict = dict(arr(i, 1))                         'extract the array from dict items (it cnnot be modified inside the item)
            arrDict(0) = arrDict(0) & "|" & arr(i, 2)         'place in the array first element the strings collected from B:B
            arrDC = Split(arrDict(1), vbLf)                   'try splitting the second array element (string(s) from C:C)
            If UBound(arrDC) = 0 Then                         'if only one element(for second occurrence):
                    If arrDict(1) <> arr(i, 3) And arr(i, 3) <> "" Then 'not add it to the string if empty or already existing
                        arrDict(1) = arrDict(1) & vbLf & arr(i, 3)
                    End If
                   dict(arr(i, 1)) = arrDict
            ElseIf UBound(arrDC) = -1 Then                      'nothing (arrDict(1) is empty)
                  dict(arr(i, 1)) = Array(arrDict(0), arr(i, 3)) 'place the string of the third column (even empty...)
            Else
                    mtch = Application.match(arr(i, 3), arrDC, 0) 'check unicity of the string from C:C
                    If IsError(mtch) Then                                          'only if not existing:
                        arrDict(1) = arrDict(1) & IIf(arr(i, 3) = "", "", vbLf & arr(i, 3))       'add it to the string to be used in the next step
                    End If
                    dict(arr(i, 1)) = arrDict                                        'put back the changed array in the dictionary item
            End If
        End If
   Next i

   ReDim arrFin(1 To UBound(arr), 1 To 1): k = 1                  'redim the final array and initialize k (used to fill the array)
   For i = 0 To dict.count - 1                                    'iterate between the dictionary keys/items:
        arrDict = dict.Items()(i)                                 'place the item array in an array
        arrDB = Split(arrDict(0), "|")                           'obtain an array of B:B strins from the item first array element
        For j = 0 To UBound(arrDB)   'how many unique keys exists!
                arrFin(k, 1) = arrDB(j) & IIf(arrDict(1) = "", "", vbLf & arrDict(1)) 'build the string of the second column
                k = k   1
        Next j
   Next i
   'drop the processed result near the existing range:
   sh.Range("D2").Resize(UBound(arrFin), 1).Value2 = arrFin
End Sub

If after testing it, nothing inconvenient appears, you may ReDim arrFin to have two columns, the code will load it without any code modification, but its content will be dropped in "B2" resized for two columns (Resize(UBound(arrFin), 2)). In this way, D:D will be errased in the same step.

CodePudding user response:

Combine Unique Values

Sub Extract_unique_values_and_combine_in_adjacent_cells()
    
    Const vDelimiter As String = vbLf
    Const dDelimiter As String = vbLf
    
    Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
    Dim strg As Range: Set strg = ws.Range("A1").CurrentRegion
    Dim srCount As Long: srCount = strg.Rows.Count - 1
    Dim srg As Range: Set srg = strg.Resize(srCount).Offset(1)
    
    Dim sData() As Variant: sData = srg.Value
    
    Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
    dict.CompareMode = vbTextCompare
    
    Dim uKey As Variant
    Dim vKey As Variant
    Dim r As Long
    
    For r = 1 To srCount
        uKey = sData(r, 1)
        If Not dict.Exists(uKey) Then
            Set dict(uKey) = CreateObject("Scripting.Dictionary")
            dict(uKey).CompareMode = vbTextCompare
        End If
        vKey = sData(r, 3)
        If Not IsError(vKey) Then
            If Len(CStr(vKey)) > 0 Then
                dict(uKey)(vKey) = Empty
            End If
        End If
    Next r
            
    Dim vLen As Long: vLen = Len(vDelimiter)
    
    Dim n As Long
    Dim vString As String
            
    For Each uKey In dict.Keys
        For Each vKey In dict(uKey).Keys
            vString = vString & vKey & vDelimiter
        Next vKey
        If Len(vString) > 0 Then
            vString = Left(vString, Len(vString) - vLen)
        End If
        dict(uKey) = vString
        vString = vbNullString
    Next uKey
            
    Dim dData() As String: ReDim dData(1 To srCount, 1 To 1)
            
    Dim dString As String
            
    For r = 1 To srCount
        dString = CStr(sData(r, 2))
        vString = dict(sData(r, 1))
        If Len(dString) = 0 Then
            If Len(vString) > 0 Then
                dData(r, 1) = vString
            End If
        Else
            If Len(vString) > 0 Then
                dData(r, 1) = dString & dDelimiter & vString
            Else
                dData(r, 1) = dString
            End If
        End If
    Next r
    
    srg.Columns(2).Value = dData
    strg.Columns(3).Clear

End Sub

CodePudding user response:

Just for alternatives sake:

enter image description here

Formula in E2:

=TEXTJOIN(CHAR(10),,B2,UNIQUE(FILTER(C$2:C$10,A$2:A$10=A2)))

Or, if available, spill all results in a single go:

=BYROW(A2:B10,LAMBDA(x,TEXTJOIN(CHAR(10),,INDEX(x,2),UNIQUE(FILTER(C$2:C$10,A$2:A$10=INDEX(x,1))))))  
  • Related