Sort/Arrange Rows based on Cell Value in Range VBA

crp

New Member
Joined
Jul 30, 2021
Messages
18
Office Version
  1. 365
Platform
  1. Windows
I have a series of data compiled into one excel sheet. The order of the data depends on each row's association to other rows, based on their LinkTo values. Below is an example of before, and how I need the data to be organized. I'd like for this to be automated in VBA.

Example Raw Data
IDCategoryLinkTo
1Fruit
2Onion4
3Apple1
4Vegetable
5Banana1
6Red Apple3

Example Desired Result
IDCategoryLinkTo
1Fruit
3Apple1
6Red Apple3
5Banana1
4Vegetable
2Onion4
 
2. All of the IDs in column A share the first 5 digits ("12345"). It is possible for there to be up to 3 additional digits following before the "-", after which it could be up to 6 digits ("12345XXX-XXXXXX"). I noticed in your code how you were distinguishing and counting digits following the "-".

So the LinkTo ids could be up to 8 digits preceding the "-" and up to 6 digits after it? Given this variability there is no way without a delimiter that I know of to differentiate the LinkTo ids since your data could be "12345-67891234-1. How do I know where to break the first LinkTo id? Is it 12345-6 and 7891234-1 or is it 12345-67 and 891234-1? You see where I'm going? If there were a delimiter such as a comma or even a space that would be extremely helpful.
3. The only differentiation between "Fruit" and "Tomato" is the category level. "Tomato" or similar descriptor can link up to "Fruit", but "Fruit" cannot link up to "Tomato". It is possible for any level (Fruit, Apple, Green Apple) to have multiple LinkTos and for some to not be found, but again, they can only link up and should show traceability from the lowest to the highest level (Green Apple is an Apple, which is a Fruit.) You are correct that Fruit should not be duplicated. Unfortunately there are no other descriptors in the data that distinguish Fruit from Tomato/Apple/etc. If it would be easier, we could add another column at the end and identify this? Column W: Fruit/Vegetable = X, Tomato/Apple/etc = Y, Green Apple/etc = Z ?
Assuming that the "fruit and vegetables" data is not ACTUAL data and that there are most likely MANY categories like "Fruit" that will not have corresponding LinkTo ids correct? If that is the case, then yes, I need some way of distinguishing items from categories. If an "X" in column W is something you can do easily that would be great. Any item with an "X" in column W will be treated as a category and if there are more than one LinkTo ids the item will NOT be duplicated.

This opens up another can of worms however. What if there is a category that is a sub-category say like "Apples" that has more than one LinkTo id and matches are found for both? Should this item be duplicated and linked to both LinkTo ids? If that is the case, that causes the LinkTo ids in Column A to have duplicates for categories. What should happen to that categories children? Should they all be duplicated and placed under both (or more) locations? This is spiraling out of control.
 
Upvote 0

Excel Facts

Who is Mr Spreadsheet?
Author John Walkenbach was Mr Spreadsheet until his retirement in June 2019.
I understand and I wish I could provide the raw data.
Having the LinkTo IDs on a new row within that cell does not space them out? I could look at a space/comma as an option, but not sure if it'll be possible.

I could easily add a category identifier to Column W to break out the 3 different levels. (Fruit/Vegetable: X, Apple/Etc: Y, Green Apple/Etc: Z) Correct, no "X" category would be duplicated.

If the sub-category finds multiple LinkTos, it should be placed under each corresponding category. Similarly, if the "Z" level item falls under multiple sub-categories (Y), they too should be placed under each corresponding sub-category. So it is possible to have duplicates created of the Apple (Y) and Green Apple (Z) levels, but not the Fruit (X) level.

If this is becoming too complex without having the raw data I understand. I appreciate your time thus far.
 
Upvote 0
Ok, so I figured out how to determine where the LinkTo ids break. Basically by entering the LinkTo ids using "Alt+Enter" to put them on a new row you are inserting Char(10) or "Line Break". I can key on that as the "delimiter" to break and search for the LinkTo ids.

The "X", "Y", and "Z" designations will help tremendously.

The last thing I will need to work out will be creating duplicates of the "Y" and "Z" level items if they match to multiple "X" or "Y" levels respectively.

Not sure what your time frame is on this, but this may take me a day or two to work out.
 
