Choking when delete large # of rows from a sheet - excel

I have a sub which adds a column from a table to an array (strArr), loops through the array to determine which rows to delete, and adds the row I want to delete to another array (deleteArr). I then loop in reverse order to delete the row. It seems to work fine for a small number of rows, but completely hangs on rows where I have a few thousand matches in deleteArr, even if I let it run forever. Does anyone have an idea what is going on here?
Public Sub DeleteRows(ByVal surveyString As String)
Dim surveyArr() As String
Dim retireArr() As String
Dim strArr() As Variant
Dim deleteArr() As Variant
Dim totalRows As Long
Dim tRange As String
Dim x As Long
Dim y As Long
Dim ws As Worksheet
'Split up fields to delete received from listBox
If surveyString <> "" Then
surveyArr = Split(surveyString, "|")
End If
totalRows = Sheets("Employee").Rows(Rows.Count).End(xlUp).Row
tRange = "L2:L" & CStr(totalRows)
strArr = Sheets("Employee").Range(tRange).Value
x = 0
If surveyString <> "" Then
'determine which rows match and need to be deleted
'the value in deleteArr is the row to delete
For i = 1 To UBound(strArr)
For i2 = 0 To UBound(surveyArr)
If strArr(i, 1) = surveyArr(i2) Then
'resize the array and add the row value of what we want to delete
ReDim Preserve deleteArr(0 To x)
deleteArr(x) = i + 1
x = x + 1
End If
Next i2
Next i
'delete the row in reverse order so no rows are skipped
Set ws = Sheets("Employee")
y = UBound(deleteArr)
For i = totalRows To 2 Step -1
If i = deleteArr(y) Then
ws.Rows(i).EntireRow.Delete
If y > 0 Then
y = y - 1
End If
End If
Next i
End If
End Sub

You could try to union a range of all rows you want to delete, then delete in one shot. Code is untested, hopefully this points you in the right direction.
Public Sub DeleteRows(ByVal surveyString As String)
Dim surveyArr() As String
Dim retireArr() As String
Dim strArr() As Variant
Dim deleteArr() As Variant
Dim totalRows As Long
Dim tRange As String
Dim x As Long
Dim y As Long
Dim ws As Worksheet
Dim UnionRange As Range
'Split up fields to delete received from listBox
If surveyString <> "" Then
surveyArr = Split(surveyString, "|")
End If
totalRows = Sheets("Employee").Rows(Rows.Count).End(xlUp).Row
tRange = "L2:L" & CStr(totalRows)
strArr = Sheets("Employee").Range(tRange).Value
Set ws = Sheets("Employee")
If surveyString <> "" Then
'determine which rows match and need to be deleted
'the value in deleteArr is the row to delete
For i = 1 To UBound(strArr)
For i2 = 0 To UBound(surveyArr)
If strArr(i, 1) = surveyArr(i2) Then
If UnionRange Is Nothing Then
Set UnionRange = ws.Rows(i)
Else
Set UnionRange = Union(UnionRange, ws.Rows(i))
End If
End if
Next
Next
If Not UnionRange Is Nothing Then UnionRange.EntireRow.Delete
End If
End Sub

Related

Add Unique values from a specific range(column) into a listbox

I am trying to add values from a specific range(column) into a listbox. However, the range has blank cells and duplicates that I am trying to get rid of. The following code works (no error msg) and does populate the listbox, but it does not get rid of the duplicates.
Can someone help?
Private Sub UserForm_Initialize()
Dim rang, rang1 As Range
Dim lstrow As Long
Dim list(), ListUniq() As Variant
Dim iRw As Integer
Dim wb As Workbook
Dim ws As Worksheet
Dim x As Long
Set wb = ThisWorkbook
Set ws = wb.Sheets("Paramed Verification Grid")
Set rang = ws.Range("E3").CurrentRegion
lstrow = rang.Rows.Count + 1
Set rang1 = Range("E3:E" & lstrow)
'list = ws.Range("E3:E" & lstrow).SpecialCells(xlCellTypeConstants)
'Resize Array prior to loading data
ReDim list(WorksheetFunction.CountA(rang1))
'Loop through each cell in Range and store value in Array
x = 0
For Each cell In rang1
If cell <> "" Then
list(x) = cell.Value
x = x + 1
End If
Next cell
ListUniq = WorksheetFunction.Unique(list)
ListUniq = WorksheetFunction.Sort(ListUniq)
ProviderListBx.list = ListUniq
End Sub

