0

I have a list that contains data row objects which have two attributes, an Id and a prentId. An example would like this:

id    ParentId
130   -1
131   130
132   131
133   131
134   132
135   131
136   132
137   136
138   136
139   136
143   136

If the list would be seen from an hierarchical point of view it would look like this:

130
    131
        132
            134
            136
                137
                138
                139
                143
        133
        135

What I would like to do is to create an algorithm that removes all the elements and sub elements which parents are matching a specific ID.

For example, if the chosen ID would be 132 then the elements with id 132, 134, 136, 137, 138, 139, 143 should be removed.

What I have tried until now goes like this

    Private Function RemoveRecursive(ByRef values As List(Of DataRow), value As String) As List(Of DataRow)
        For index As Integer = values.Count - 1 To 0 Step -1
            If Not IsDBNull(values(index)("parent")) AndAlso values(index)("parent") = value Then
                values.RemoveAt(index)
                Return RemoveRecursive(values, value)
                value = values(index)("id")
            End If
        Next
        Return values
    End Function
Rhumborl
  • 16,349
  • 4
  • 39
  • 45
Andy
  • 129
  • 12
  • 1
    I did that because the code can be easily translated from vb.net to c# and the other way around and moreover because c# and vb are using the same datatypes. (Here is a translator http://converter.telerik.com/) – Andy Jan 30 '16 at 11:31
  • I understand and I am well aware of this, but this question is rather generic, therefore I thought it will be no problem to also include the C# tag. Sorry for the inconvenience... – Andy Jan 30 '16 at 11:55

4 Answers4

1

My attempt to solve you problem (c#):

private static void RemoveRecursive(List<DataRow> values, string valueToRemove) {
    DataRow itemToRemove = values.Find(dr => (string)dr["id"] == valueToRemove);
    values.Remove(itemToRemove);
    IEnumerable<string> children = values.Where(dr => (string)dr["ParentId"] == valueToRemove).Select(dr => (string)dr["id"]);
    foreach (string child in children)
        RemoveRecursive(values, child);
}

You can invoke this method in this way:

RemoveRecursive(values, "131");

You can also add some null checkings.

romanoza
  • 4,775
  • 3
  • 27
  • 44
  • Thank you for your post. I have converted your answer to Vb and I have made the necessary modifications to fit my situation. I have also added the null checking and it appears to work. – Andy Jan 30 '16 at 12:48
0

I'm assuming from the data supplied that 133 and 135 also want deleting as they're children of 131?

One way of achieving what you want could be;

  • create empty list of 'tagged' entries
  • iterate through your list, if the ParentID == 131 or the ParentID is in the list of tagged entries, add this node to the tagged entries
  • Remove all tagged objects from your original data set
cgt_mky
  • 196
  • 1
  • 2
  • 7
  • Thank you for the suggestion. It resembles the way @romanoza approached this situation. – Andy Jan 30 '16 at 12:53
0

You are not far off. There are 2 things you have missed:

  1. If the current row has id equal to the search value, not just the parent.
  2. You are setting revalue after the inner call to RemoveRecursive so it is using the same value as the initial one, i.e. it will only check for 132every time.

Number 1 just requires extending your if statement. For number 2 I would just store the current id value in a new variable before removing the row and pass that to the recursive call:

Private Function RemoveRecursive(ByRef values As List(Of DataRow), value As String) As List(Of DataRow)
    For index As Integer = values.Count - 1 To 0 Step -1
        If Not IsDBNull(values(index)("parent")) AndAlso (values(index)("parent") = value OrElse values(index)("id") = value) Then
            Dim newValue as integer = values(index)("id").ToString()
            values.RemoveAt(index)
            RemoveRecursive(values, newValue)
        End If
    Next
    Return values
End Function

Working .NETFiddle

Rhumborl
  • 16,349
  • 4
  • 39
  • 45
0

A general example of parent / child that might prove helpful. This is optimistic in that there is no error checking. Notice that the Add and Remove methods are both overloaded.

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim root As New PC(1)
    root.AddChild(1, 10)
    root.AddChild(1, 11)
    root.AddChild(1, 12)
    root.AddChild(11, 110)
    root.AddChild(11, 111)
    root.AddChild(110, 1100)
    root.AddChild(110, 1101)
    root.AddChild(1100, 11000)
    root.AddChild(1100, 11001)
    root.AddChild(1101, 11010)
    root.RemoveChild(1100)
End Sub

Class PC 'parent child
    Dim myID As Integer
    Dim parentPC As PC = Nothing
    Dim children As New List(Of PC)

    Public Sub New(ID As Integer)
        Me.myID = ID
    End Sub

    Public Function AddChild(parent As Integer, child As Integer) As PC
        Dim rv As PC
        rv = Me.AddChild(Me, parent, child)
        Return rv
    End Function

    Private Function AddChild(aPC As PC, parent As Integer, child As Integer) As PC
        Dim rv As PC
        Dim addPC As PC
        If aPC.myID = parent Then
            addPC = New PC(child)
            addPC.children = New List(Of PC)
            addPC.parentPC = aPC
            aPC.children.Add(addPC)
            Return addPC
        Else
            For Each chld As PC In aPC.children
                rv = chld.AddChild(chld, parent, child)
                If rv IsNot Nothing Then
                    Exit For
                End If
            Next
        End If
        Return rv
    End Function

    Public Function RemoveChild(child As Integer) As Boolean
        Dim rv As Boolean = False
        rv = Me.RemoveChild(child, Me)
        Return rv
    End Function

    Private Function RemoveChild(child As Integer, aPC As PC) As Boolean
        Dim rv As Boolean = False
        If aPC.myID = child Then
            For x As Integer = aPC.children.Count - 1 To 0 Step -1
                Dim chld As PC = aPC.children(x)
                Me.RemoveChild(chld.myID, chld)
            Next
            aPC.parentPC.children.Remove(aPC)
            aPC.children = Nothing
            aPC = Nothing
            rv = True
        Else
            For Each chld As PC In aPC.children
                rv = Me.RemoveChild(child, chld)
                If rv Then
                    Exit For
                End If
            Next
        End If
        Return rv
    End Function
End Class
dbasnett
  • 11,334
  • 2
  • 25
  • 33