Delete duplicates from list - vb.net

I have the following class :
Public Class titlesclass
Public Property Link As String
Public Property Title As String
Public Function Clear()
Link.Distinct().ToArray()
Title.Distinct().ToArray()
End Function
End Class
And the following code :
For Each title As Match In (New Regex(pattern).Matches(content)) 'Since you are only pulling a few strings, I thought a regex would be better.
Dim letitre As New titlesclass
letitre.Link = title.Groups("Data").Value
letitre.Title = title.Groups("Dataa").Value
lestitres.Add(letitre)
'tempTitles2.Add(title.Groups("Dataa").Value)
Next
I tried to delete the duplicated strings using the simple way
Dim titles2 = lestitres.Distinct().ToArray()
And calling the class function :
lestitres.Clear()
But the both propositions didn't work , i know that i'm missing something very simple but still can't find what it is

Easier to use a class that already implements IComparable:
Dim query = From title In Regex.Matches(content, pattern).Cast(Of Match)
Select Tuple.Create(title.Groups("Data").Value, title.Groups("Dataa").Value)
For Each letitre In query.Distinct
Debug.Print(letitre.Item1 & ", " & letitre.Item2)
Next
or Anonymous Types:
Dim query = From title In Regex.Matches(content, pattern).Cast(Of Match)
Select New With {Key .Link = title.Groups("Data").Value,
Key .Title = title.Groups("Dataa").Value}
For Each letitre In query.Distinct
Debug.Print(letitre.Link & ", " & letitre.Title)
Next

Ok, Since I notice you are using a ClassHere is one option you can do in order to not add duplicate items to your List within a class.I'm using a console Application to write this example, it shouldn't be too hard to understand and convert to a Windows Form Application if need be.
Module Module1
Sub Main()
Dim titlesClass = New Titles_Class()
titlesClass.addNewTitle("myTitle") ''adds successfully
titlesClass.addNewTitle("myTitle") '' doesn't add
End Sub
Public Class Titles_Class
Private Property Title() As String
Private Property TitleArray() As List(Of String)
Public Sub New()
TitleArray = New List(Of String)()
End Sub
Public Sub addNewTitle(title As String)
Dim added = False
If Not taken(title) Then
Me.TitleArray.Add(title)
added = True
End If
Console.WriteLine(String.Format("{0}", If(added, $"{title} has been added", $"{title} already exists")))
End Sub
Private Function taken(item As String) As Boolean
Dim foundItem As Boolean = False
If Not String.IsNullOrEmpty(item) Then
foundItem = Me.TitleArray.Any(Function(c) -1 < c.IndexOf(item))
End If
Return foundItem
End Function
End Class
End Module
Another option would be to use a HashSet, It will never add a duplicate item, so even if you add an item with the same value, it wont add it and wont throw an error
Sub Main()
Dim titlesClass = New HashSet(Of String)
titlesClass.Add("myTitle") ''adds successfully
titlesClass.Add("myTitle") '' doesn't add
For Each title As String In titlesClass
Console.WriteLine(title)
Next
End Sub
With all of that aside, have you thought about using a Dictionary so that you could have the title as the key and the link as the value, that would be another way you could not have a list (dictionary) contain duplicate items

Related

Is there a function to write/export values of list-/class items?

From an external application data is extracted and written to a list with several sublist items, here is an example narrowed down to the main list creation:
Public Class clMain
Public lsLevel_1 As New List(Of clLevel_1)
End Class
Public Class clLevel_1
Public sgName As String
Public lsLevel_2 As New List(Of clLevel_2)
End Class
Public Class clLevel_2
Public sgName As String
Public lsLevel_3 As New List(Of clLevel_3)
End Class
Public Class clLevel_3
Public sgName As String
Public sgComment As String
End Class
Module Code
Sub Main()
Dim lv_clMain As clMain = New clMain
For lv_i16Level_1 = 10 To 11
Dim lv_clLevel_1 As New clLevel_1
lv_clLevel_1.sgName = "Level 1: " & lv_i16Level_1
For lv_i16Level_2 = 20 To 21
Dim lv_clLevel_2 As New clLevel_2
lv_clLevel_2.sgName = "Level 2: " & lv_i16Level_2
For lv_i16Level_3 = 30 To 31
Dim lv_clLevel_3 As New clLevel_3
lv_clLevel_3.sgName = "Level 3: " & lv_i16Level_3
lv_clLevel_2.lsLevel_3.Add(lv_clLevel_3)
Next
lv_clLevel_1.lsLevel_2.Add(lv_clLevel_2)
Next
lv_clMain.lsLevel_1.Add(lv_clLevel_1)
Next
End Sub
End Module
Once the list has been "filled", I would like to write/export the values to a file. What would be a good approach for this tasks? I think creating a function with lv_clMain as parameter. But as lv_clMain is not a collection type, and it can contain several entries of lsLevel_1 I am somewhat lost about the correct approach.
Thanks to user Hursey, I found a solution using XML serialisation.
Function SerializeXml(ByVal obData As Object, ByVal sgFileName As String, tyData As Type)
Dim obXmlSerializer As System.Xml.Serialization.XmlSerializer = New System.Xml.Serialization.XmlSerializer(tyData)
Dim obSW As System.IO.TextWriter = New System.IO.StreamWriter(sgFileName)
obXmlSerializer.Serialize(obSW, obData)
obSW.Close()
End Function