Upvote 0
Ok, so I figured out how to determine where the LinkTo ids break. Basically by entering the LinkTo ids using "Alt+Enter" to put them on a new row you are inserting Char(10) or "Line Break". I can key on that as the "delimiter" to break and search for the LinkTo ids.

The "X", "Y", and "Z" designations will help tremendously.

The last thing I will need to work out will be creating duplicates of the "Y" and "Z" level items if they match to multiple "X" or "Y" levels respectively.

Not sure what your time frame is on this, but this may take me a day or two to work out.
Excellent. I will build in the X, Y, Z designations into the data. No problem with the timeline. Thanks again.
 
Upvote 0
Ok, I think I solved it all.

Assumptions
1. The "Level" is designated in column W with either and x, y, or z
a. Top Level items are designated with an x. These items will NEVER be duplicated and should NOT have any LinkID matches in the data
b. Mid Level items are designated with a y. These items MAY be duplicated if they are linked to more than one top level or x item.
c. Lowest Level items are designated with a z. These items MAY be duplicated if they are linked to more than one mid level or y item.
d. All levels can have children linked to them.
2. Any item that is linked to multiple parent items will be duplicated as many times as there are parents and linked to each parent
3. Any item that does not have a valid LinkTo id will not be processed and will be orphaned (not under any parent) at the top of the list.
4. The data range is from A1 to X last row.


VBA Code:
Sub LinkSort()
    Dim lRow As Long, remRow As Long, i As Long, j As Long
    Dim str As String, strFirstAddress As String
    Dim rng As Range, c As Range
    Dim idFound As Boolean
    Dim idArr As Variant
    Dim ws As Worksheet
    
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    
    Set ws = ActiveSheet
     
    'Get last row of data in column "A"
    lRow = Cells(Rows.Count, 1).End(xlUp).Row
    remRow = lRow
    
    'Sort data twice by col W.  Once descending to push all x's to the bottom and then again to sort remaining y's and z's ascending and alpha
    i = 1
    Do
        Set rng = Range(Cells(2, 1), Cells(remRow, 24))
        With ws
            .Sort.SortFields.Clear
            If i < 2 Then
                .Sort.SortFields.Add2 Key:=Range("W2:W" & remRow) _
                    , SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
            Else
                .Sort.SortFields.Add2 Key:=Range("W2:W" & remRow) _
                    , SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
            End If
            .Sort.SortFields.Add2 Key:=Range("H2:H" & remRow) _
                , SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
        End With
        With ws.Sort
            .SetRange rng
            .Apply
        End With
        i = i + 1
        
        If i <= 2 Then
            Set c = Range("W2:W" & lRow).Find("x", , xlValues, xlWhole)
            remRow = c.Row - 1
        End If
    Loop While i <= 2
    
    'Set r to the data in column "A" so we can search for LinkTo ID's
    Set rng = Range(Cells(2, 1), Cells(Cells(Rows.Count, 1).End(xlUp).Row, 1))
    
    
    i = 2
    idFound = False
    
    While i <= remRow
'        Debug.Print remRow
'        Debug.Print i
        idArr = Split(Cells(i, 14).Value, Chr(10))
        For j = 0 To UBound(idArr)
            With rng
                Set c = .Find(idArr(j), , xlValues, xlWhole)
                If Not c Is Nothing Then
                    If c.Row = i Then GoTo SkipError
                    idFound = True
                    strFirstAddress = c.Address
                    Do
                        Rows(i & ":" & i).Copy
                        Rows(c.Row + 1 & ":" & c.Row + 1).Insert Shift:=xlDown
                        Set rng = Range(Cells(2, 1), Cells(Cells(Rows.Count, 1).End(xlUp).Row, 1))
                        Set c = .FindNext(c)
                    Loop Until c.Address = strFirstAddress
                End If
            End With
            strFirstAddress = ""
SkipError:
        Next j
        If Not idFound Then
            i = i + 1
        Else
            Rows(i & ":" & i).EntireRow.Delete
            remRow = remRow - 1
        End If
        idFound = False
    Wend
    
    Application.EnableEvents = True
    Application.ScreenUpdating = True
End Sub
 
Upvote 0
Solution
Ok, I think I solved it all.

