Open AdamSpeight2008 opened 4 years ago
@everyone - leaving here now, you can reach me on twitter.
I want a clever way to do myList(myList.Count - 1)
.
I am not sure that myList.Last
is equivilant, since IEnumerable
doesn't use index to het elements, but iterates through the whole sequence!
So, I want a syntax that yields exactly myList(myList.Count - 1)
Noting that I don't like C# range in this matter. It confuses me.
So, any suggestions?
I want a clever way to do
myList(myList.Count - 1)
. I am not sure thatmyList.Last
is equivilant, sinceIEnumerable
doesn't use index to het elements, but iterates through the whole sequence! So, I want a syntax that yields exactlymyList(myList.Count - 1)
Noting that I don't like C# range in this matter. It confuses me. So, any suggestions?
I think the official line is that if your using myList(myList.Count -1)
, then you're doing it wrong and you should be using a for each
loop.
But I agree with your sentiment. List should provide a LastIndex
or UpperBound
property. But that's also not really a VB problem, it's a library problem.
@pricerc In my use case, I only care about first and last items, as the list contains the xml nodes, and I want first to be sure that the root (the last element in the list, is my "vbxml' tag to provide html5 auto complete in XML literals:
Dim parents = token.GetAncestors(Of XmlElementSyntax)( )
If parents.Count = 0 OrElse GetStartTagName(parents(parents.Count - 1)) <> VbXmlRootName Then
Return Nothing
End If
@VBAndCs That's fine.
I think your point about Last()
is valid though; IEnumerable
should be able to just skip to that last one if the underlying list supports it.
But that's also not really a VB problem, it's a library problem.
Yes it is. C# introduces range indexers to address this. So, in C# you can use parents(^1)
instead of parents(parents.Count - 1)
.
If such a syntax comes to VB, I would prefer to use #
to refer the NUMBER of elements in the collection (as ^
used in arithmetic operations), so we can subtract numbers from #
to get the desired index: parents(# - 1)
. In my opinion parents(# - 2)
is more readable than C#'s parents(^2)
.
I think your point about Last() is valid though; IEnumerable should be able to just skip to that last one if the underlying list supports it.
It is not easy to do, since IEnumerable is unaware of the source of the sequence. Defanging indexes in the language is easier.
A Count
keyword will be better than #
.
@VBAndCs I solved this problem by writing new extension functions.
Imports System.Runtime.CompilerServices
Module RangeExtensions
<Extension>
Function Last(Of T)(list As IReadOnlyList(Of T)) As T
Return list.ItemFromEnd(1)
End Function
<Extension>
Function ItemFromEnd(Of T)(list As IReadOnlyList(Of T), index As Integer) As T
Return list(list.Count - index)
End Function
End Module
Usage:
Module Program
Sub Main()
Dim items = {1, 2, 3, 4, 5}
Console.WriteLine("Data")
Console.WriteLine(String.Join(", ", items))
Console.WriteLine("The last item")
Console.WriteLine(items.Last)
Console.WriteLine("The item 2 from end")
Console.WriteLine(items.ItemFromEnd(2))
End Sub
End Module
Output
Data
1, 2, 3, 4, 5
The last item
5
The item 2 from end
4
@pricerc I looked through many examples in my code and the reason I to use For i = vs. For Each is in many cases I need i, usually because I want the last 2 entries or I need i like to print "{i + 1} of {Last +1}". In the first case ^ or # in VB would be great. If VB used # it might be possible to use it within the for loop to solve the second issue. @Nukepayload2 I love the extension but my issue is that list.Count is constantly being recalculated. I don't know if the C# implementation caches this value but it should be able to since lists can't be modified within the loop.
<Extension>
Function ItemFromEnd(Of T)(list As IReadOnlyList(Of T), index As Integer) As T
Return list(list.Count - index)
End Function
@paul1956 That's why I referred to the official line - because real life is never like the theory.
Like database theory purists say that you should not have null values. In practice, they're invaluable.
In an IReadOnlyCollection(Of T)
.Count
is a property and isn't calculated on the fly (in most use cases). ICollection(Of T)
requires an indexer, so .First
and .Last
would be O(1)
most of the time (eg Array / List).
With a little bit of thought It is possible write and extension method on IEnumerable(Of T)
to return the first and last items in a single pass (assuming it isn't an infinite enumerable source)
<Extension>
Function FirstAndLast(Of T)(source As IEnumerable(Of T), Optional allowSame As Boolean = True) As (First As T, Last As T)?
Dim result As (First As T, Last As T)? = Nothing
Dim isFirst = True
For Each x In source
If isFirst Then
result = (x, If(allowSame, x, Nothing))
isFirst = False
Else
result = (result.Value.First, x)
End If
Next
Return result
End Function
A block of pre processor so out of place, I understand it hasn't specify font color on keyword in old version but now it should be same level tab space as other line in code block.
There are always areas of Visual Basic (.net) that are a pain in the "code-hole" to write or debug. Let's create post of them, so potential contributors can work on solving them for you.
What are yours? And the mythical code wizard, could fix by doing what instead.