Comparing trees in a treeview - vb.net

I want to be able to iterate through a tree and compare nodes one tree to the nodes in another tree of the same format.
EX: There are five categories.
1 - 5. Are all static and identical between both trees.
In 1. All static values. So i need to just compare the values of the nodes there.
In 2. This is comes from a KVP object and so these can be different in terms of number of nodes and their single children.
In 3. Same as 2 but there are 5 children
In 4-5 are the same as 1.
I was thinking of looping in the trees and having a different for loop for each category and checking the nodes contain the same values.
The dynamic one for 2 and 3 I would do something of the same, but check the size first then loop through if the size is the same.
If i find a difference I would change that nodes back color.
Is this the best method for me?

I don't think you have much of a choice but to do different comparison algorithms based on the data that in the tree since it seems that your nodes are symantically different depending on where they are in the tree.

Since I knew the depth...i did this:
For h As Integer = 0 To tree1.Nodes(0).Nodes.Count - 1
For i As Integer = 0 To tree1.Nodes(0).Nodes(h).Nodes.Count - 1
For j As Integer = 0 To tree1.Nodes(0).Nodes(h).Nodes(i).Nodes.Count - 1
If tree1.Nodes(0).Nodes(h).Nodes(i).Text <> _
tree2.Nodes(0).Nodes(h).Nodes(i).Text Then
tree2.Nodes(0).Nodes(h).Nodes(i).BackColor = Color.Red
tree2.Nodes(0).Nodes(h).Nodes(i).Nodes(j).Expand()
tree2.Nodes(0).Nodes(h).Nodes(i).Expand()
tree2.Nodes(0).Nodes(h).Expand()
tree2.Nodes(0).Expand()
tree1.Nodes(0).Nodes(h).Nodes(i).BackColor = Color.Red
tree1.Nodes(0).Nodes(h).Nodes(i).Nodes(j).Expand()
tree1.Nodes(0).Nodes(h).Nodes(i).Expand()
tree1.Nodes(0).Nodes(h).Expand()
tree1.Nodes(0).Expand()
ElseIf tree1.Nodes(0).Nodes(h).Nodes(i).Nodes(j).Text <> _
tree2.Nodes(0).Nodes(h).Nodes(i).Nodes(j).Text Then
tree2.Nodes(0).Nodes(h).Nodes(i).Nodes(j).BackColor = Color.Red
tree2.Nodes(0).Nodes(h).Nodes(i).BackColor = Color.Red
tree2.Nodes(0).Nodes(h).Nodes(i).Nodes(j).Expand()
tree2.Nodes(0).Nodes(h).Nodes(i).Expand()
tree2.Nodes(0).Nodes(h).Expand()
tree2.Nodes(0).Expand()
tree1.Nodes(0).Nodes(h).Nodes(i).Nodes(j).BackColor = Color.Red
tree1.Nodes(0).Nodes(h).Nodes(i).BackColor = Color.Red
tree1.Nodes(0).Nodes(h).Nodes(i).Nodes(j).Expand()
tree1.Nodes(0).Nodes(h).Nodes(i).Expand()
tree1.Nodes(0).Nodes(h).Expand()
tree1.Nodes(0).Expand()
End If
Next
Next
Next

Related

Remove elements in an arraylist that exist in another arraylist

