Combining items within an array in VB.NET - arrays

I'm very new to VB.net and I have an array, like so,
points = [[1,2],[5,6],[7,8],[9,10]]
The result I want is to be able to break this up into pairs, starting with the first item and then combining it with the next item, until the last item is paired with the previous item, like so:
new points = [[[1,2],[5,6]],[[5,6],[7,8]],[[7,8],[9,10]]]
Doing it without a loop is preferred. Being a python guy, i would do this using a list comprehension with array slicing, I'm interested to see how it would look like in VB.
thanks for your help.

One option would be:
Dim pointPairs = Enumerable.Range(0, points.Length - 2).Select(Function(n) Tuple.Create(points(n), points(n + 1)))
That will give you an IEnumerable(Of Tuple(Of Point, Point)). You could then enumerate that directly or call ToArray or ToList, e.g.
Dim pointPairs = Enumerable.Range(0, points.Length - 2).
Select(Function(n) Tuple.Create(points(n), points(n + 1))).
ToArray()

Related

VB.net How to access row in 2 dimensional array?

I want to read all elements of the first row from a 2-D array in VB.NET. Is there a simple way to do it rather than running a loop. I know that in MATLAB there is possibilty to say: A(1,:) and its done.
Thank you.
There is nothing built into the array itself to read without a loop but you could use a LINQ query, e.g.
Dim firstRow = Enumerable.Range(0, myArray.GetUpperBound(1) + 1).
Select(Function(i) myArray(0, i))
Note that what is a "row" and what is a "column" is not defined for a 2D array so it's really up to you whether you want to use that or this:
Dim firstRow = Enumerable.Range(0, myArray.GetUpperBound(0) + 1).
Select(Function(i) myArray(i, 0))
That gives you an enumerable list of the specified elements so the question is, what do you want to do with it? You haven't actually specified. As it is, all you can really do is enumerate it, which means running a For Each loop over it. That kinda defeats the purpose of avoiding a loop in the first place. If you're saying, without actually saying, that you want those elements in a 1D array then you can call ToArray:
Dim firstRow = Enumerable.Range(0, myArray.GetUpperBound(1) + 1).
Select(Function(i) myArray(0, i)).
ToArray()
EDIT:
Here is an extension method that you could use to do the job:
Imports System.Runtime.CompilerServices
Public Module ArrayExtensions
<Extension>
Public Function GetRow(Of T)(source As T(,), rowIndex As Integer) As T()
Return Enumerable.Range(0, source.GetUpperBound(1) + 1).
Select(Function(i) source(rowIndex, i)).
ToArray()
End Function
End Module
It then makes the calling code much cleaner, especially if you need to do it multiple times, e.g.
Dim firstRow = myArray.GetRow(0)
If you don't know size of your array u can use nested loop For Each like this:
For each row in array
For each col in row
''something here like Debug.Write(col.ToString)
Next
Next
If you know your size and you want specific item in the array you simply do that like in any other loop "for" like this:
For index As Integer = 5 To 5
For index2 As Integer = 3 To 3
Debug.Write(index2.ToString)
Next
Next

VBA Associative Arrays (How to Index)