Highlight Differences across Workbook Ranges VBA

I've managed to compare 3 separate ranges on one workbook with 3 single ranges across 3 workbooks. Right now it's written to just pop up with a message box either letting me know the data is the same or the data is different. What I would like to do is for the macro to not only let me know there are differences, but to also highlight where the differences are to me. I guess this could be done by just highlighting the cells on the first workbook that are different to the other three or I guess it could also be done by pasting the different values on the sheets in question from COL N onward.
Sub Macro1()
Dim varDataMatrix() As Variant
Dim varDataMatrix2() As Variant
Dim varDataMatrix3() As Variant
Dim lngArrayCount As Long
Dim lngArrayCount2 As Long
Dim lngArrayCount3 As Long
Dim rngMyCell As Range
Dim rngMyCell2 As Range
Dim rngMyCell3 As Range
Dim wbWorkbookOne As Workbook
Dim wbWorkbookTwo As Workbook
Dim wbWorkbookThree As Workbook
Dim wbWorkbookFour As Workbook
Application.ScreenUpdating = False
Set wbWorkbookOne = Workbooks("PositionTest.xls")
Set wbWorkbookTwo = Workbooks("ATest.xlsx")
Set wbWorkbookThree = Workbooks("BTest.xlsx")
Set wbWorkbookFour = Workbooks("CTest.xlsx")
'First create an array of the values in the desired range of the first workbook.
For Each rngMyCell In wbWorkbookOne.Sheets("Positions").Range("B3:B6")
lngArrayCount = lngArrayCount + 1
ReDim Preserve varDataMatrix(1 To lngArrayCount)
varDataMatrix(lngArrayCount) = rngMyCell
Next rngMyCell
lngArrayCount = 0 'Initialise variable
'Loop through Array elements
For Each rngMyCell In wbWorkbookTwo.Sheets("A").Range("B2:B5")
lngArrayCount = lngArrayCount + 1
If rngMyCell.Value <> varDataMatrix(lngArrayCount) Then
GoTo QuitRoutinue
End If
Next rngMyCell
For Each rngMyCell2 In wbWorkbookOne.Sheets("Positions").Range("F3:F6")
lngArrayCount2 = lngArrayCount2 + 1
ReDim Preserve varDataMatrix2(1 To lngArrayCount2)
varDataMatrix2(lngArrayCount2) = rngMyCell2
Next rngMyCell2
lngArrayCount2 = 0 'Initialise variable
'Loop through Array elements
For Each rngMyCell2 In wbWorkbookThree.Sheets("B").Range("B2:B5")
lngArrayCount2 = lngArrayCount2 + 1
If rngMyCell2.Value <> varDataMatrix2(lngArrayCount2) Then
GoTo QuitRoutinue
End If
Next rngMyCell2
For Each rngMyCell3 In wbWorkbookOne.Sheets("Positions").Range("J3:J6")
lngArrayCount3 = lngArrayCount3 + 1
ReDim Preserve varDataMatrix3(1 To lngArrayCount3) 'Append the record to the existing array
varDataMatrix3(lngArrayCount3) = rngMyCell3
Next rngMyCell3
lngArrayCount3 = 0 'Initialise variable
For Each rngMyCell3 In wbWorkbookFour.Sheets("C").Range("B2:B5") 'Workbook one range is A10:A15 on 'Sheet2'.
lngArrayCount3 = lngArrayCount3 + 1
If rngMyCell3.Value <> varDataMatrix3(lngArrayCount3) Then
GoTo QuitRoutinue
End If
Next rngMyCell3
'If we get here both datasets have matched.
Set wbWorkbookOne = Nothing
Set wbWorkbookTwo = Nothing
Application.ScreenUpdating = True
Erase varDataMatrix() 'Deletes the varible contents, free some memory
MsgBox "Data is the same.", vbInformation
Exit Sub
Set wbWorkbookOne = Nothing
Set wbWorkbookTwo = Nothing
Application.ScreenUpdating = True
Erase varDataMatrix() 'Deletes the varible contents, free some memory
MsgBox "Data is different.", vbExclamation
End Sub
Highlights differences on Positions sheet and shows values in columns L to N. Uses Application.Transpose to create 1D arrays from a vertical range of cells. Note : Transpose won't work for a non-contiguous range.
Option Explicit
Sub Macro2()
Dim ws(3) As Worksheet, sht, w, n As Long
sht = Array("Positions", "A", "B", "C")
For Each w In Array("PositionTest.xls", "ATest.xlsx", "BTest.xlsx", "CTest.xlsx")
Set ws(n) = Workbooks(w).Sheets(sht(n))
n = n + 1
Next
Dim i As Long, r As Long, diff As Long
Dim rng0 As Range, rngN As Range, a As Range, b As Range
Dim ar0, arN
' compare sheets
For n = 1 To 3
Set rng0 = ws(0).Range("H5:H7,H9:H11,H13:H19,H21:H22").Offset(, (n - 1) * 4) ' H, L, P
Set rngN = ws(n).Range("E3:E18") ' sheet A, B, C
' copy to array
arN = Application.Transpose(rngN)
i = 0
For Each a In rng0
i = i + 1
r = a.Row
' cells on position sheet
Set b = ws(0).Cells(r, "R").Offset(, n) ' diff in col L,M,N
' compare arrays
If a.Value <> arN(i) Then
a.Interior.Color = RGB(255, 255, 0) ' yellow
b.Value = rngN.Cells(i, 1)
diff = diff + 1
Else
a.Interior.Pattern = False
b.Clear
End If
Next
Next
MsgBox diff & " differences", vbInformation
End Sub

