How to iterate a jagged array? - vb.net

This has been driving me crazy for a few days. Why doesn't the following work?
Dim arr(3, 3) As Integer
For y As Integer = 0 To arr.GetLength(0) - 1
For x As Integer = 0 To arr.GetLength(y) - 1
arr(y, x) = y + x
Next
Next
Also, what if the array looked like this instead?
{ {1, 2, 3},
{4, 5, 6, 7, 8, 9, 9, 9},
{5, 4, 3, 2}
}

Because there is no '2' or '3' dimension. Should be .GetLength(1) instead of .GetLength(y)
Also: in VB.Net array declarations work a little differently. The subscript you specify in the declaration is the last index, not the number of items created like with C# or C++. But the array is still 0-indexed like C# or C++, instead of 1-indexed like VB6. That means that if you move to VB.Net from a different language your array instincts are probably wrong, no matter which language it is. In VB.Net, Dim arr(3,3) As Integer actually creates a 4x4 array.

Ok, so what you really need is a "jagged array". This will allow you to have an "array that contains other arrays of varying lengths".
Dim arr As Integer()() = {New Integer() {1, 2, 3}, New Integer() {4, 5, 6, 7, 8, 9, 9, 9}, New Integer() {5, 4, 3, 2}}
For x = 0 To arr.GetUpperBound(0)
Console.WriteLine("Row " & x & " has " & arr(x).GetUpperBound(0) & " columns")
For y = 0 To arr(x).GetUpperBound(0)
Console.WriteLine("(" & x & "," & y & ") = " & arr(x)(y))
Next
Next
Output:
Row 0 has 2 columns
(0,0) = 1
(0,1) = 2
(0,2) = 3
Row 1 has 7 columns
(1,0) = 4
(1,1) = 5
(1,2) = 6
(1,3) = 7
(1,4) = 8
(1,5) = 9
(1,6) = 9
(1,7) = 9
Row 2 has 3 columns
(2,0) = 5
(2,1) = 4
(2,2) = 3
(2,3) = 2

arr.GetLength(y)
should be
arr.GetLength(1)

Well what if I had an array that looked like this
{ {1, 2, 3},
{4, 5, 6, 7, 8, 9, 9, 9},
{5, 4, 3, 2}
}
How would GetLength(1) still know the length of each row?
Basically what I want is.... a way to find the number of elements in any given row.

Dim arr(3, 3) As Integer
Dim y As Integer
Dim x As Integer
For x = 0 To arr.Rank - 1
For y = 0 To arr.GetLength(x) - 2
arr(x, y) = x + y
Next
Next
The above code worked for me.
Edit, the code feels dirty though. I'm wondering what it is you are trying to accomplish?

