simplify linq expression in for each loop - vb.net

i want to remove or set an item equal to nothing in a collection or list
the following code works but is there a simplier expression
For Each i In response.Report.Arrestee
i.ArresteeArmedWithCode = Nothing
Next

LINQ isn't supposed to have side effects, so don't try and create LINQ expressions that set values.
If Arrestee is a List<T> you can do Arrestee.ForEach(Function(a) a.ArresteeArmedWithCode = Nothing) (ForEach is a List thing, not a LINQ thing) but it's relatively opinionated as to whether that's simpler. It's fewer lines but brevity does not always lead to clarity

Related

Convert For-Each into a LINQ query for adding items in list and check wehter list contains the item already

I would like to convert ForEach to LINQ. Currently I'm using these two parts
If TypeOf e.FilterPopup Is RadListFilterPopup Then
Dim ePopup As RadListFilterPopup = DirectCast(e.FilterPopup, RadListFilterPopup)
Dim childList As New List(Of Object)()
For Each row As GridViewRowInfo In Me.grdCNCFilesRad.ChildRows
Dim value = row.Cells(e.Column.Index).Value
If Not childList.Contains(value) Then
childList.Add(value)
End If
Next
Dim newPopup As New RadListFilterPopup(e.Column)
For Each item As System.Collections.ArrayList In ePopup.MenuTreeElement.DistinctListValues.Values
If Not childList.Contains(item(0)) Then
newPopup.MenuTreeElement.DistinctListValues.Remove(item(0).ToString())
End If
Next
e.FilterPopup = newPopup
End If
How can I do the same with a LINQ query?
I don't know what your variable grdCNCFilesRad is type of, but I assume it is no .NET type. But when I read ChildRows then I can be sure that this is some sort of enumeration (somewhere in it's inheritance tree must be the interface IEnumerable).
So you can include System.Linq and apply a AsQueryable() at your ChildRows.
The rest is just a little bit of Linq (Select, Where, ToList()). That's it!
Edit:
The first part should be solved by this:
Dim childList =
Me.grdCNCFilesRad.ChildRows
.AsQueryble()
.Select(Function(row) row.Cells(e.Column.Index).Value)
.Distinct()
There is no need of converting ForEach with Linq if you go for performance issues.
Your existing foreach code looks good.
Note: Don't think Linq is better compared to for-each in performance.

Linq ToList does nothing

I have Option Strict and Option Infer both set "On".
This code works fine:
Dim tBoxes = From t In MainForm.Frame2.Controls.OfType(Of TextBox).ToList
tBoxes.ToList().ForEach(Sub(c) c.DataBindings.Clear())
Why can't I combine them into the one line below (I believe it's related to the fact that the first line above does not set tBoxes to a list but remains an IEnumerable even though I am calling ToList, why is this?)
Dim tBoxes = From t In MainForm.Frame2.Controls.OfType(Of TextBox).ToList.ForEach(Sub(c) c.DataBindings.Clear())
This code results in an error
Expression does not produce a value
This might seem like much ado about nothing but it's not just the reduction to one line, I'd like to understand what's going on here.
VB.NET 2010
The problem is not the ToList call, but List.ForEach Method which is Sub, hence does not have a result and cannot be assigned to a variable.
If you want to use a single line, remove Dim tBoxes =.
Update In fact there is another problem in the above code.
Dim tBoxes = From t In MainForm.Frame2.Controls.OfType(Of TextBox).ToList
is equivalent to
Dim tBoxList = MainForm.Frame2.Controls.OfType(Of TextBox).ToList
Dim tBoxes = From t in tBoxList
so obviously tBoxes is IEnumerable<TextBox>.
Since the from t In .. part is unnecessary in this case, the "oneliner" should be something like this
MainForm.Frame2.Controls.OfType(Of TextBox).ToList.ForEach(Sub(c) c.DataBindings.Clear())
If you really need a query part, to avoid such confusions, don't forget to enclose it in (..) before calling ToList or other methods like Count, Any etc., like this
(from t In MainForm.Frame2.Controls.OfType(Of TextBox)).ToList.ForEach(Sub(c) c.DataBindings.Clear())
Small description but enough to understand
From t In MainForm.Frame2.Controls.OfType(Of TextBox) 'Filter all object of type text box
.ToList 'Convert IEnemerable(Of TextBox) to a IList type.
.ForEach(Sub(c) c.DataBindings.Clear())' Iterate through list and remove bindg of each text box
Issue is that .ForEach does not return any value so that there is nothing to assign the tBoxes object that you have created. It is just like a void method or Sub in VB.net.

Standard for using the Me keyword in VB.Net