i have 2 array list, dateListDead and dateListNotMinggu. Both is DateTime List of Array. This is the ilustration of the date value in list of array
The arrayList value
its supposed to remove specific element that exist in other array list.
so far i tried, this code it's not working.
Dim d, x As Integer
For x = 0 To dateListDead.Count - 1
For d = 0 To dateListNotMinggu.Count - 1
If dateListNotMinggu(d) = dateListDead(x) Then
dateListNotMinggu.RemoveAt(d)
End If
Next
Next
the error is : index out of range. how could it be ? i define the parameter of end looping base on arraylist.count -1
The main is that you are using a For loop from the first index to the last index but you don't account for the change of index when you remove a value. If there might be multiple values then you should start and the end rather than the beginning. In that case, removing an item won't affect the indexes of the items you are yet to test. If there can only be one match then you should be exiting the loop when you find one.
Either way, while you don't have to, I would suggest using a For Each loop on the outside. If you want to perform an action for each item in a list then that's exactly what a For Each loop is for. Only use a For loop if you need to use the loop counter for something other than accessing each item in turn.
For multiple matches:
For Each dateDead As Date In dateListDead
For i = dateListNotMinggu.Count - 1 To 0 Step -1
If CDate(dateListNotMinggu(i)) = dateDead Then
dateListNotMinggu.RemoveAt(i)
End If
Next
Next
For a single match:
For Each dateDead As Date In dateListDead
For i = 0 To dateListNotMinggu.Count - 1
If CDate(dateListNotMinggu(i)) = dateDead Then
dateListNotMinggu.RemoveAt(i)
Exit For
End If
Next
Next
Note that I have also cast the Date values as that type for comparison, which is required with Option Strict On. Option Strict is Off by default but you should always turn it On because it will help you write better code by focusing on data types.
Also, the code above would work with a List(Of Date) as well as an ArrayList but the casts would not be required with a List(Of Date). That's one of the advantages of using a generic List(Of T) over an ArrayList, which paces no restrictions on what it can contain.
If you really must use a For loop because that's what your homework assignment says then it would look like this:
For i = 0 To dateListDead.Count - 1
For j = dateListNotMinggu.Count - 1 To 0 Step -1
If CDate(dateListNotMinggu(j)) = CDate(dateListDead(i)) Then
dateListNotMinggu.RemoveAt(j)
End If
Next
Next
and this:
For i = 0 To dateListDead.Count - 1
For j = 0 To dateListNotMinggu.Count - 1
If CDate(dateListNotMinggu(j)) = CDate(dateListDead(i)) Then
dateListNotMinggu.RemoveAt(j)
Exit For
End If
Next
Next
Note that it is convention to use i as a first option for a loop counter, then j for the first nested loop, then k for the second nested loop. You should only use something else if you have good reason to do so. Remember that the loop counter doesn't represent the value in the list but rather its index. That's why you use i for index and not d for date or the like.
EDIT:
As per Jimi's comment below, the way this would usually be tackled is with a simple LINQ query. If you were using LINQ then you definitely wouldn't be using an ArrayList but rather a List(Of Date). In that case, the code would look like this:
dateListNotMinggu = dateListNotMinggu.Except(dateListDead).ToList()
If you were completely insane and wanted to use LINQ and ArrayLists then this would work:
dateListNotMinggu = New ArrayList(dateListNotMinggu.Cast(Of Date)().
Except(dateListDead.Cast(Of Date)()).
ToArray())
Take note that, as I replied in the comments, using LINQ will generate a new list, rather than changing the existing one.

check if string contains specific integer

I have a grid view that has a column containing strings (Middle column).
On the rowDataBound event I want to loop through the column looking for the integer it contains and then display a value in the first column.
I know that the integer range will be 1 to 63 so I can use a FOR loop to loop through the numbers. Here is what I have so far.
For x As Integer = 1 To 63
If CType(e.Row.Cells(2).FindControl("lblTagName"), Label).Text Then
End If
Next
The problem I am having is using contains. I cant use the following as it would also be true for the number 1, 10, 11 etc when x = 1.
For x As Integer = 1 To 63
If CType(e.Row.Cells(2).FindControl("lblTagName"), Label).Text.Contains(x) Then
End If
Next
How do I make sure it only gets one result per number? i.e x = 6 would return UMIS.75OPTR6GROSSMARGIN.F_CV and not all the other strings that contain the number 6.
UPDATE - based on some answers I may not of explained this very well. I want to loop through the gridview and if the number 1 is found and only the number 1 in the second column, not 10 etc then I want to display "Run 1" in the first column. So when x = 10 it will show "Run 10" and so on.
UPDATE 2 - its definatley my explanation, apologies.
The resultant grid view would look like this.
The order of the second column is not set and is not in order.
You'd have to check the entire text of the label to determine whether it is only 1, and not 10, 11, 12, 13, ... as well.
Also, in this case you should use DirectCast rather than CType. CType is only used when converting to different types that include conversion operators, here you are always dealing with a label.
For x As Integer = 1 To 63
If String.Equals(DirectCast(e.Row.Cells(2).FindControl("lblTagName"), Label).Text, "UMIS.75OPTR" & x & "GROSSMARGIN.F_CV", StringComparison.OrdinalIgnoreCase) Then
'Do your stuff.
End If
Next
You might want to think if doing it the other way around. Get the list of numbers in your string with a regular expression match.
Dim s As String = "asd12asdasd.sdf3sdf"
For Each m As System.Text.RegularExpressions.Match In System.Text.RegularExpressions.Regex.Matches(s, "[\d]*")
If m.Success AndAlso Not String.IsNullOrEmpty(m.Value) Then
' m.Value
End If
Next
With that list of number, you can check if it's between 1 and 63.
If your string have the same suffix/prefix, just remove them to show you what the number is.
Dim s As String = "UMIS.75OPTR12GROSSMARGIN.F_CV"
Dim number As String = s.Replace("UMIS.75OPTR", "").Replace("GROSSMARGIN.F_CV", "")
Go backwards in a Do Until Loop:
Dim bolFoundMatch As Boolean = False
Dim intCursor As Integer = 63
Do Until (bolFoundMatch OrElse intCursor = 0)
If CType(e.Row.Cells(2).FindControl("lblTagName"), Label).Text.Contains(intCursor) Then
'Anything you want to do when you find your match.
'This will ensure your loop exits.
bolFoundMatch = True
End If
intCursor -= 1
Loop