Assumptions
1. The "Level" is designated in column W with either and x, y, or z
a. Top Level items are designated with an x. These items will NEVER be duplicated and should NOT have any LinkID matches in the data
b. Mid Level items are designated with a y. These items MAY be duplicated if they are linked to more than one top level or x item.
c. Lowest Level items are designated with a z. These items MAY be duplicated if they are linked to more than one mid level or y item.
d. All levels can have children linked to them.
2. Any item that is linked to multiple parent items will be duplicated as many times as there are parents and linked to each parent
3. Any item that does not have a valid LinkTo id will not be processed and will be orphaned (not under any parent) at the top of the list.
4. The data range is from A1 to X last row.


VBA Code:
Sub LinkSort()
    Dim lRow As Long, remRow As Long, i As Long, j As Long
    Dim str As String, strFirstAddress As String
    Dim rng As Range, c As Range
    Dim idFound As Boolean
    Dim idArr As Variant
    Dim ws As Worksheet
   
    Application.ScreenUpdating = False
    Application.EnableEvents = False
   
    Set ws = ActiveSheet
    
    'Get last row of data in column "A"
    lRow = Cells(Rows.Count, 1).End(xlUp).Row
    remRow = lRow
   
    'Sort data twice by col W.  Once descending to push all x's to the bottom and then again to sort remaining y's and z's ascending and alpha
    i = 1
    Do
        Set rng = Range(Cells(2, 1), Cells(remRow, 24))
        With ws
            .Sort.SortFields.Clear
            If i < 2 Then
                .Sort.SortFields.Add2 Key:=Range("W2:W" & remRow) _
                    , SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
            Else
                .Sort.SortFields.Add2 Key:=Range("W2:W" & remRow) _
                    , SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
            End If
            .Sort.SortFields.Add2 Key:=Range("H2:H" & remRow) _
                , SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
        End With
        With ws.Sort
            .SetRange rng
            .Apply
        End With
        i = i + 1
       
        If i <= 2 Then
            Set c = Range("W2:W" & lRow).Find("x", , xlValues, xlWhole)
            remRow = c.Row - 1
        End If
    Loop While i <= 2
   
    'Set r to the data in column "A" so we can search for LinkTo ID's
    Set rng = Range(Cells(2, 1), Cells(Cells(Rows.Count, 1).End(xlUp).Row, 1))
   
   
    i = 2
    idFound = False
   
    While i <= remRow
'        Debug.Print remRow
'        Debug.Print i
        idArr = Split(Cells(i, 14).Value, Chr(10))
        For j = 0 To UBound(idArr)
            With rng
                Set c = .Find(idArr(j), , xlValues, xlWhole)
                If Not c Is Nothing Then
                    If c.Row = i Then GoTo SkipError
                    idFound = True
                    strFirstAddress = c.Address
                    Do
                        Rows(i & ":" & i).Copy
                        Rows(c.Row + 1 & ":" & c.Row + 1).Insert Shift:=xlDown
                        Set rng = Range(Cells(2, 1), Cells(Cells(Rows.Count, 1).End(xlUp).Row, 1))
                        Set c = .FindNext(c)
                    Loop Until c.Address = strFirstAddress
                End If
            End With
            strFirstAddress = ""
SkipError:
        Next j
        If Not idFound Then
            i = i + 1
        Else
            Rows(i & ":" & i).EntireRow.Delete
            remRow = remRow - 1
        End If
        idFound = False
    Wend
   
    Application.EnableEvents = True
    Application.ScreenUpdating = True
End Sub
I’ve been trying to run this, however it seems to be taking a long time. I’ve tried canceling it a few times and at that time it shows several thousand rows of data. The original data set is around 400 rows.
 
Upvote 0
I encountered this by a data error. Check to make sure that you don't have an item (probably the one that is repeating so much) that has a LinkTo id that is it's own LinkTo id.
 
Upvote 0
I encountered this by a data error. Check to make sure that you don't have an item (probably the one that is repeating so much) that has a LinkTo id that is it's own LinkTo id.
That was it. Everything looks to be repeating and tracing to their respective categories. Thank you very much for your assistance.
 
Upvote 0

Forum statistics

Threads
1,223,894
Messages
6,175,250
Members
452,623
Latest member
Techenthusiast

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