Running Total Excel or VBA functionReset Based on Cell value

Hi I have a column of 0's and 1's I want to create a running total of the non 0 values un-till it reaches a cell value of 0. Once it hits zero it should, return an empty cell, reset to 0, and begin again from 1 at the next cell value of 1.
Any help would be appreciated, including what I might want to look at to help.
Editing with current solution:
Ive found this solution that works, how would I go about making this a function instead of using this Sub()?
Sub test()
Dim value As Integer
value = 0
For i = 1 To Range("Table2").Rows.Count
If ThisWorkbook.Worksheets("Sheet1").Range("Table2[Current Col]").Cells(i) = 0 Then
value = 0
Range("Table2[New Column]")(i) = ""
ElseIf ThisWorkbook.Worksheets("Sheet1").Range("Table2[Current Col]").Cells(i) = 1 Then
value = value + 1
Range("Table2[New Column]")(i) = value
End If
Next i
End Sub
Incrementing Groups
Use variables to avoid long unreadable lines.
Option Explicit
Sub test()
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim srg As Range: Set srg = ws.Range("Table2[Current Col]")
Dim drg As Range: Set drg = ws.Range("Table2[New Col]")
Dim sValue As Variant
Dim dValue As Variant
Dim iValue As Long
Dim i As Long
For i = 1 To srg.Cells.Count
' Read from source cell into a variable ('sValue').
sValue = srg.Cells(i).Value
' Test and write result to a variable ('dValue').
If IsNumeric(sValue) Then
If sValue = 1 Then
iValue = iValue + 1
dValue = iValue
End If
Else
iValue = 0
dValue = Empty
End If
' Write from the variable ('dValue') to the destination cell.
drg.Cells(i).Value = dValue
Next i
End Sub
As a UDF:
Function CountUp(rng As Range)
Dim arr, arrOut(), v As Long, i As Long
arr = rng.Columns(1).value
ReDim arrOut(1 To UBound(arr, 1), 1 To UBound(arr, 2))
v = 0
For i = 1 To UBound(arr, 1)
v = IIf(arr(i, 1) = 1, v + 1, 0)
arrOut(i, 1) = v
Next i
CountUp = arrOut
End Function
If your Excel version has the "autospill" feature then you can enter it as a normal function: if not then you need to select the whole output range and enter the formula using Ctrl+Shift+Enter

In Excel Sheet how to Eliminate or Remove, Filter and copy the selected records defined in another sheet using dynamic array list (VBA Module)