How Do I Count When Numbers Do Not Show Up

I have numbers from 1 to 25, four numbers will show up daily. I need to put a +1 on each of the four numbers and need to put a -1 on each of the 21 numbers didn't show up.
The four numbers that come up daily will be inputted in four different text boxes. The count being positive or negative needs to go on 25 separate text boxes labeled 1 thru 25.
I have tried "if textbox <> number, then count -= 1" but I get a count of -4 because it doesn't see the number in any of the four text boxes.
I only need a daily count not a textbox count. Sorry I don't have any code started and would greatly appreciate if someone can point me in the right direction. I'm doing this on Visual Studio 2012.
Thank you all for responding. Here is some code I've started but the count is not correct. My four input text boxes are in GroupBox2. Four numbers from 1 to 25 will draw daily like a lottery. The four numbers drawn will have a value of +1 each all others -1. I need to find the age of each number 1 thru 25. If a number has a +3 then that means that number has drawn 3 consecutive days. If a number has a -15 then that means that number has not drawn for the past 15 days.
Dim tb As New TextBox
Dim ctrl As Control
Dim Counter As Integer
For Each ctrl In GroupBox2.Controls
tb = ctrl
If tb.Text = 1 Then
Counter += 1
ElseIf tb.Text <> 1 Then
Counter -= 1
TextBox464.Text = Counter
End If
If tb.Text = 2 Then
Counter += 1
ElseIf tb.Text <> 2 Then
Counter -= 1
TextBox463.Text = Counter
End If
If tb.Text = 3 Then
Counter += 1
ElseIf tb.Text <> 3 Then
Counter -= 1
TextBox462.Text = Counter
End If
If tb.Text = 4 Then
Counter += 1
ElseIf tb.Text <> 4 Then
Counter -= 1
TextBox461.Text = Counter
End If
Next
We will need more information about how your going to approach it to be able to help you further, but as for your problem with this If Textbox <> number Then count -= 1 you can use something like this since your only going to be having numbers on the textboxes If Cint(Textbox.Text) <> number then count -= 1 since your using just Textbox its attempting to evaluate it as a control and not the property that your looking for, you need to read from its .Text Property, However since its evaluated as a String and not an Integer it will throw an error exception, thats why the Cint() is included (This may also be used to convert it to integer Ctype(number, Integer) Not sure if there is an execution speed difference or not, however Cint() is a faster way of writing it.) it will try and convert the String into an Integer and when its converted into an integer it can be evaluated like one using <>. No one is going to write a whole solution out for you, but while you attempt to create it yourself and more information can be added we are more than happy to help you with problems along the way.

Error code using Linq to add to a variable