Your declaration: DIM arr(3,3) As Integer allready specifies that there are 3 elements in any given row (or 4, I'm not so sure about VB)
You could try:
Dim arr(3) as Integer()
You should then be able to do:
arr(n).Length
To find the length of row n.
I'm a bit rusty on VB6 and never learned VB.NET, but this should give you a 'jagged' array. Check out the msdn documentation on multidimensioned arrays.

This code en C# is to get all the combinations of items in a jagged array:
static void Main(string[] args)
{
bool exit = false;
int[] indices = new int[3] { 0, 0, 0 };
string[][] vectores = new string[3][];
vectores[0] = new string[] { "A", "B", "C" };
vectores[1] = new string[] { "A", "B" };
vectores[2] = new string[] { "B", "D", "E", "F" };
string[] item;
int[] tamaños = new int[3]{vectores[0].GetUpperBound(0),
vectores[1].GetUpperBound(0),
vectores[2].GetUpperBound(0)};
while (!exit)
{
item = new string[]{ vectores[0][indices[0]],
vectores[1][indices[1]],
vectores[2][indices[2]]};
Console.WriteLine("[{0},{1},{2}]={3}{4}{5}", indices[0], indices[1], indices[2], item[0], item[1], item[2]);
GetVector(tamaños, ref indices, ref exit);
}
Console.ReadKey();
}
public static void GetVector(int[] tamaños, ref int[] indices, ref bool exit)
{
for (int i = tamaños.GetUpperBound(0); i >= 0; i--)
{
if (tamaños[i] > indices[i])
{
indices[i]++;
break;
}
else
{
//ULTIMO ITEM EN EL ARRAY, VALIDAR LAS OTRAS DIMENSIONES SI YA ESTA EN EL ULTIMO ITEM
if (!ValidateIndexes(tamaños, indices))
indices[i] = 0;
else
{
exit = true;
break;
}
}
}
}
public static bool ValidateIndexes(int[] tamaños, int[] indices)
{
for (int i = 0; i < tamaños.Length; i++)
{
if (tamaños[i] != indices[i])
return false;
}
return true;
}
The output looks like
[0,0,0]=AAB
[0,0,1]=AAD
[0,0,2]=AAE
[0,0,3]=AAF
[0,1,0]=ABB
[0,1,1]=ABD
[0,1,2]=ABE
[0,1,3]=ABF
[1,0,0]=BAB
[1,0,1]=BAD
[1,0,2]=BAE
[1,0,3]=BAF
[1,1,0]=BBB
[1,1,1]=BBD
[1,1,2]=BBE
[1,1,3]=BBF
[2,0,0]=CAB
[2,0,1]=CAD
[2,0,2]=CAE
[2,0,3]=CAF
[2,1,0]=CBB
[2,1,1]=CBD
[2,1,2]=CBE
[2,1,3]=CBF

Related

Is there a way to sort a list of numbers whilst maintaining their original indexes in VB.net?

I need to sort a list of numbers but I need to keep their initial indexes.
I had previously just created an array of the numbers and then another array of the indexes which I sorted at the same time like so:
Dim AverageSuccess(23) As Decimal
Dim intervalList() As Integer = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}
x = 2
Do
Sorted = True
For i = 23 To x Step -1
If AverageSuccess(i) < AverageSuccess(i - 1) Then
TempNum = AverageSuccess(i)
AverageSuccess(i) = AverageSuccess(i - 1)
AverageSuccess(i - 1) = TempNum
TempIndex = intervalList(i)
intervalList(i) = intervalList(i - 1)
intervalList(i - 1) = TempIndex
Sorted = False
End If
Next
x += 1
Loop Until Sorted
however this is for a project and my teacher informed me that this is bad programming practise and I should be using a list instead.
I have struggled to find a simple example of how to use a list in VB.net for this purpose, so if someone could give me an example I would appreciate it.
I don't know how much you have covered about lists in class...
Let us create a list with some integers in:
Dim nums As New List(Of Integer) From {9, 8, 4, 5}
Now, we want to store the original indices of those numbers. We can do that with the Select method, which has an optional parameter that will give the index of the current item, and create a new entity with items which we can give names to, say "Num" and "Idx":
Dim numsWithIndex = nums.Select(Function(n, i) New With {.Num = n, .Idx = i})
Then we can use the LINQ method OrderBy to get those entities in the desired order:
Dim sortedNums = numsWithIndex.OrderBy(Function(nwi) nwi.Num)
And we can have a look at what we have constructed with
Console.WriteLine(String.Join(vbCrLf, sortedNums))
which outputs:
{ Num = 4, Idx = 2 }
{ Num = 5, Idx = 3 }
{ Num = 8, Idx = 1 }
{ Num = 9, Idx = 0 }
(It shows the names we gave to the properties of the anonymous type created with New earlier.)
Here is the whole thing as a console app you can copy-and-paste to investigate with on your computer:
Module Module1
Sub Main()
Dim nums As New List(Of Integer) From {9, 8, 4, 5}
Dim numsWithIndex = nums.Select(Function(n, i) New With {.Num = n, .Idx = i})
Dim sortedNums = numsWithIndex.OrderBy(Function(nwi) nwi.Num)
Console.WriteLine(String.Join(vbCrLf, sortedNums))
Console.ReadLine()
End Sub
End Module

Sort 3 x one dimensional arrays in parallell