Sort specific objects

I have tree classes as follows:
Public Class HtmlSection
Property Name As String
Property SubSections As List(Of HtmlSubSection)
End Class
Public Class HtmlSubSection
Property Name As String
Property SelectedSentences As List(Of HtmlSentence)
End Class
Public Class HtmlSentence
Property Sentence As String
Property Position As Integer
End Class
In below method i am searching for all sentences for each subsection belonging to specific section, at the end i sort those records by Position asc. However sometimes positions have to be changed (directly in sentences) because there could be gaps means after i do OrderBy it will be ordered but it could look like this below. Is there any easy way like linq to change that Positions of that sentences to avoid gaps let's say in the method i shown below.
2
5
77
1001
i would like to change positions starting from 0 in our example:
0
1
2
3
Method:
Public Function GetSelectedSentencesOnSectionLevel(section As HtmlSection) As List(Of HtmlSentence)
Dim sentencesList As New List(Of HtmlSentence)
For Each exSection As HtmlSection In _htmlFactory.SectionsList
If exSection.Name = section.Name Then
Dim sentencesList As New List(Of HtmlSentence)
If Not IsNothing(exSection.SubSections) Then
For Each exSubsection As HtmlSubSection In exSection.SubSections
If Not IsNothing(exSubsection.SelectedSentences) Then
For Each exSentence As HtmlSentence In exSubsection.SelectedSentences
sentencesList.Add(exSentence)
Next
End If
Next
End If
End If
Next
'sort sentences by Posiions ascending
sentencesList = sentencesList.OrderBy(Function(x) x.Position).ToList()
Return sentencesList
End Function
EDIT : more code for helpers:
global class:
Public Class HtmlFactory
Property SectionsList As List(Of HtmlSection)
Sub New()
SectionsList = New List(Of HtmlSection)
End Sub
Sub New(pSectionsList As List(Of HtmlSection))
_SectionsList = pSectionsList
End Sub
Public Sub AddSection(section As HtmlSection)
SectionsList.Add(section)
End Sub
....
Here you are a pure LINQ solution.
Dim index As Integer = -1
Dim sectionName As String
Dim allTheSections As List(Of HtmlSection)
Dim sentenceList = allTheSections _
.Where(Function(sect) _
sect.SubSections IsNot Nothing _
AndAlso sect.Name.Equals(sectionName, StringComparison.OrdinalIgnoreCase)) _
.SelectMany(Function(sect) sect.SubSections) _
.Where(Function(subSect) subSect.SelectedSentences IsNot Nothing) _
.SelectMany(Function(subSect) subSect.SelectedSentences) _
.OrderBy(Function(ss) ss.Position) _
.Select(Function(ss)
index += 1
Return New HtmlSentence With {.Position = index, .Sentence = ss.Sentence}
End Function) _
.ToList()
In this example, allTheSections is where you exSection does coming from.

Parse custom language syntax

