-
Apr 5th, 2021, 12:35 PM
#1
LINQ question
I'm a LINQ newbie...seen lots of code examples here but have no clue how to do it.
Let's say I've got this list(of string)...
Code:
BookAbbrList = New List(Of List(Of String)) From {
New List(Of String)({"Genesis", "gen"}),
New List(Of String)({"Exodus", "exo"}),
New List(Of String)({"Leviticus", "lev"}),
New List(Of String)({"Numbers", "num"}),
New List(Of String)({"Deuteronomy", "deu"}),
New List(Of String)({"Joshua", "jos"}),
How could I take the "abbreviations" - gen, exo, lev - and return the BOOK NAME...
tia
-
Apr 5th, 2021, 01:09 PM
#2
Re: LINQ question
I would personally use a Dictionary(Of String, String) and not use LINQ at all for this situation:
Code:
Dim bookAbbrList = New Dictionary(Of String, String)() From {
{ "gen", "Genesis" },
{ "exo", "Exodus" },
{ "lev", "Leviticus" },
{ "num", "Numbers" },
{ "deu", "Deuteronomy" },
{ "jos", "Joshua" }
}
Console.Write("Book Abbreviation: ")
Dim abbreviation = Console.ReadLine()
If (bookAbbrList.ContainsKey(abbreviation)) Then
Dim fullName = bookAbbrList(abbreviation)
Console.WriteLine(fullName)
End If
Example: https://dotnetfiddle.net/UaqKoj
-
Apr 5th, 2021, 01:10 PM
#3
Re: LINQ question
@dday - thanks for that.
I might put more items in the list - it might not be just two.
Is there also a LINQ attack that would get to the "row" of data?
-
Apr 5th, 2021, 01:15 PM
#4
Re: LINQ question
In that case, you could still use LINQ. What you would be doing is querying items where the current item's list contains the abbreviation. Maybe something like this:
Code:
Dim bookAbbrList = New List(Of List(Of String)) From {
New List(Of String)({"Genesis", "gen"}),
New List(Of String)({"Exodus", "exo"}),
New List(Of String)({"Leviticus", "lev"}),
New List(Of String)({"Numbers", "num"}),
New List(Of String)({"Deuteronomy", "deu"}),
New List(Of String)({"Joshua", "jos"})
}
Console.Write("Book Abbreviation: ")
Dim abbreviation = Console.ReadLine()
Dim match = bookAbbrList.FirstOrDefault(Function(record) record.Contains(abbreviation))
If (match IsNot Nothing) Then
Console.WriteLine(String.Join(",", match))
End If
Example: https://dotnetfiddle.net/WQZaVd
Update
To elaborate on my code a little bit, here it is broken down:
- FirstOrDefault: Query the collection for the first instance that matches the predicate. If no match is returned, return Nothing
- Function(record): The start of my predicate, indicating that record represents the current item in the iteration
- record.Contains(abbreviation): The condition that is being used to return a Boolean value for the predicate. If true, then it will return record
- If (match IsNot Nothing) Then: Since I used the FirstOrDefault method, I need to check if the returned value is Nothing because if it is, then that means nothing matched the condition in my predicate
Last edited by dday9; Apr 5th, 2021 at 02:04 PM.
-
Apr 5th, 2021, 07:31 PM
#5
Re: LINQ question
Originally Posted by szlamany
@dday - thanks for that.
I might put more items in the list - it might not be just two.
Is there also a LINQ attack that would get to the "row" of data?
In that case, it sounds like you should be defining a dedicated type to represent a "row" and then creating a List(Of T) where T is that type. You can certainly write LINQ queries to do things with a list of lists but it's generally not something you would do for the type of data you seem to be working with.
-
Apr 6th, 2021, 02:30 AM
#6
Re: LINQ question
this isn't my Code
I found this sample but can't remenber where, I was looking at the time for 'Linq Join'
see if it helps
Code:
Public Class Form1
Public Class Student
Public Property Id As String
Public Property StudentName As String
Public Property GPA As String
End Class
Public Class StudentsSubject
Public Property SubjectId As String
Public Property StudentId As String
Public Property Id As String
End Class
Public Class Subject
Public Property Id As String
Public Property SubjectName As String
End Class
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim students As New List(Of Student)
students.Add(New Student With {.Id = "1", .GPA = "GPA1", .StudentName = "John"})
students.Add(New Student With {.Id = "2", .GPA = "GPA2", .StudentName = "Peter"})
students.Add(New Student With {.Id = "3", .GPA = "GPA3", .StudentName = "Mary"})
Dim subjects As New List(Of Subject)
subjects.Add(New Subject With {.Id = "100", .SubjectName = "Maths"})
subjects.Add(New Subject With {.Id = "200", .SubjectName = "Physics"})
Dim studentsSubject As New List(Of StudentsSubject)
studentsSubject.Add(New StudentsSubject With {.Id = "10", .StudentId = "1", .SubjectId = "100"})
studentsSubject.Add(New StudentsSubject With {.Id = "20", .StudentId = "1", .SubjectId = "200"})
studentsSubject.Add(New StudentsSubject With {.Id = "30", .StudentId = "2", .SubjectId = "100"})
studentsSubject.Add(New StudentsSubject With {.Id = "40", .StudentId = "3", .SubjectId = "100"})
Dim listOfStudents = From st In students
Join ss In studentsSubject On ss.StudentId Equals st.Id
Join sb In subjects On ss.SubjectId Equals sb.Id
Select st.StudentName, st.GPA, sb.SubjectName
Dim line As String = New String("-", 40)
For Each s In listOfStudents
Debug.WriteLine("{0} on {1}", s.StudentName, _
s.SubjectName)
Next
Dim res = From cust In students
Group Join so In studentsSubject
On so.StudentId Equals cust.Id
Into MatchedOrders = Group
From mo In MatchedOrders.DefaultIfEmpty()
For Each r In res
Debug.WriteLine("{0} on {1}", r.mo.StudentId, _
r.mo.SubjectId)
Next
End Sub
End Class
to hunt a species to extinction is not logical !
since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.
-
Apr 6th, 2021, 05:07 AM
#7
Re: LINQ question
Originally Posted by jmcilhinney
In that case, it sounds like you should be defining a dedicated type to represent a "row" and then creating a List(Of T) where T is that type. You can certainly write LINQ queries to do things with a list of lists but it's generally not something you would do for the type of data you seem to be working with.
I'm attempting to learn a little LINQ. And do it with a simple structure.
So far what I dislike about the LINQ syntax is how bloated it is compared to hiding a loop on that list in a function that just returns the name. Then that code - calling that function - stays nice and neat. I guess LINQ experts would simply use the LINQ search in the "function" and keep the calling code tight anyway.
I'm writing a one-off, one-time use program and was handed that list. Was about to write a loop to turn abbreviations into names - thought about using LINQ...
-
Apr 6th, 2021, 05:29 AM
#8
Re: LINQ question
Originally Posted by szlamany
I'm a LINQ newbie...seen lots of code examples here but have no clue how to do it.
Let's say I've got this list(of string)...
Code:
BookAbbrList = New List(Of List(Of String)) From {
New List(Of String)({"Genesis", "gen"}),
New List(Of String)({"Exodus", "exo"}),
New List(Of String)({"Leviticus", "lev"}),
New List(Of String)({"Numbers", "num"}),
New List(Of String)({"Deuteronomy", "deu"}),
New List(Of String)({"Joshua", "jos"}),
How could I take the "abbreviations" - gen, exo, lev - and return the BOOK NAME...
tia
To address the question as asked:
vb.net Code:
Dim name = BookAbbrList.FirstOrDefault(Function(book) book(1) = abbreviation)?(0)
The First, FirstOrDefault, Single and SingleOrDefault methods all work similarly but there's never a time that one isn't the only correct option and you should use that one based on your scenario.
First: gets the first match and throws an exception if there are no matches.
FirstOrDefault: gets the first match or Nothing if there is no match.
Single: gets the only match and throws an exception if there are no matches or multiple matches.
SingleOrDefault: gets the only match or Nothing if there is no match and throws an exception if there are multiple matches.
If you use First or Single then you can do away with the null-coalescing operator (?) because the expression preceding it can never be Nothing.
-
Apr 6th, 2021, 05:41 AM
#9
Re: LINQ question
Originally Posted by jmcilhinney
The First, FirstOrDefault, Single and SingleOrDefault methods all work similarly but there's never a time that one isn't the only correct option and you should use that one based on your scenario.
First: gets the first match and throws an exception if there are no matches.
FirstOrDefault: gets the first match or Nothing if there is no match.
Single: gets the only match and throws an exception if there are no matches or multiple matches.
SingleOrDefault: gets the only match or Nothing if there is no match and throws an exception if there are multiple matches.
If you use First or Single then you can do away with the null-coalescing operator (?) because the expression preceding it can never be Nothing.
I do tend to dwell on this a bit because a lot of people don't immediately see the difference or realise that it's important. The implementations of those four methods is effectively like this:
vb.net Code:
Imports System.Runtime.CompilerServices
Public Module EnumerableExtensions
<Extension>
Public Function First(Of T)(source As IEnumerable(Of T), predicate As Predicate(Of T)) As T
For Each item In source
If predicate(item) Then
Return item
End If
Next
Throw New Exception()
End Function
<Extension>
Public Function FirstOrDefault(Of T)(source As IEnumerable(Of T), predicate As Predicate(Of T)) As T
For Each item In source
If predicate(item) Then
Return item
End If
Next
Return Nothing
End Function
<Extension>
Public Function [Single](Of T)(source As IEnumerable(Of T), predicate As Predicate(Of T)) As T
Dim matchFound = False
Dim match As T
For Each item In source
If predicate(item) Then
If matchFound Then
Throw New Exception()
End If
matchFound = True
match = item
End If
Next
If matchFound Then
Return match
End If
Throw New Exception()
End Function
<Extension>
Public Function SingleOrDefault(Of T)(source As IEnumerable(Of T), predicate As Predicate(Of T)) As T
Dim matchFound = False
Dim match As T
For Each item In source
If predicate(item) Then
If matchFound Then
Throw New Exception()
End If
matchFound = True
match = item
End If
Next
If matchFound Then
Return match
End If
Return Nothing
End Function
End Module
You just need to think about how you would write the code to do it long-hand to know which of the four methods you should choose. Note that First and FirstOrDeafult return as soon as a match is found while Single and SingleOrDefault continue enumerating to the end of the list or until a second match is found. This means that the former two might be much quicker for long lists but the latter two provide explicit validation where list items are supposed to be unique.
-
Apr 6th, 2021, 07:56 AM
#10
Re: LINQ question
@jmc - thanks - that explains it nicely. LINQ is no longer in the realm of "regex" to me!
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|