I have 3 x one dimensional arrays of type integer, each array is the same length (between 5 - 20 million values) and I would like to sort them in parallel (ie keeping the relative position) by the second array, then the third array, then the first array. Does anyone have any ideas on the most efficient way to do this in vb.net?
If it helps, the first array is just a record of the initial position (the three arrays will be re-sorted to this order once various calculations are done). I need to sort them to determine the number of unique combinations of the second and third arrays (a combination is determined by the position in the array - in the example below the combinations are (0-1-4), (1-1-6) etc). Once that is determined I will resort them back based on the first array.
I looked at array.sort but that only covers 2 parallel arrays. I'm a bit wary of putting the values in tuples (or any other format), as it would (I assume - perhaps not) be a big overhead when converting 5 - 20 million records before processing.
For example:
Record Number Array: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
Link Array: {1, 1, 2, 1, 1, 2, 2, 2, 1, 2}
Line Number Array: {4, 6, 3, 5, 6, 7, 3, 2, 3, 4}
Having sorted / ordered by the second, then third, then first array, the expected output would be:
Record Number (1st Array): {8, 0, 3, 1, 4, 7, 2, 6, 9, 5}
Link Array (2nd Array): {1, 1, 1, 1, 1, 2, 2, 2, 2, 2}
Line Number Array (3rd Array): {3, 4, 5, 6, 6, 2, 3, 3, 4, 7}
Array.sort only allows you to sort 2 arrays in parallel and i'm a little confused by the options available in LinQ.
Does anyone have any suggestions on the best way to solve this problem?
Cheers,
I think you can get a backup of your Link Array. Then sort Link Array with Record Array, then you can sort un-sorted Link Array with Line Number Array. Can you please try it?
You didn't say if you want efficient in terms of time or space, but this takes about 3 seconds to transfer 20 million records from arrays to a list, and then about 30 seconds to do the sort on them, using less than 1GB of RAM. On my computer.
Option Infer On
Module Module1
Class Grouped
Property RecNo As Integer
Property Link As Integer
Property LineNo As Integer
Public Overrides Function ToString() As String
Return String.Format("({0}, {1}, {2})", Me.RecNo, Me.Link, Me.LineNo)
End Function
End Class
Sub Main()
' First try with the test data to show the correct result is obtained
Dim recNos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
Dim links = {1, 1, 2, 1, 1, 2, 2, 2, 1, 2}
Dim lineNos = {4, 6, 3, 5, 6, 7, 3, 2, 3, 4}
' transfer the arrays to a List
Dim xs As New List(Of Grouped)
xs.Capacity = recNos.Length
For i = 0 To recNos.Length() - 1
xs.Add(New Grouped With {.RecNo = recNos(i), .Link = links(i), .LineNo = lineNos(i)})
Next
' sort the data
Dim ys = xs.OrderBy(Function(x) x.Link).ThenBy(Function(x) x.LineNo).ToList()
Console.WriteLine(String.Join(", ", ys))
' Now try with twenty million records
Dim rand = New Random()
Dim nRecs As Integer = 20000000
recNos = Enumerable.Range(0, nRecs - 1).ToArray()
ReDim links(nRecs - 1)
ReDim lineNos(nRecs - 1)
For i = 0 To nRecs - 1
links(i) = rand.Next(0, 9)
lineNos(i) = rand.Next(1, 9)
Next
Dim sw As New Stopwatch
sw.Start()
xs.Clear()
xs.Capacity = nRecs
For i = 0 To recNos.Length() - 1
xs.Add(New Grouped With {.RecNo = recNos(i), .Link = links(i), .LineNo = lineNos(i)})
Next
sw.Stop()
Console.WriteLine(sw.ElapsedMilliseconds.ToString())
sw.Restart()
ys = xs.OrderBy(Function(x) x.Link).ThenBy(Function(x) x.LineNo).ToList()
sw.Stop()
Console.WriteLine(sw.ElapsedMilliseconds.ToString())
Console.ReadLine()
End Sub
End Module

Unique Combination of Set