I am developing a server-side scripting language which I intend to use on my private server. It is similar to PHP, and I know that I could easily use PHP instead but I'm just doing some programming for fun.
The syntax of basic commands in my language is as follows:
command_name "parameter1" : "parameter2" : "parameter3"
But it can also be like this when I want to join values for a parameter:
command_name "parameter1" : "param" & "eter2" : "par" & "amet" & "er3"
How would I go about parsing a string like the ones shown above (it will be perfectly typed, no syntax errors) to an object that has these properties
Custom class "Request"
Property "Command" as String, should be the "command_name" part
Property "Parameters" as String(), should be an array of Parameter objects
Shared Function FromString(s As String) as Request, this should accept a string in the language above and parse it to a Request object
Custom class "Parameter"
Property "Segments" as String(), for example "para", "mete", and "r3"
Sub New(ParamArray s as String()), this is how it should be generated from the code
It should be done in VB.NET and I am a moderate level programmer, so even if you just have an idea of how to attack this then please share it with me. I am very new to parsing complex data like this so I need a lot of help. Thanks so much!
Here is another method that is simpler.
Module Module1
Sub Main()
Dim inputs As String() = {"command_name ""parameter1"" : ""parameter2"" : ""parameter3""", "command_name ""parameter1"" : ""param"" & ""eter2"" : ""par"" & ""amet"" & ""er3"""}
For Each _input As String In inputs
Dim commandStr As String = _input.Substring(0, _input.IndexOf(" ")).Trim()
Dim parameters As String = _input.Substring(_input.IndexOf(" ")).Trim()
Dim parametersA As String() = parameters.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(Function(x) x.Trim()).ToArray()
Dim parametersB As String()() = parametersA.Select(Function(x) x.Split("&".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(Function(y) y.Trim(" """.ToCharArray())).ToArray()).ToArray()
Dim newCommand As New Command() With {.name = commandStr, .parameters = parametersB.Select(Function(x) New Parameter(x)).ToArray()}
Command.commands.Add(newCommand)
Next (_input)
Dim z = Command.commands
End Sub
End Module
Public Class Command
Public Shared commands As New List(Of Command)
Public name As String
Public parameters As Parameter()
End Class
Public Class Parameter
Sub New()
End Sub
Sub New(names As String())
Me.names = names
End Sub
Public names As String()
End Class
I figured it out myself
Module Module1
Sub Main()
Dim r As Request = Request.Parse(Console.ReadLine())
Console.WriteLine("The type of request is " & r.Name)
For Each p As Parameter In r.Parameters
Console.WriteLine("All segments inside of parameter " & r.Parameters.IndexOf(p).ToString)
For Each s As String In p.Segments
Console.WriteLine(" Segment " & p.Segments.IndexOf(s).ToString & " is " & s)
Next
Next
Main()
End Sub
Public Class Request
Public Name As String
Public Parameters As New List(Of Parameter)
Public Shared Function Parse(line As String)
Dim r As New Request
r.Name = line.Split(" ")(0)
Dim u As String = line.Substring(line.IndexOf(" "), line.Length - line.IndexOf(" "))
Dim p As String() = u.Split(":")
For Each n As String In p
Dim b As String() = n.Split("&")
Dim e As New List(Of String)
For Each m As String In b
Dim i As Integer = 0
Do Until i > m.Length - 1
If m(i) = ControlChars.Quote Then
Dim s As String = ""
i += 1
Do Until i > m.Length - 1 Or m(i) = ControlChars.Quote
s &= m(i)
i += 1
Loop
e.Add(s)
End If
i += 1
Loop
Next
r.Parameters.Add(New Parameter(e.ToArray))
Next
Return r
End Function
End Class
Public Class Parameter
Public Segments As New List(Of String)
Public Sub New(ParamArray s As String())
Segments = s.ToList
End Sub
End Class
End Module

How to add item when using List (Of)?

I created a class where i declared some properties.
Public Class BlogPost
Dim _postTitleUrl As String = String.Empty
Dim _pageGUID As String = String.Empty
Property postTitleUrl() As String
Get
Return _postTitleUrl
End Get
Set(ByVal value As String)
_postTitleUrl = value
End Set
End Property
Property pageGUID() As String
Get
Return _pageGUID
End Get
Set(ByVal value As String)
_pageGUID = value
End Set
End Property
End Class
Now, I have another class where I want to set the values.
Public Class SetBlogData
Public blogPostList As New List(Of BlogPost)
Public dataCounter as integer = 0
blogPostList(dataCounter).pageGUID = mainBlogSPWeb.ID.ToString
....
This gives me an error about Index was out of range. Hpw can I properly access the properties in BlogPost class?
Because your list has nothing .
You should use add method to add your new item. Like ...
Dim blogPostList = New List(Of BlogPost)
Dim blogPost = New BlogPost
blogPost.pageGUID = mainBlogSPWeb.ID.ToString
blogPostList.Add(blogPost)
You need to put a BlogPost in your list by writing blogPost.List.Add(New BlogPost())
I find this a good way of inserting into a list:
1) Check if the list is nothing
2) If so instantiate a new list
3) Add the value to list.
Below is an example.
Private Sub ExampleAddValueToList(ByVal value as BlogPost)
Try
If _blogPostList Is Nothing Then
_blogPostList = New List(Of BlogPost)
End If
_blogPostList.Add(value)
Catch ex As Exception
debug.write(ex.message)
End Try
End Sub

Get the name of the object passed in a byref parameter vb.net