I'm a newbie to VBA so please forgive my lack of experience.
Im using excel VBA and trying to figure out how to index an array. I'm importing a CSV and using the split function. I need to access each individual items of the items split into the array(s). The best way to explain what I need is an example like this from Actionscript:
var a:Array = [];
a[1] = "Hello";
a[2] = "World";
(Except that what I have is a dynamic array created by the SPLIT function)
Where I could access "Hello" with the var a[1]
Here is what I have so far:
Sub getTxtfile()
FilePath = Application.GetOpenFilename _
(Title:="Please choose a file to open", _
FileFilter:="CSV Files *.csv* (*.csv*),")
Open FilePath For Input As #1
row_number = 0
Do Until EOF(1)
Line Input #1, LineFromFile
LineItems = Split(LineFromFile, ",")
'ActiveCell.Offset(row_number, 0).Value = LineItems(1)
'ActiveCell.Offset(row_number, 1).Value = LineItems(0)
row_number = row_number + 1
'Debug.Print LineItems(0) & ": " & LineItems(1)
Loop
Close #1
End Sub
I now have 2 arrays (LineItems(0) & LineItems(1)) but how do I index what is inside of them at this point?
Thanks for any and all help, it is greatly appreciated.
Mike
The CSV I'm using is formatted to use with other applications SolidWorks, python, etc.) besides Excel. I need to access only certain elements within the array to populate certain cells. As it is...I can pull the entire array into columns but I don't want to do that, just the ones I need. Here is a sample of the CSV:
0,.200
p,1.0709
q,1.167
r,1.177
s,1.216
t,1.570
u,1.5843
v,1.6883
w,1.9079
e,.2645
What I want to do is reference the letter in the first element and have the second element inserted in a certain cell: Reference "t" through an index and have "1.570" inserted.
The elements in my arrays are LineItems(0) and LineItems(1). So ideally I'm looking to reference each indexed item in an element as LineItems(1)(a) / LineItems(1-a) or something similar to that.
I think the commented-out lines in your code should actually work, at least as far as array access is concerned. (However, I may not fully understand what you are trying to accomplish. Would you please edit your question to clarify?) I do recommend adding
Option Explicit
Option Base 0
at the top of your file, and
Dim LineItems as Variant
before the Split call. That way the compiler will help you find errors.
However, If what you really want is to open a CSV, please allow me to suggest:
Dim wb as Workbook
Workbooks.OpenText Filename:="<filename>", DataType:=xlDelimited, Comma:=True
Set wb = Workbooks(Workbooks.Count)
which will give you a new workbook wb with the CSV parsed and ready to be accessed just like any other worksheet (docs on MSDN).
You can have associative arrays in VBA with Scripting.Dictionary object or the .NET System.Collections.HashTable, but that seems a bit overkill.
You can use Jagged Arrays (Arrays of Arrays) like this:
Line = "0,.200 p,1.0709 q,1.167 r,1.177 s,1.216 t,1.570 u,1.5843 v,1.6883 w,1.9079 e,.2645"
LineItems = Split(Line, ",")
Dim LineSubItems() ' has to be Variant or Variant() array
ReDim LineSubItems(0 To UBound(LineItems))
For i = 0 To UBound(LineItems)
LineSubItems(i) = Split(LineItems(i), " ")
Next
Debug.Print LineSubItems(1)(1) ' "p"

Can I Create an Array of Points Arrays in Visual Basic

Dim myPointsArray As New List(Of Point) creates an array that I can fill with a For Next loop but I have several separate arrays that I want to fill so it would be great if Dim myPointsArrays(20) as.... would work so that I don't have to Dim a points array for each of my separate arrays. Then I could fill them with a nested For Next loop.
This is me filling one array
For i = 1 To 6
myPointsArray.Add(New Point(Ox + HxPnt(4, i) / skale, Oy - HyPnt(4, i) / skale))
Next
Here Ox,Oy represent an origin, HxHyPnts are members of an array of points, skale is used to scale the global values to drawing with pixels.
Problem is that I need to draw lots of different polylines and polygons from dozens of arrays.
You can create nested lists (List(Of T) is not an Array):
Dim myPointsArray As New List(Of List(Of Point))
For i = 1 To 6
Dim innerList = new List(Of Point)
myPointsArray.Add(innerList)
For j = 1 to 10
innerList.Add(New Point(Ox + HxPnt(4, i) / skale, Oy - HyPnt(4, i) / skale))
Next
Next
To iterate again over these values use the following:
For Each list As List(Of Point) in myPointsArray
For Each p As Point in list
// Access p here
Next
Next
Got it!
I created a new points array variable (newPoints), filled it from the list then converted it with the .ToArray thingy. Then drew each polyline in the loop by filling, clearing and refilling the array. Is this the best way to do this. Seems good to me.
For Each list As List(Of Point) In myPointsArray
For Each p As Point In list
newPoints.Add(New Point((Ox + p.X) / skale, (Oy + p.Y) / skale))
Next
e.Graphics.DrawLines(myPen, newPoints.ToArray)
newPoints.Clear()
Next

How to iterate an array of a dynamic size in a loop