I'm trying to write vb.net code to return unique combinations for set
My set contains 3 different elements. I've found similar post this Post but couldn't find any VB solution to get this results
Example:
Elements: 1, 2, 3
{ 1, 2, 3}
Result must be
1
2
3
12
13
23
123
...........
>...................
i,m trying to achieve this by using following code
Function GetCombinations(ByVal depth As Integer, ByVal values As String()) As IEnumerable(Of String)
If depth > values.Count + 1 Then Return New List(Of String)
Dim result = New List(Of String)
For i = 0 To depth - 1
For y = 0 To values.Count - 1
If i = 0 Then
result.Add(values(y))
Else
result.Add(values(i - 1) + values(y))
End If
Next
Next
Return result
End Function
To Get the result
Dim reslt = GetCombinations(4, data_array)
?reslt
Count = 12
(0): "1"
(1): "2"
(2): "3"
(3): "11"
(4): "12"
(5): "13"
(6): "21"
(7): "22"
(8): "23"
(9): "31"
(10): "32"
(11): "33"
Hint:
I work with Mathematics and manage to calculate no of combinations. i can test the out with
this formula
Example
there is this formula called nCr. it means out of n number of element how many way of taking r number of elements with unique combinations of r.
nPr = n!/(n-r)!
n! = 1 * 2 * 3 * 4* ... (n-1) * n
Elements: 1, 2, 3
In this case n = 3 and r can be 1, 2, and 3 all
number of combinations = 3P1 + 3P2 + 3P3
= 3!/2! + 3!/1! + 3!/0!
= 6/2 + 6/1 + 6/1 (0!=1)
= 3+6+6
= 15
Knowing the term makes it easier to find existing algorithms. What you're looking for is a power set. Here's a quick VB.NET translation of a C# implementation I found on Rosetta Code:
Public Function GetPowerSet(Of T)(ByVal input As IEnumerable(Of T)) As IEnumerable(Of IEnumerable(Of T))
Dim seed As IEnumerable(Of IEnumerable(Of T)) = {Enumerable.Empty(Of T)()}
Return input.Aggregate(seed, Function(a, b) a.Concat(a.Select(Function(x) x.Concat({b}))))
End Function
Testing:
For Each x In GetPowerSet({1, 2, 3})
Console.WriteLine(String.Join(", ", x))
Next
Output:
1
2
1, 2
3
1, 3
2, 3
1, 2, 3
EDIT - Based on your latest explanation, I think you need a different approach. It seems you want combinations with repetitions / replacement, for all sizes up to the input size. You could simply call one of those algorithms with parameters (S, k) for each value of k from 1 to n and join all the results into a single set of results.
Translating Python's algorithm:
Public Iterator Function GetCombinationsWithReplacement(Of T)(source As IEnumerable(Of T), size As Integer) As IEnumerable(Of IEnumerable(Of T))
Dim pool = source.ToList()
Dim n = pool.Count
If n = 0 AndAlso size > 0 Then
Return
End If
Dim indices = Enumerable.Repeat(0, size).ToList()
Yield indices.Select(Function(i) pool.Item(i))
While True
Dim index As Nullable(Of Integer) = Nothing
For i = size - 1 To 0 Step -1
If indices.Item(i) <> n - 1 Then
index = i
Exit For
End If
Next
If Not index.HasValue Then
Return
End If
indices = indices.Take(index.Value).Concat(Enumerable.Repeat(indices.Item(index.Value) + 1, size - index.Value)).ToList()
Yield indices.Select(Function(i) pool.Item(i))
End While
End Function
(You will need to modify this if your VB.NET compiler doesn't support Yield.)
The results of calling this with different sizes are:
GetCombinationsWithReplacement({1, 2, 3}, 1):
{1}
{2}
{3}
GetCombinationsWithReplacement({1, 2, 3}, 2):
{1, 1}
{1, 2}
{1, 3}
{2, 2}
{2, 3}
{3, 3}
GetCombinationsWithReplacement({1, 2, 3}, 3):
{1, 1, 1}
{1, 1, 2}
{1, 1, 3}
{1, 2, 2}
{1, 2, 3}
{1, 3, 3}
{2, 2, 2}
{2, 2, 3}
{2, 3, 3}
{3, 3, 3}
We can join these into a single sequence with all 19 subsets:
Public Iterator Function GetCombinationsWithReplacementAllSizes(Of T)(source As IEnumerable(Of T)) As IEnumerable(Of IEnumerable(Of T))
Dim pool = source.ToList()
For size = 1 To pool.Count
For Each subset In GetCombinationsWithReplacement(pool, size)
Yield subset
Next
Next
End Function
Here is some pseudo code for any number of elements to help you (I make no claim this is the fastest way to do this, it is just a way to do it.)
Given elementList is a list of elements given
Given tempList is a list as a temporary holder
Given resultList is a list of the result
loop by # items in elementlist
{
if tempList is empty // special case for first iteration
add each element of elementList to tempList and resultlist
else
{
for each element in templist
for each element2 in elementlist
add combo to result list
copy elements added to result list for this iteration to templist
}
}

Comparing two arrays in vb.net

I need to do Reverse Intersection operation with two arrays and save the result in a different array
Eg: Array A {1, 2, 3}; Array B {1, 2, 3, 4, 5, 6} Resultant Array Should be {4, 5, 6}
I Tried out the following logic but didn't work
int k = 0;
int a[2] = {1,10};
int p[10];
int roll[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i = 0; i < 2; i++)
{
for (int j = 1; j <= 10; j++)
{
if (a[i] == roll[j])
{
break;
}
else
{
p[k] = 0;
p[k] = roll[j];
k++;
}
}
}
I need it for my vb.net project
I don't understand how that C# code is related to your VB.NET problem, if you only want to find integers which are in one array and not in the other, use Enumerable.Except:
Dim intsA = {1, 2, 3}
Dim intsB = {1, 2, 3, 4, 5, 6}
Dim bOnly = intsB.Except(intsA).ToArray()
Try something like this if you can't use Linq:
Function RevIntersect(arr1() As String, arr2() As String) As String()
Dim sResult, aux As New List(Of String)()
aux.AddRange(arr1)
aux.AddRange(arr2)
For Each elem As String In aux
If (Not arr1.Contains(elem) OrElse Not Arr2.Contains(elem)) AndAlso _
Not sResult.Contains(elem) Then
sResult.Add(elem)
End If
Next
Return sResult.ToArray()
End Function

Exception was thrown when adding new rows to datatable

listA is list of integer
dtListB is DataTable
I'm checking that if StudentId of ListA doesn't exist in dtListB, then add it to the dtListB.
Dim stidListA, stIdListB As Integer
For Each la In listA
stidListA = la.StudentId
For n = 0 To dtListB.Rows.Count - 1 Step 1
stIdListB = dtListB.Rows(n)("StudentId")
If stIdListB <> stidListA Then
dtListB.Rows.Add(New Object() {la.StudentId, la.AssignedId, la.TimeSheetId, 0})
End If
Next
Next
I'm not sure why the error is thrown :
Exception of type 'System.OutOfMemoryException' was thrown.
dtListB.Rows.Add(New Object() {la.StudentId, la.AssignedId, la.TimeSheetId, 0})
Any suggestion? Thanks.
You are iterating with dtListB.Rows and inserting into dtListB.Rows. It is increasing the Count and just looping for an indeterminate amount of time.
The problem is that you aren't really checking if StudentId of ListA doesn't exist in dtListB. What your code actually does is it compares EVERY element in dtListB with the current element from ListA and if in case they are not the same, it adds a new row to dtListB. So assume you have the following lists:
ListA = { 3, 4 };
dtListB = { 1, 2, 3 };
The first iteration for ListA, value is 3.
Check: (3 <> 1) and add '3' to dtListB (dtListB = { 1, 2, 3, 3 })
Check: (3 <> 2) and add '3' to dtListB (dtListB = { 1, 2, 3, 3, 3 })
Check: (3 <> 3) and don't do anything.
(Since the list has grown, you have two more iterations)
For the second iteration for ListA with value 4 you will insert it 5 times for every element of the existing array you compare with, and get the resulting list: { 1, 2, 3, 3, 3, 4, 4, 4, 4, 4 }. Obviously that's not the result you are expecting, and you probably have larger lists and run out of memory.
I would suggest keeping a Boolean flag itemFound when looping through dtListB. Set it to False before the loop and if a matching item is encountered, set it to True. After the loop, check if itemFound is False and if so, add the item to the list. This probably isn't valid VB syntax, but you get the idea:
For Each la In listA
stidListA = la.StudentId
Dim itemFound as Boolean = False
For n = 0 To dtListB.Rows.Count - 1 Step 1
stIdListB = dtListB.Rows(n)("StudentId")
If stIdListB <> stidListA Then
itemFound = True
Break //(is there Break in VB?)
End If
Next
If Not itemFound Then
dtListB.Rows.Add(New Object() {la.StudentId, la.AssignedId, la.TimeSheetId, 0})
End If
Next