I'll start with the problem, and then describe how the procedure works.
I'm getting a an error saying:
Value of type 'System.Collections.Generic.IEnumerable(Of LunchMoneyGame.LunchMoneyMainForm.Group)' cannot be converted to 'LunchMoneyGame.LunchMoneyMainForm.Group'.
The blue underline pinpointing the problem is on Dim obj As LunchMoneyGame.LunchMoneyMainForm.Group = From r In temp Where r.ID = Number Select r on the right side of the operator.
I know it is something to do with explicitness
--
This is for a table top card game i am converting to a text game.
The goal of this sub procedure is to subtract a card from a players hand and add it to the discard pile. Once added to the discard pile I then call the discard pile to move the cards from the discard pile to the card deck.
The problem starts with the number generator.
What I'm doing is randomly picking a card from the deck. by generating a the card id of a card in the deck. Once the ID is generated using linq the number generated then takes a card, then adds the card to the players inventory (quantity integer), and subtracts from the deck.
T represents whose turn it is. Currently for testing purposes I have coded it for only 2 players in terms of the T variable.
Private Sub GrabFromDeckAndDiscard()
Dim CardCheckBoxInteger As Integer
'ReDeclare CheckBox Array for Private sub
Dim CardCheckBoxArray() As CheckBox = {CardCheckBox1, CardCheckBox2, CardCheckBox3, CardCheckBox4, CardCheckBox5}
'Discard
Select Case T
Case 0
Player1HandGroup(NumberArray(Checks)).QuantityInteger -= 1
Case 1
Player1HandGroup(NumberArray(Checks)).QuantityInteger2 -= 1
Case 2
Player1HandGroup(NumberArray(Checks)).QuantityInteger3 -= 1
Case 3
Player1HandGroup(NumberArray(Checks)).QuantityInteger4 -= 1
Case 4
Player1HandGroup(NumberArray(Checks)).QuantityInteger5 -= 1
End Select
'Add Card to Discard Pile
DiscardGroup(NumberArray(Checks)).QuantityInteger += 1
'Shuffle Deck from Discard Pile if Deck is out of cards
Call DiscardPile()
'Reset Number Generator, unless weapon isn't discard
Dim temp As IEnumerable(Of LunchMoneyGame.LunchMoneyMainForm.Group) = From r In DeckGroup Where r.QuantityInteger > 0 Select r
If temp IsNot Nothing AndAlso temp.count > 0 Then
Number = (temp(Rnd.Next(0, temp.Count)).ID)
' ** Edit **: This will give you the actual object to be manipulated
Dim obj As LunchMoneyGame.LunchMoneyMainForm.Group = (From r In temp Where r.ID = Number Select r).Single
Dim PlayerQuantitySubtractionInteger As Integer
For PlayerQuantitySubtractionInteger = ChecksDynamicA To ChecksDynamicB
' ** Edit **
obj.QuantityInteger -= 1
'Select the Player depending value of T
Select Case T
Case 0
Player1HandGroup(Number).QuantityInteger += 1
Case 1
Player1HandGroup(Number).QuantityInteger2 += 1
Case 2
Player1HandGroup(Number).QuantityInteger3 += 1
Case 3
Player1HandGroup(Number).QuantityInteger4 += 1
Case 4
Player1HandGroup(Number).QuantityInteger5 += 1
End Select
CardTypeArray(PlayerQuantitySubtractionInteger) = Player1HandGroup(Number).CardType
CardCheckBoxArray(TextBoxInteger).Text = Player1HandGroup(Number).CardNameString
NumberArray(PlayerQuantitySubtractionInteger) = Number
Next PlayerQuantitySubtractionInteger
End If
'Switch to next player
Select Case T
Case 0
For CardCheckBoxInteger = 0 To 4
CardCheckBoxArray(CardCheckBoxInteger).Text = Player1HandGroup(NumberArray(CardCheckBoxInteger + 5)).CardNameString
Next
T += 1
Case 1
If GameSize = 2 Then
For CardCheckBoxInteger = 0 To 4
CardCheckBoxArray(CardCheckBoxInteger).Text = Player1HandGroup(NumberArray(CardCheckBoxInteger)).CardNameString
Next CardCheckBoxInteger
T -= 1
End If
If GameSize > 2 Then
T += 1
End If
Case 2
Case 3
Case 4
End Select
Label1.Text = T.ToString
'Clear Check Boxes when turn is finished
For CardCheckBoxInteger = 0 To 4
CardCheckBoxArray(CardCheckBoxInteger).Checked = False
Next
'Turn off play button
PlayButton.Enabled = False
End Sub
From r In temp
Where r.ID = Number
Select r
Returns a collection of everything which matches the Where clause (see this for the MSDN documentation). .NET represents this as an IEnumerable, which means a collection of things which can be iterated over (e.g. using a For loop or something like that).
There is no automatic concept that you'll only get one record returned even though your condition is an = (there may also be conditions, broadly, where no elements are returned such as when nothing matches the equality condition or where multiple things match the equality condition).
In order to indicate that you only want one element you can use the First, Single, FirstOrDefault or SingleOrDefault methods.
For example:
Dim obj As LunchMoneyGame.LunchMoneyMainForm.Group = (From r In temp Where r.ID = Number Select r).Single()
Which will give you an object of type LunchMoneyGame.LunchMoneyMainForm.Group, which I think you're looking for in this case.
As an aside, it's not bad to know the difference between the methods I've outlined above. In my lay perspective (as I'm no expert):
First returns the first item in the collection or throws an Exception if the collection is empty
FirstOrDefault returns the first item in the collection or null if empty
Single returns the only item in the collection or throws an Exception if the collection does not have exactly one item
SingleOrDefault returns the first item in the collection, null if the collection is empty and throws an exception if the collection has more than 1 element
So in your case, since you should only match on a single item with the = condition I would suggest using Single or SingleOrDefault
PS. Sorry, when I say null above, that'll be Nothing in VB.NET speak

SSIS - Script Component, Split single row to multiple rows (Parent Child Variation)

Thanks in advance for your help. I'm in need of help on writing SSIS script component to delimit single row to multiple rows. There were many helpful blog and post I looked at below:
http://beyondrelational.com/ask/public/questions/1324/ssis-script-component-split-single-row-to-multiple-rows-parent-child-variation.aspx
http://bi-polar23.blogspot.com/2008/06/splitting-delimited-column-in-ssis.html
However, I need a little extra help on coding to complete the project. Basically here's what I want to do.
Input data
ID Item Name
1 Apple01,02,Banana01,02,03
2 Spoon1,2,Fork1,2,3,4
Output data
ParentID ChildID Item Name
1 1 Apple01
1 2 Apple02
1 3 Banana01
1 4 Banana02
1 5 Banana03
2 1 Spoon1
2 2 Spoon2
2 3 Fork1
2 4 Fork2
2 5 Fork3
2 6 Fork4
Below is my attempt to code, but feel free to revise whole if it's illogic. SSIS Asynchronous output is set.
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
Dim posID As Integer, childID As Integer
Dim delimiter As String = ","
Dim txtHolder As String, suffixHolder As String
Dim itemName As String = Row.ItemName
Dim keyField As Integer = Row.ID
If Not (String.IsNullOrEmpty(itemList)) Then
Dim inputListArray() As String = _
itemList.Split(New String() {delimiter}, _
StringSplitOptions.RemoveEmptyEntries)
For Each item As String In inputListArray
Output0Buffer.AddRow()
Output0Buffer.ParentID = keyField
If item.Length >= 3 Then
txtHolder = Trim(item)
Output0Buffer.ItemName = txtHolder
'when item length is less than 3, it's suffix
Else
suffixHolder = Trim(item)
txtHolder = Left(txtHolder.ToString(), Len(txtHolder) _
- Len(suffixHolder)) & suffixHolder.ToString()
Output0Buffer.ItemName = txtHolder
End If
Next
End If
End Sub
The current code produces the following output
ID Item Name
1 Apple01
1 02
1 Banana01
1 02
1 03
2 Spoon1
2 2
2 Fork1
2 2
2 3
2 4
If I come across as pedantic in this response, it is not my intention. Based on the comment "I'm new at coding and having a problem troubleshooting" I wanted to walk through my observations and how I came to them.
Problem analysis
The desire is to split a single row into multiple output rows based on a delimited field associated to the row.
The code as it stands now is generating the appropriate number of rows so you do have the asynchronous part (split) of the script working so that's a plus. What needs to happen is we need to 1) Populate the Child ID column 2) Apply the item prefix to all subsequent row when generating the child items.
I treat most every problem like that. What am I trying to accomplish? What is working? What isn't working? What needs to be done to make it work. Decomposing problems into smaller and smaller problems will eventually result in something you can do.
Code observations
Pasting in the supplied code resulted in an error that itemList was not declared. Based on usage, it seems that it was intended to be itemName.
After fixing that, you should notice the IDE indicating you have 2 unused variables (posID, childID) and that the variable txHolder is used before it's been assigned a value. A null reference exception could result at runtime. My coworker often remarks warnings are errors that haven't grown up yet so my advice to you as a fledgling developer is to pay attention to warnings unless you explicitly expect the compiler to warn you about said scenario.
Getting started
With a choice between solving the Child ID situation versus the name prefix/suffix stuff, I'd start with an easy one, the child id
Generating a surrogate key
That's the fancy title phrase that if you searched on you'd have plenty of hits to ssistalk or sqlis or any of a number of fabulously smart bloggers. Devil of course is knowing what to search on. No where do you ever compute or assign the child id value to the stream which of course is why it isn't showing up there.
We simply need to generate a monotonically increasing number which resets each time the source id changes. I am making an assumption that the inbound ID is unique in the incoming data like a sales invoice number would be unique and we are splitting out the items purchased. However if those IDs were repeated in the dataset, perhaps instead of representing invoice numbers they are salesperson id. Sales Person 1 could have another row in the batch selling vegetables. That's a more complex scenario and we can revisit if that better describes your source data.
There are two parts to generating our surrogate key (again, break problems down into smaller pieces). The first thing to do is make a thing that counts up from 1 to N. You have defined a childId variable to serve this. Initialize this variable (1) and then increment it inside your foreach loop.
Now that we counting, we need to push that value onto the output stream. Putting those two steps together would look like
childID = 1
For Each item As String In inputListArray
Output0Buffer.AddRow()
Output0Buffer.ParentId = keyField
Output0Buffer.ChildId = childID
' There might be VB shorthand for ++
childID = childID + 1
Run the package and success! Scratch the generate surrogate key off the list.
String mashing
I don't know of a fancy term for what needs to be done in the other half of the problem but I needed some title for this section. Given the source data, this one might be harder to get right. You've supplied value of Apple01, Banana01, Spoon1, Fork1. It looks like there's a pattern there (name concatenated with a code) but what it is it? Your code indicates that if it's less than 3, it's a suffix but how do you know what the base is? The first row uses a leading 0 and is two digits long while the second row does not use a leading zero. This is where you need to understand your data. What is the rule for identifying the "code" part of the first row? Some possible algorithms
Force your upstream data providers to provide consistent length codes (I think this has worked once in my 13 years but it never hurts to push back against the source)
Assuming code is always digits, evaluate each character in reverse order testing whether it can be cast to an integer (Handles variable length codes)
Assume the second element in the split array will provide the length of the code. This is the approach you are taking with your code and it actually works.
I made no changes to make the generated item name work beyond fixing the local variables ItemName/itemList. Final code eliminates the warnings by removing PosID and initializing txtHolder to an empty string.
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
Dim childID As Integer
Dim delimiter As String = ","
Dim txtHolder As String = String.Empty, suffixHolder As String
Dim itemName As String = Row.ItemName
Dim keyField As Integer = Row.ID
If Not (String.IsNullOrEmpty(itemName)) Then
Dim inputListArray() As String = _
itemName.Split(New String() {delimiter}, _
StringSplitOptions.RemoveEmptyEntries)
' The inputListArray (our split out field)
' needs to generate values from 1 to N
childID = 1
For Each item As String In inputListArray
Output0Buffer.AddRow()
Output0Buffer.ParentId = keyField
Output0Buffer.ChildId = childID
' There might be VB shorthand for ++
childID = childID + 1
If item.Length >= 3 Then
txtHolder = Trim(item)
Output0Buffer.ItemName = txtHolder
Else
'when item length is less than 3, it's suffix
suffixHolder = Trim(item)
txtHolder = Left(txtHolder.ToString(), Len(txtHolder) _
- Len(suffixHolder)) & suffixHolder.ToString()
Output0Buffer.ItemName = txtHolder
End If
Next
End If
End Sub