I'm looking to find out if there is a standard or preferred way for using the (Me) keyword in VB.Net syntax.
Currently I know of 3 ways to use (Me).
Without the (Me) keyword:
Cursor = Cursors.WaitCursor
RadioButtonSortBySurname.Checked = True
LightGrid.SortColumn(2)
LightGrid.Columns(2).LastSortState = Ascending
LightGrid.SortColumn(1)
LightGrid.Columns(1).LastSortState = Ascending
Cursor = Cursors.Default
LightGrid.StatusRowText = ""
LightGrid.Select()
(Me) in a Using structure:
Using Me
Cursor = Cursors.WaitCursor
RadioButtonSortBySurname.Checked = True
LightGrid.SortColumn(2)
LightGrid.Columns(2).LastSortState = Ascending
LightGrid.SortColumn(1)
LightGrid.Columns(1).LastSortState = Ascending
Cursor = Cursors.Default
LightGrid.StatusRowText = ""
LightGrid.Select()
End Using
Using (Me) on each control:
Cursor = Cursors.WaitCursor
Me.RadioButtonSortBySurname.Checked = True
Me.LightGrid.SortColumn(2)
Me.LightGrid.Columns(2).LastSortState = Ascending
Me.LightGrid.SortColumn(1)
Me.LightGrid.Columns(1).LastSortState = Ascending
Cursor = Cursors.Default
Me.LightGrid.StatusRowText = ""
Me.LightGrid.Select()
There is one case where you have to use it, to help the compiler when the variable name is ambiguous:
Sub Foo(ByVal bar As Integer)
Me.Bar = bar
End Sub
Which assigns a field in the class from an argument that has the same name. Without Me. it assigns the argument value to itself, which compiles but is never what is intended. Not so uncommon in vb.net since it is case insensitive. Otherwise recommended, it can be painful to think of an argument identifier name that is different from the field name. Some programmers (and tools) favor always giving the field name a leading underscore to avoid this problem.
But the ones you presented in your question are a matter of personal taste. There are two benefits to prefixing Me., it helps code readability since it indicates scope and narrows down where the reader has to look for the declaration. And it really helps the IntelliSense popup narrow down the list of candidates, the feature I personally care about a great deal. It is up to you.
The decision is only yours, all ways are acceptable, but... Some tools Like ReSharper recomends you to don't use the Me (VB) or this (C#) keyword to make your code more legible and smaller.
In my case I avoid using the Me keyword, but as I said, the decision is only yours.
The ME is implied if you do not specifically use it. As Hans mentioned, sometimes you HAVE to use it because the scope of ME will be overridden by closer scoped names.
Like the others I only use it infrequently, usually when I cant remember what I called some control I just added two minutes ago... then I take it out again just because I don't care for the grammar LOL. I would have preferred "THIS" rather than ME.

For Each loop enumerator expression and memory consumption

According to the language specification guide for VB.NET Section 10.9.3
The enumerator expression in a for each loop is copied over into
memory.
If I have a list of 10000 objects that list will be in memory twice for the code below?
dim myList as new list(of bobs)
'put 10000 bobs in my list
for each x In myList
'do something
next
If I were generating the list from a linqQuery or some other such query it would make sense to generate that list at the for each loop statement thus not having the list in memory twice for example.
for each x in myList.where(function(x) x.name = Y)
'do something
next
If the LINQ query is unreadable on the for each loop, do I forgo readability and just put it on the for each loop declaration line?
Should I declare the list in its own variable and just bite the bullet and have the list exist twice in memory?
that list will be in memory twice for the code below
No, it won't. In your case, the spec is talking about the variable "x" here - not the entire collection. Remember, and enumerator (any IEnumerable<T> or similar) doesn't necessarily even have items in memory. When created via an iterator in C#, for example, you can have "collections" that are generated as you enumerate over them. There isn't a "list" of objects (necessarily) that could be copied, even if the language wanted to do so.
Is the linq query is unreadable on the for each loop
In many cases, I prefer filtering this way. You can just as easily move this outside of the loop, if you want to make it more clear, as well:
Dim filteredCollection = myList.Where(Function(x) x.name = Y)
For Each x in filteredCollection
There is no disadvantage to doing this if you find it more readable.

Linq to Datarow, Select multiple columns as distinct?