I am using VB.NET.
I would like have an array called A1 and I will perform for loop inside that array.
In the middle of the for loop, I need to remove an item from that A1 array.
I am aware that if I remove that object from A1 array, the program will crash (out of bounds error message).
Which array variable in VB.NET would allow me to perform the task above?
Code sample is most welcomed!
Thank you.
The easiest way would be to use a list instead of an array (or convert the array you have to a List(of T).
The trick then is to move from the end of the list to the front instead of the other way around.
For example:
Sub Main()
Dim RndGenerator as New Random
Dim a As New List(Of Double)
For i = 0 To 99
a.Add(RndGenerator.NextDouble() * 10) 'Populate the list
Next
For i = a.Count - 1 To 0 Step -1 'This loop performs the deletion.
If a(i) > 5 Then a.RemoveAt(i)
Next
Console.ReadKey() 'Or debugger.Break to look at the result
End Sub
This will populate a list with random numbers from 0 to 10. It then removes all numbers >5 from the list.
Edit:
Good point from Steven Doggart on not using VB6 Relics. Edited the code to use the System.Random class instead.

VB storing text from txt file to 2D array using Comma Delimiter

I've searched for an answer, but struggling to adapt my findings into my code/text file.
I have this text file;
20,Ben
10,Dave
7,Bob
Scores and names.
I want pull the data from the text file into a 2D array, for example:
array(0, 0)
array(1, 0)
array(0, 1)
array(1, 1)
array(0, 2)
array(1, 2)
which would translate to;
array(20)
array(Ben)
array(10)
array(Dave)
array(7)
array(Bob)
Thanks in advance
As mentioned in my comment,i would use a class for this.
For example Player with two properties: Name As String and Score As Int32. Then you can create a List(Of Player) instead. That's much more readable, reusable and maintainable and also less error-prone than fiddling around with indices.
Public Class Player
Public Property Name As String
Public Property Score As Int32
End Class
Here is a readable LINQ query that initializes a List(Of Player) from lines in a text-file:
Dim allPlayers = From line In System.IO.File.ReadLines("path")
Let Columns = line.Split(","c)
Where Columns.Length = 2
Let Score = Int32.Parse(Columns(0).Trim())
Let Name = Columns(1).Trim()
Select New Player With {.Score = Score, .Name = Name}
Dim playerList As List(Of Player) = allPlayers.ToList()
If you need an array use ToArray instead of ToList.
You can access alist like an array via indexer(list(0)) or via LINQ methods:
Dim firstPlayer As Player = playerList.FirstOrDefault() ' is Nothing if there are no players '
Console.WriteLine("Name of the player: " & firstPlayer.Name)
Console.WriteLine("His score is: " & firstPlayer.Score)
or in a loop:
For Each player In playerList
Console.WriteLine("Player {0} has scored {} points.", player.Name, player.Score)
Next
By the way, LINQ is useful to keep your code readable, under the hood it also uses loops. So you could simply use OrderByDescendending to order your players by score and output the top 3 with the highest score:
Dim best3Players = From player In playerList
Order By Player.Score Descending
Take 3
Select String.Format("{0}({1})", player.Name, player.Score)
Dim output = String.Join(", ", best3Players)
Windows.Forms.MessageBox.Show(output) ' since you have mentioned messagebox '
If you know the file is not going to be terribly big, the simplest approach would be to simply use the File.ReadAllLines method, which returns a single-dimension array of strings containing one item per line in the file. You could then loop through each line and use the String.Split method to split apart the two values from the line, for instance:
Dim lines() As String = File.ReadAllLines("leaderboard.csv")
Dim values(lines.Length - 1, 1) As String
For i As Integer = 0 to lines.Length - 1
Dim parts() As String = lines(i).Split(","c)
values(i, 0) = parts(0)
values(i, 1) = parts(1)
Next
However, it would be better if you created a class to store all of the data from each row, like this:
Public Class LeaderboardEntry
Public Property PlayerName As String
Public Property Score As Integer
End Class
Then, you could load the values into a list of those objects, like this:
Dim entries As New List(Of LeaderboardEntries)()
For Each i As String In File.ReadAllLines("leaderboard.csv")
Dim parts() As String = lines(i).Split(","c)
Dim entry As New LeaderboardEntry()
entry.Score = parts(0)
entry.PlayerName = parts(1)
entries.Add(entry)
Next
Then, instead of saying values(0, 0) to get the score of the first entry, you would say entries(0).Score, which is much more readable, especially if you add more fields to each line in the file. For more information on the value of using a class for something like this, check out my answer to this question.
Bear in mind however, that the above code assumes that each line is properly formatted. If the file contained any lines that did not include a comma, it would fail. If you need to handle that situation, you'd need to add some extra checking. Also, in the above scenario, the player name cannot contain any commas. The best way to handle that would be to use proper CSV formatting, like this:
30,"Doe, John"
And then your quotes would need to be escaped, etc., in which case, it would be worth using the TextFieldParser class to read the CSV file rather than reinventing the wheel. Another option would be to simply disallow the users from entering a comma in their name. If you take that approach, however, it would be better to pick a less common character for your delimiter, such as a pipe symbol (|).
Instead of using a CSV file, I would recommend using an XML file. Then you could easily store the data, even if it grows in complexity, and read and write to it using either the XmlSerializer, XmlDocument, or XDocument.
If all you need to do is store simple values like this, though, an even better option may to be to use the My.Settings feature.

Resources