Unable to cast object error when attempting to return key/value pairs by querying a datacontext - vb.net

I am attempting to get data from a datacontext. Normally, I've had no problems doing it, but I'm having trouble trying to return a list of key/value pairs.
Basically I'm attempting to grab all unique names from a table as the key column and the number of times they appear in the table as the value column.
My data would look like this:
apple 5
banana 1
dragonfruit 3
.
.
.
The full error message is:
Unable to cast object of type 'System.Data.Linq.DataQuery1[VB$AnonymousType_32[System.String,System.Int32]]' to type 'System.Collections.Generic.List`1
The code I'm using is this:
Dim indicators As List(Of Object)
Public Sub GetIndicatorData()
Using context = new A_DataContext
indicators = (From p In chartdata Group p By __groupByKey1__ = p.INDK8R Into g = Group
Select New With {.name = __groupByKey1__, .count = g.Count()}).AsEnumerable()
indDataSource = indicators
End Sub
but I've also tried to:
Return indicators as a list
Return indicators as an enumerable.

Use a class to encapsulate your anonymous type
Public Class NameAndCount
Public ReadOnly Property Name As String
Public ReadOnly Property Count as Integer
Public Sub New(name As String, count As Integer)
Me.Name = name
Me.Count = count
End Sub
End Class
' ...
Private indicators As IEnumerable(Of NameAndCount)
Public Sub GetIndicatorData()
Using context = new A_DataContext
indicators = From p In chartdata Group p By __groupByKey1__ = p.INDK8R Into g = Group
Select New NameAndCount(__groupByKey1__, g.Count())
indDataSource = indicators.ToList()
End Using
End Sub

Figured it out. I changed the declaration of indicators to simply be an object then removed the .enumerable (or .toList) from the query result.
Thank you for the consideration and time.

Related

Retrieve list that is saved in a datatable