basically i'm trying to reproduce the following mssql query as LINQ
SELECT DISTINCT [TABLENAME], [COLUMNNAME] FROM [DATATABLE]
the closest i've got is
Dim query = (From row As DataRow In ds.Tables("DATATABLE").Rows _
Select row("COLUMNNAME") ,row("TABLENAME").Distinct
when i do the above i get the error
Range variable name can be inferred
only from a simple or qualified name
with no arguments.
i was sort of expecting it to return a collection that i could then iterate through and perform actions for each entry.
maybe a datarow collection?
As a complete LINQ newb, i'm not sure what i'm missing.
i've tried variations on
Select new with { row("COLUMNNAME") ,row("TABLENAME")}
and get:
Anonymous type member name can be
inferred only from a simple or
qualified name with no arguments.
to get around this i've tried
Dim query = From r In ds.Tables("DATATABLE").AsEnumerable _
Select New String(1) {r("TABLENAME"), r("COLUMNNAME")} Distinct
however it doesn't seem to be doing the distinct thing properly.
Also, does anyone know of any good books/resources to get fluent?
You start using LINQ on your datatable objects, you run the query against dt.AsEnumberable, which returns an IEnumerable collection of DataRow objects.
Dim query = From row As DataRow In ds.Tables("DATATABLE").AsEnumerable _
Select row("COLUMNNAME") ,row("TABLENAME")
You might want to say row("COLUMNNAME").ToString(), etc. Query will end up being an IEnumerable of an anonymous type with 2 string properties; is that what you're after? You might need to specify the names of the properties; I don't think the compiler will infer them.
Dim query = From row As DataRow In ds.Tables("DATATABLE").AsEnumerable _
Select .ColumnName = row("COLUMNNAME"), .TableName = row("TABLENAME")
This assumes that in your original sql query, for which you used ADO to get this dataset, you made sure your results were distinct.
Common cause of confusion:
One key is that Linq-to-SQL and (the Linq-to-object activity commonly called) LINQ-to-Dataset are two very different things. In both you'll see LINQ being used, so it often causes confusion.
LINQ-to-Dataset
is:
1 getting your datatable the same old way you always have, with data adapters and connections etc., ending up with the traditional datatable object. And then instead of iterating through the rows as you did before, you're:
2 running linq queries against dt.AsEnumerable, which is an IEnumerable of datarow objects.
Linq-to-dataset is choosing to (A) NOT use Linq-to-SQL but instead use traditional ADO.NET, but then (B) once you have your datatable, using LINQ(-to-object) to retrieve/arrange/filter the data in your datatables, rather than how we've been doing it for 6 years. I do this a lot. I love my regular ado sql (with the tools I've developed), but LINQ is great
LINQ-to-SQL
is a different beast, with vastly different things happening under the hood. In LINQ-To-SQL, you:
1 define a schema that matches your db, using the tools in in Visual Studio, which gives you simple entity objects matching your schema.
2 You write linq queries using the db Context, and get these entities returned as results.
Under the hood, at runtime .NET translates these LINQ queries to SQL and sends them to the DB, and then translates the data return to your entity objects that you defined in your schema.
Other resources:
Well, that's quite a truncated summary. To further understand these two very separate things, check out:
LINQ-to-SQL
LINQ-to-Dataset
A fantastic book on LINQ is LINQ in Action, my Fabrice Marguerie, Steve Eichert and Jim Wooley (Manning). Go get it! Just what you're after. Very good. LINQ is not a flash in the pan, and worth getting a book about. In .NET there's way to much to learn, but time spent mastering LINQ is time well spent.
I think i've figured it out.
Thanks for your help.
Maybe there's an easier way though?
What i've done is
Dim comp As StringArrayComparer = New StringArrayComparer
Dim query = (From r In ds.Tables("DATATABLE").AsEnumerable _
Select New String(1) {r("TABLENAME"), r("COLUMNNAME")}).Distinct(comp)
this returns a new string array (2 elements) running a custom comparer
Public Class StringArrayComparer
Implements IEqualityComparer(Of String())
Public Shadows Function Equals(ByVal x() As String, ByVal y() As String) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of String()).Equals
Dim retVal As Boolean = True
For i As Integer = 0 To x.Length - 1
If x(i) = y(i) And retVal Then
retVal = True
Else
retVal = False
End If
Next
Return retVal
End Function
Public Shadows Function GetHashCode(ByVal obj() As String) As Integer Implements System.Collections.Generic.IEqualityComparer(Of String()).GetHashCode
End Function
End Class
Check out the linq to sql samples:
http://msdn.microsoft.com/en-us/vbasic/bb688085.aspx
Pretty useful to learn SQL. And if you want to practice then use LinqPad
HTH
I had the same question and from various bits I'm learning about LINQ and IEnumerables, the following worked for me:
Dim query = (From row As DataRow In ds.Tables("DATATABLE").Rows _
Select row!COLUMNNAME, row!TABLENAME).Distinct
Strangely using the old VB bang (!) syntax got rid of the "Range variable name..." error BUT the key difference is using the .Distinct method on the query result (IEnumerable) object rather than trying to use the Distinct keyword within the query.
This LINQ query then returns an IEnumerable collection of anonymous type with properties matching the selected columns from the DataRow, so the following code is then accessible:
For Each result In query
Msgbox(result.TABLENAME & "." & result.COLUMNNAME)
Next
Hoping this helps somebody else stumbling across this question...