I need the experts help as I am new in this area. I am trying to create the Dynamic array Macro for Excel sheet (VBA). In which I want to eliminate (delete or hide) the number of records on the bases of data selected in one particular column (“AlertCount”) in main Sheet “StatusReport” using dynamic array list.
Example : StatusReport (Worksheet)
Filter_Criteria (Worksheet)
Expected output :
All record should display without "1055" and "1056" related Alert Count (Eliminate Record)
But its removed all the records now instead of selected value
My Module as below it display the filter records only but I need to eliminate the selected filter records . VBA Module as below :
Sub DeleteFilter_Data()
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("A:A")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("A" & i + 2)
Next i
Dim Arr01 As Variant
Dim i01 As Integer
Dim i02 As Integer
'Creates a list of everything in Column I, minus everything in Filter_Criteria list
Arr01 = Range("I2", Range("I2").End(xlDown))
For i01 = 1 To UBound(Arr01, 2)
For i02 = 0 To n - 1
If Arr01(i01, 1) = AlertCount_List(i02) Then
Arr01(i01, 1) = ""
End If
Next i02
Next i01
'Turns list into strings (needed for the Filter command).
Dim ListEdited() As String
ReDim ListEdited(1 To UBound(Arr01, 1)) As String
For i01 = 1 To UBound(Arr01, 2)
ListEdited(i01) = Arr01(i01, 1)
Next i01
'Filter command that keeps all entries except any found within the Filter_Criteria Sheet.
Data_sh.UsedRange.AutoFilter 9, ListEdited(), xlFilterValues
End Sub
Please help me out with corrected Macro using dynamic array list.
Thanks
Susheel
I think you are asking to keep all Alert_Counts except for the ones on the Filter_Criteria sheet? The code below does this. Please let me know if I have misunderstood your questions and I will try again.
EDIT 20210630: I have updated the below code.
Sub HideFilter_Data()
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("I:I")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("I" & i + 2)
Next i
Dim Arr01 As Variant
Dim i01 As Integer
Dim i02 As Integer
'Creates a list of everything in Column I, minus everything in Filter_Criteria list
Arr01 = Range("I2", Range("I2").End(xlDown))
For i01 = 1 To UBound(Arr01, 1)
For i02 = 0 To n - 1
If Arr01(i01, 1) = AlertCount_List(i02) Then
Arr01(i01, 1) = ""
End If
Next i02
Next i01
'Turns list into strings (needed for the Filter command).
Dim ListEdited() As String
ReDim ListEdited(1 To UBound(Arr01, 1)) As String
For i01 = 1 To UBound(Arr01, 1)
ListEdited(i01) = Arr01(i01, 1)
Next i01
'Filter command that keeps all entries except any found within the Filter_Criteria Sheet.
Data_sh.UsedRange.AutoFilter 9, ListEdited(), xlFilterValues
'Data_sh.UsedRange.AutoFilter 9, AlertCount_List(), xlFilterValues
'Data_sh.UsedRange.AutoFilter 9, Criteria1:="<> 1056" ‘ This work fine but it's a hard coded value
End Sub
I have got the solution of how to Filter, Eleminate or Hide and Copy the Selected records to another worksheet. The list of Filter data defined in another worksheet and execute the Module by Button press events on the worksheet.
For Eliminate Data case we need to create the 2 list from main worksheet and another one for eliminate the records worksheet. And Compare the both the list and replace the matched case with null or blank in main sheet
Sub HideFilter_Data()
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("A:A")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("A" & i + 2)
Next i
' Create the List of main worksheet
Dim Arr01 As Variant
Dim i01 As Integer
Dim i02 As Integer
Dim r As Integer
Dim r1 As Integer
r = Application.WorksheetFunction.CountA(Data_sh.Range("I:I")) - 2
ReDim StatusCount_List(r + 1) As String
For r1 = 0 To r
StatusCount_List(r1) = Data_sh.Range("I" & r1 + 2)
Next r1
'Creates a list of everything in Column I, minus everything in Filter_Criteria list
Dim str As Variant
Dim cnt As Integer
cnt = 0
' Executing the double loop for comparing both the List and eleminate the match data from the main sheet.
For Each Item In StatusCount_List
For Each subItem In AlertCount_List
If Item = subItem Then
StatusCount_List(cnt) = ""
End If
Next subItem
cnt = cnt + 1
Next Item
Data_sh.UsedRange.AutoFilter 9, StatusCount_List(), xlFilterValues
End Sub
Main Worksheet :
Eliminating Criteria (Hide the records)
Output (Eliminated / Hide/ Remove) as below:
Filter the selected records. Filter list defined in another worksheet.
If we need to select the selected record from the list of another sheet by using dynamic array.
Option Explicit ' Force explicit variable declaration.
Sub Filter_Data()
Dim Data_sh As Worksheet
Dim Filter_Criteria As Worksheet
Dim Output_Sh As Worksheet
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Set Output_Sh = ThisWorkbook.Sheets("Output")
Output_Sh.UsedRange.Clear
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("A:A")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("A" & i + 2)
Next i
Data_sh.UsedRange.AutoFilter 9, AlertCount_List(), xlFilterValues
End Sub
Output :
Copy the selected records to new worksheet. Filter list defined in another worksheet.
Option Explicit ' Force explicit variable declaration.
Sub CopyFilter_Data()
Dim Data_sh As Worksheet
Dim Filter_Criteria As Worksheet
Dim Output_Sh As Worksheet
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Set Output_Sh = ThisWorkbook.Sheets("Output")
Output_Sh.UsedRange.Clear
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("A:A")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("A" & i + 2)
Next i
'Data_sh.UsedRange.AutoFilter 9, Array("1055", "1056"), xlFilterValues
Data_sh.UsedRange.AutoFilter 9, AlertCount_List(), xlFilterValues
Data_sh.UsedRange.Copy Output_Sh.Range("A1")
Data_sh.AutoFilterMode = False
'MsgBox ("Data has been copied")
End Sub
Output :
Please, try the next code. As I said (twice) in my comments it is not possible to filter more than two "not equal to" type conditions. So, it solves the problem as you presented in your question (two conditions):
Sub filterCriteriaArray()
Dim Data_sh As Worksheet, Filter_Criteria As Worksheet, lastR As Long, arrC()
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
lastR = Filter_Criteria.Range("A" & Filter_Criteria.rows.count).End(xlUp).row
arrC = Filter_Criteria.Range("A2:A" & lastR).value
Data_sh.UsedRange.AutoFilter field:=9, Criteria1:="<>" & arrC(1, 1), Operator:=xlAnd, Criteria2:="<>" & arrC(2, 1)
End Sub
Edited:
The next code version uses AdvancedFilter, which allows using more criteria of the type you need, but it does not uses array as criteria. I used a trick, creating a range in a newly add sheet (hidden), based on the array extracted from your criteria sheet:
Sub filterCriteriaFromArray()
Dim Data_sh As Worksheet, Filter_Criteria As Worksheet, crit As Worksheet, lastR As Long, arrCr()
Dim strHeader As String, filtRng As Range, rngCrit As Range, i As Long
strHeader = "Head8" ' "AlertCount" 'important the be the correct header (of I:I column)
Set Data_sh = ActiveSheet 'ThisWorkbook.Sheets("StatusReport")
Set filtRng = Data_sh.Range(Data_sh.Range("A1"), _
Data_sh.cells(Data_sh.UsedRange.rows.count, Data_sh.cells(1, Data_sh.Columns.count).End(xlToLeft).Column))
Set Filter_Criteria = Data_sh.Next 'ThisWorkbook.Sheets("Filter_Criteria")
lastR = Filter_Criteria.Range("A" & Filter_Criteria.rows.count).End(xlUp).row 'last row in Filter_Criteria
arrCr = Filter_Criteria.Range("A2:A" & lastR).value 'put criteria values in the array
On Error Resume Next
Set crit = Sheets("CriteriaSh") 'check if sheets "CriteriaSh" exists
If err.Number <> 0 Then
err.Clear 'if it does not exist, it is created
Set crit = Data_sh.Parent.Sheets.Add(After:=Worksheets(Sheets.count))
crit.Name = "CriteriaSh"
crit.Visible = xlSheetVeryHidden
Else
crit.cells.ClearContents 'if it exists its cells are cleared
End If
On Error GoTo 0
For i = 1 To UBound(arrCr) 'Build the range to be used in AdvancedFilter criteria
crit.cells(1, i).value = strHeader
crit.cells(2, i).value = "<>" & arrCr(i, 1)
Next i
'set the criteria range:
Set rngCrit = crit.Range(crit.Range("A1"), crit.cells(2, UBound(arrCr)))
filtRng.AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=rngCrit, Unique:=False
End Sub