I created a datatable containing the list of notes for songs:
Private Table As New DataTable
Public Sub New()
With Table
.Columns.Add("Name")
.Columns.Add("NoteList")
.Rows.Add("GOT", GOT)
.Rows.Add("Yesterday", Yesterday)
End With
End Sub
GOT and Yesterday are lists of notes (notes is a class containing note, duration etc..)
On the form I then assign the datatable to a combobox:
ComboSongs.DisplayMember = Songs.DataTable.Columns(0).ColumnName
ComboSongs.ValueMember = Songs.DataTable.Columns(1).ColumnName
ComboSongs.DataSource = Songs.DataTable
I try to get the list of notes like this:
Dim songToPlay As List(Of Note) = CType(ComboSongs.SelectedValue, List(Of Note))
When I try to get the list I get the error:
System.InvalidCastException: 'Unable to cast object of type 'System.String' to type 'System.Collections.Generic.List`1[test.Note]'.'
Now I am unsure where I am getting it wrong. What would be the correct way to do this?
Your ValueMember is what is returned through the ComboBox.SelectedValue. So since you set the ValueMember like this
ComboSongs.ValueMember = Songs.DataTable.Columns(1).ColumnName
you only get the ColumnName. I assume that's a string and, well, the error message tells you it is one
... Unable to cast object of type 'System.String' ...
I guess that should be "NoteList", since that would be returned by Songs.DataTable.Columns(1).ColumnName
But this all doesn't make much sense, as I guess you are selecting a song there, either "Yesterday" or "GOT". At the point you're at it's so convoluted to return the DataTable rows, and index them. You will need to find the row by name and that is just too complicated when you could just create a class with strong names. I'll give you a class based solution but I'm not sure if you can make that change.
Private Class Song
Public Property Name As String
Public Property NoteList As List(Of Note)
End Class
Private Class Note
' your implementation
End Class
Dim songs As New List(Of Song)()
songs.Add(New Song() With {.Name = "GOT", .NoteList = New List(Of Note)})
songs.Add(New Song() With {.Name = "Yesterday", .NoteList = New List(Of Note)})
' need to populate those NoteLists first
ComboSongs.DisplayMember = "Name"
ComboSongs.DataSource = songs
Dim songToPlay = songs.SingleOrDefault(Function(s) s.Name = ComboSongs.SelectedValue)
Dim noteList = songToPlay.NoteList

LINQ fill nested list of Object from query results

I am joining two tables Race and Odds (joined on RaceID) and would like to create an object for each Race with its nested odds. Via LINQ I am able to group the query results into objects but can't populate the nested Odds list for each Race. Any ideas?
Ideally, I should have
obj[0] with RaceID = 1, Odds{1,2,3}
obj[1] with RaceID = 2, Odds{4,5,6,7}
etc
Public Class Race
Private RaceID As Integer
Private Name As String
'other properties here
Private Odds As New List(Of Odds)
End Class
Public Class Odds
Private OddsID As Integer
Private Odds As System.Nullable(Of Decimal)
Private RaceID As Integer
'other properties here
End Class
My function
Dim objRace As New List(Of Race)
Using context As IDataContext = DataContext.Instance()
Dim repository = context.GetRepository(Of Race)()
objRace = repository.Find("SELECT Odds.OddsID, Odds.Odds, Odds.Name, Odds.Type, Odds.DateUTC, Odds.WebsiteName, Odds.RaceID AS RaceID, Race.RaceID AS RaceID,
Race.Name AS RaceName, Race.RaceDate, Race.Location, Race.URL, Race.PortalID AS PortalID, Race.RaceTime
FROM Odds RIGHT OUTER JOIN
Race ON Odds.RaceID = Race.RaceID
WHERE (Race.PortalID = #0)", PortalId)
End Using
LINQ to get objects
Dim result = objRace.GroupBy(Function(records) records.RaceID).[Select](Function(r) New Race() With {
.RaceID = r.Key
.Odds = ' <------ **somehow populate this nested list**
}).ToList()
my query results:
The r should be a implementation of IGrouping. This contains a Key that you are using for your RaceID and it is a IEnumerable itself.
So you can store the Odds by calling r.ToList() or r.ToArray. How ever you want to store them.
I just noticed that you already create the list instance for your Odds in the constructor. You may want to change this, so you can put the collection in directly using the With initialisation.

How to find the largest integer(s) between 3 integers

I would like to find the largest integer(s) between 3 integers.
I could do this by nesting If statements. Since I have further code to write however this would be long and untidy.
I was wondering if there was an easier way to find the largest integer(s) (including if let's say A and B are equal but both higher than C).
P.S Can you do this with 2-D arrays?
Use LINQ to do this:
Dim numbers() As Integer = {1, 3, 5}
Dim max As Integer = numbers.Max()
Debug.Write("Max number in numbers() is " & max.ToString())
Output:
Edited as per conversation with OP on wanting to know which genre was ranked the best.
When asked How do you get the data? OP responds with:
I have a text file containing movie|genre on every line. I read this and count which genre (out of 3) is the highest.
I have drafted up some code which reads from a text file and populates a class.
First let me show you the code:
Dim myFilms As New Films
Using sr As New IO.StreamReader("C:\films.txt")
Do Until sr.Peek = -1
Dim columns As String() = sr.ReadLine().Split(New Char() {"|"c}, StringSplitOptions.RemoveEmptyEntries)
'columns(0) = film name
'columns(1) = genre
myFilms.Add(New Film(columns(0), columns(1)))
Loop
End Using
If myFilms.Count > 0 Then
Dim bestGenre = myFilms.GetBestGenre()
'Go off and read the genre file based on bestGenre
End If
From the above code you can see the class Films being populated with a new Film. I then call a method from the Films class, but only if there are films to choose from. Let me show you the class structure for both these:
Film:
Public Class Film
Public Key As String
Public Sub New(ByVal filmName As String,
ByVal genre As String)
_filmName = filmName
_genre = genre
End Sub
Private _filmName As String
Public ReadOnly Property FilmName As String
Get
Return _filmName
End Get
End Property
Private _genre As String
Public ReadOnly Property Genre As String
Get
Return _genre
End Get
End Property
End Class
Films:
Public Class Films
Inherits KeyedCollection(Of String, Film)
Protected Overrides Function GetKeyForItem(ByVal item As Film) As String
Return item.Key
End Function
Public Function GetBestGenre() As String
Return Me.GroupBy(Function(r) r.Genre).OrderByDescending(Function(g) g.Count()).First().Key
End Function
End Class
I must note that although this code does work it may come unstuck if you have 2 or more genres which are joint top. The code still works however it only returns one of the genres. You may want to expand on the code to suit your needs based on that scenario.
Try something like this:
Dim max As Integer
max = integer1
If integer2 > max Then
max = integer2
End If
If integer3 > max Then
max = integer3
End If
Not many more ways that I can think of off the top of my head to do this.
Something along these lines will work for any number of integers.
Put the numbers into an array then use a For[...]Next statement to loop through the array comparing the current member with max. If max is lower, set it to the current member. When the loop terminates, max will contain the highest number:
Dim nums() As Integer = {1, 2, 3}
Dim max As Integer
For i = 0 To nums.Length - 1
If max < nums(i) Then
max = nums(i)
End If
Next

LongListSelector selecteditem

I have a LongListSelector in an .xaml and I am able to fill it by binding to a an ItemSource when the source is filled by a DataContext using a single table from my SQL Server CE database like this:
Dim row = (From rows In db.Hub
Order By rows.HubID Descending
Select rows).ToList()
Me.MainLongListSelector.ItemsSource = row
I am thus able to get the ID of the selected item as follows:
HubID = CType(MainLongListSelector.SelectedItem, Hub).HubID
I am also able to bind to a 'query' DataSource as follows:
Dim row = (From ac In db.Activity
Join at In db.ActivityType On ac.ActivityTypeID Equals at.ActivityTypeID
Select New With {.ID = ac.ActivityID,
.Title = ac.Activity1}).ToList()
Me.MainLongListSelector.ItemsSource = row
however, since this is not referring to a specific table in the DataContext, I cannot get the ID using the above code, ie:
Dim ActID = CType(MainLongListSelector.SelectedItem, Activity).ActivityID '- returns nothing
How should I get the value(s) of selectedItem in this case?
NB: I have created the anonymous fields (.ID and .Title) because those are the names I have bound in the xaml, so the LongListSelected gets populated without writing extra code.
Thanks
Phew!!
I discovered that two things:
this HubID = CType(MainLongListSelector.SelectedItem, Hub).HubID is calling a List (Of DataContext), while in the second scenario above I am using a List (Of Anonymous). So I searched for List (Of Anonymous) and this came up!
I now know I can create a class for List (Of Anonymous) and properly name its properties, thus make it available outside its methods, like in my 'query' question above.
So the answer is I created the class for my anonymous list, declared its properties
Public Class AnonList
Private _id As Integer
Public Property ID() As Integer
Get
Return _id
End Get
Set(ByVal value As Integer)
_id = value
End Set
End Property
Private _title As String
Public Property Title() As String
Get
Return _title
End Get
Set(ByVal value As String)
_title = value
End Set
End Property
Private _desc As String
Public Property Desc() As String
Get
Return _desc
End Get
Set(ByVal value As String)
_desc = value
End Set
End Property
End Class
and therefore assigned them to the ItemSource values,
Select New AnonList With {.ID = ac.ActivityID,
thus being able to get the SelectedItem values as required:
ActivityID = CType(MainLongListSelector.SelectedItem, AnonList).ID
Took a bit of determination to figure that out!

How to sort an object in a list on a non-unique value?

I'm trying to categorize articles by stored keywords. I have a list of keywords for a category, and I want an article to get assigned a category that has the most keyword count.
For Each keyword As String In category.Keywords
category.tempCount += Regex.Matches(article.Item("title").InnerXml, Regex.Escape(keyword)).Count
category.tempCount += Regex.Matches(article.Item("description").InnerXml, Regex.Escape(keyword)).Count
Next
And this is done for each category, ran for each article. I'm trying to sort the list in order to tell which category is the best one for this article. However it is possible more than one category is the best, and that none of the categories fit. So running this did not help me:
Categories.Sort(
Function(article1 As ArticleCategory, article2 As ArticleCategory)
Return article1.tempCount.CompareTo(article2.tempCount)
End Function)
Maybe I'm doing this all wrong, but so far I think I'm on the right path. (I also have a default compare in the Category class, it just wasn't working either.)
I get an exception on the sorting most likely caused because they are not unique.
The exception I get is an InvalidOperationException: Failed to compare two elements in the array. That's with using the comparer I built in the ArticleClass
Imports System.Xml
Class ArticleCategory
Implements IComparer(Of ArticleCategory)
Public ReadOnly key As Int32
Public ReadOnly Name As String
Public ReadOnly Keywords As List(Of String)
Public tempCount As Integer = 0
Public Sub New(ByVal category As XmlElement)
key = System.Web.HttpUtility.UrlDecode(category.Item("ckey").InnerXml)
Name = System.Web.HttpUtility.UrlDecode(category.Item("name").InnerXml)
Dim tKeywords As Array = System.Web.HttpUtility.UrlDecode(category.Item("keywords").InnerXml).Split(",")
Dim nKeywords As New List(Of String)
For Each keyword As String In tKeywords
If Not keyword.Trim = "" Then
nKeywords.Add(keyword.Trim)
End If
Next
Keywords = nKeywords
End Sub
'This should be removed if your using my solution.
Public Function Compare(ByVal x As ArticleCategory, ByVal y As ArticleCategory) As Integer Implements System.Collections.Generic.IComparer(Of ArticleCategory).Compare
Return String.Compare(x.tempCount, y.tempCount)
End Function
End Class
You need to implement IComparable instead of IComparer.
IComparer would be implemented by the class performing the sorting (such as a List class) while IComparable would be implemented by the class being sorted.
For example:
Public Function CompareTo(other As ArticleCategory) As Integer Implements System.IComparable(Of ArticleCategory).CompareTo
Return Me.tempCount.CompareTo(other.tempCount)
End Function
The best solution I found was using the Microsoft LINQ (a query language for objects) it works very well and quickly produces the right result.
Dim bestCat As ArticleCategory
bestCat = (From cat In Categories
Order By cat.tempCount Descending, cat.Name
Select cat).First
Completing my solution:
For Each category As ArticleCategory In Categories
category.tempCount = 0
For Each keyword As String In category.Keywords
category.tempCount += Regex.Matches(System.Web.HttpUtility.UrlDecode(article.Item("title").InnerXml), Regex.Escape(keyword)).Count
category.tempCount += Regex.Matches(System.Web.HttpUtility.UrlDecode(article.Item("description").InnerXml), Regex.Escape(keyword)).Count
Next
Next
Dim bestCat As ArticleCategory
Try
bestCat = (From cat In Categories
Order By cat.tempCount Descending, cat.Name
Select cat).First
Catch ex As Exception
ReportStatus(ex.Message)
End Try
So this is my preferred method to do a sort or a query on a list object or an array. It produces the best result, in the fastest time without having to add the IComparer implementations to your class.
Check it out at Microsoft.com