How can I get the name of the object that was passed byref into a method?
Example:
Dim myobject as object
sub mymethod(byref o as object)
debug.print(o.[RealName!!!!])
end sub
sub main()
mymethod(myobject)
'outputs "myobject" NOT "o"
end sub
I'm using this for logging. I use one method multiple times and it would be nice to log the name of the variable that I passed to it. Since I'm passing it byref, I should be able to get this name, right?
For minitech who provided the answer:
This would give you the parameter name in the method and it's type, but not the name of the variable that was passed byref.
using system.reflection
Dim mb As MethodBase = MethodInfo.GetCurrentMethod()
For Each pi As ParameterInfo In mb.GetParameters()
Debug.Print("Parameter: Type={0}, Name={1}", pi.ParameterType, pi.Name)
Next
If you put that in "mymethod" above you'd get "o" and "Object".
That's impossible. Names of variables are not stored in IL, only names of class members or namespace classes. Passing it by reference makes absolutely zero difference. You wouldn't even be able to get it to print out "o".
Besides, why would you ever want to do that?
Alternatively you could get the 'Type' of the object using reflection.
Example: (Use LinqPad to execute)
Sub Main
Dim myDate As DateTime = DateTime.Now
MyMethod(myDate)
Dim something As New Something
MyMethod(something)
End Sub
Public Class Something
Public Sub New
Me.MyProperty = "Hello"
End Sub
Public Property MyProperty As String
End Class
Sub MyMethod(Byref o As Object)
o.GetType().Name.Dump()
End Sub
Sorry to say, but this is your solution. I left (ByVal o As Object) in the method signature in case you're doing more with it.
Sub MyMethod(ByVal o As Object, ByVal name As String)
Debug.Print(name)
End Sub
Sub Main()
MyMethod(MyObject, "MyObject")
End Sub
Alternatively you could create an interface, but this would only allow you to use MyMethod with classes you design. You can probably do more to improve it, but as this code stands you can only set the RealName at creation.
Interface INamedObject
Public ReadOnly Property RealName As String
End Interface
Class MyClass
Implements INamedObject
Public Sub New(ByVal RealName As String)
_RealName = RealName
End Sub
Private ReadOnly Property RealName As String Implements INamedObject.RealName
Get
Return _RealName
End Get
End Property
Private _RealName As String
End Class
Module Main
Sub MyMethod(ByVal o As INamedObject)
Debug.Print(o.RealName)
End Sub
Sub Main()
Dim MyObject As New MyClass("MyObject")
MyMethod(MyObject)
End Sub
End Module
If your program is still in the same place relative to the code that made it, this may work:
' First get the Stack Trace, depth is how far up the calling tree you want to go
Dim stackTrace As String = Environment.StackTrace
Dim depth As Integer = 4
' Next parse out the location of the code
Dim delim As Char() = {vbCr, vbLf}
Dim traceLine As String() = stackTrace.Split(delim, StringSplitOptions.RemoveEmptyEntries)
Dim filePath As String = Regex.Replace(traceLine(depth), "^[^)]+\) in ", "")
filePath = Regex.Replace(filePath, ":line [0-9]+$", "")
Dim lineNumber As String = Regex.Replace(traceLine(depth), "^.*:line ", "")
' Now read the file
Dim program As String = __.GetStringFromFile(filePath, "")
' Next parse out the line from the class file
Dim codeLine As String() = program.Split(delim)
Dim originLine As String = codeLine(lineNumber * 2 - 2)
' Now get the name of the method doing the calling, it will be one level shallower
Dim methodLine As String = Regex.Replace(traceLine(depth - 1), "^ at ", "")
Dim methodName = Regex.Replace(methodLine, "\(.*\).*$", "")
methodName = Regex.Replace(methodName, "^.*\.", "")
' And parse out the variables from the method
Dim variables As String = Regex.Replace(originLine, "^.*" & methodName & "\(", "")
variables = Regex.Replace(variables, "\).*$", "")
You control the depth that this digs into the stack trace with the depth parameter. 4 works for my needs. You might need to use a 1 2 or 3.
This is the apparently how Visual Basic controls handle the problem.
They have a base control class that in addition to any other common properties these controls may have has a name property.
For Example:
Public MustInherit Class NamedBase
Public name As String
End Class
Public Class MyNamedType
Inherits NamedBase
public Value1 as string
public Value2 as Integer
End Class
dim x as New MyNamedType
x.name = "x"
x.Value1 = "Hello, This variable is name 'x'."
x.Value2 = 75
MySubroutine(x)
public sub MySubroutine(y as MyNamedType)
debug.print("My variable's name is: " & y.name)
end sub
The output in the intermediate window should be:
My variable's name is: x