Trying to create a specific loop in excel to get an output

bear with me on this question. I'm pretty sure it'll be easy for those who have knowledge in this field, but I do not know much about VBA or how to create loops in Excel to be creating this formula:
Please review the picture here
What I'm trying to construct is a loop that'll concatenate those numbers.
EX. I want to concatenate in this order A2,"-",B2; A3,"-",B2; A4,"-",B2.....A16,"-",B2
Once everything in A1- A16 is concatenated with B2, I want to move on to concatenating A1-A16 with B3.EX: A2,"-",B3; A3,"-",B3.....A16,"-",B3
I know this is possible because certain loops can be created to go through with this procedure, but I do not know VBA and am not sure if this is possible with just the pre-existing formulas in Excel. Thanks to anyone who helps.
From what you described, it's pretty simple nested loop. Below code will concatenate the way you wanted and store it to column C.
Sub MyConcat()
Const lColA As Long = 1
Const lColB As Long = 2
Const lColTxt As Long = 3 ' concatenated result in Column C
Dim oWS As Worksheet, sTxt As String
Dim lRowA As Long, lRowB As Long, lRowTxt As Long
Set oWS = ThisWorkbook.Worksheets("Sheet1") ' Change this to match yours
lRowA = 1
lRowTxt = 1
oWS.Columns(lColTxt).Clear ' remove previous data on Column C
Do Until IsEmpty(oWS.Cells(lRowA, lColA))
sTxt = ""
lRowB = 2
Do Until IsEmpty(oWS.Cells(lRowB, lColB))
sTxt = oWS.Cells(lRowA, lColA).Text & "-" & oWS.Cells(lRowB, lColB).Text
oWS.Cells(lRowTxt, lColTxt) = sTxt
lRowB = lRowB + 1
lRowTxt = lRowTxt + 1
Loop
lRowA = lRowA + 1
Loop
Set oWS = Nothing
End Sub
EDIT: This should fit in many situations of number of Parent SKUs.
Usable on your data in second image, including another set of "TuTi" and Parent SKUs of different length. Please try understand it, it will be a whole page of explanations.
Private Const lColA As Long = 1
Private Const lColB As Long = 2
Private Const lColTxt As Long = 3 ' concatenated result in Column C
Dim oWS As Worksheet, sGroup As String, lRowCurr As Long, lRowTxt As Long
Sub MyConcat()
Dim oRng As Range, lStopRow As Long
Set oWS = ThisWorkbook.Worksheets("Sheet1") ' Change this to match yours
lRowCurr = 1 ' Current Row index
lRowTxt = 1 ' Results from Row 1
sGroup = ""
With oWS
.Columns(lColTxt).Clear ' remove previous data on Column C
' Row of LastCell in current sheet + 1
lStopRow = .Cells.SpecialCells(xlLastCell).Row + 1
' Row of "Ctrl-Up" from LastCell Row at column A
lStopRow = .Cells(lStopRow, lColA).End(xlUp).Row + 1
' Start processing rows until until StopRow in column A
Do Until lRowCurr = lStopRow
Set oRng = .Cells(lRowCurr, lColA)
If IsGroupCell(oRng) Then
sGroup = oRng.Value ' Stores Group text
ElseIf IsParentSKU(oRng) Then
Call MyConcat2 ' Invoke the mix sub that writes the result in column C
End If
lRowCurr = lRowCurr + 1
Set oRng = Nothing
Loop
End With
Set oWS = Nothing
End Sub
Private Sub MyConcat2()
Dim sTxt As String, oRng As Range
Dim lRowA As Long, lRowB As Long
lRowA = lRowCurr + 1
Set oRng = oWS.Cells(lRowA, lColA)
' Stop mixing the values when it is a Group or Parent SKU row
Do Until IsGroupCell(oRng) Or IsParentSKU(oRng) Or IsEmpty(oRng)
sTxt = ""
lRowB = lRowCurr + 1
' Don't mix if it is a Parent SKU
Do Until IsParentSKU(oWS.Cells(lRowB, lColA)) Or IsEmpty(oWS.Cells(lRowB, lColB))
sTxt = oWS.Cells(lRowA, lColA).Text & "-" & oWS.Cells(lRowB, lColB).Text
oWS.Cells(lRowTxt, lColTxt) = sGroup & "-" & sTxt
lRowB = lRowB + 1
lRowTxt = lRowTxt + 1
Loop
lRowA = lRowA + 1
Set oRng = oWS.Cells(lRowA, lColA)
Loop
lRowCurr = lRowA - 1
Set oRng = Nothing
End Sub
Private Function IsGroupCell(oRng As Range) As Boolean
IsGroupCell = (Not IsNumeric(Left(oRng.Value, 1)) And IsEmpty(oRng.Offset(0, 1)))
End Function
Private Function IsParentSKU(oRng As Range) As Boolean
IsParentSKU = (IsNumeric(oRng.Value) And IsNumeric(oRng.Offset(0, 1).Value))
End Function

Resources