How to make series with a given array? - arrays

I am a VB.Net user. I would like to make an array as per below
Dim QRList() As Integer = {1, 2, 3, 4, 9, 12, 13, 14, 15, 16, 41, 42, 44, 48, 49, 50}
into a series as per below
1 - 4,
9 - 9,
12 - 16,
41 - 42,
44 - 44,
48 - 50,
I have tried the code below. But it doesn't work.
Dim QRList() As Integer = {1, 2, 3, 4, 9, 12, 13, 14, 15, 16, 41, 42, 44, 48, 49, 50}
Dim QRCount As Integer = QRList.Length
Dim startIndex, startN, endN As Integer
For i As Integer = 0 to QRCount - 1
startindex = 0
i = startindex
startN = QRList(i)
Do
If startindex = QRCount - 1 Then
endN = QRList(i)
ElseIf startindex < QRCount - 1 Then
If QRList(i + 1) - QRList(i) + 1 = 1 Then
endN = QRList(i + 1)
ElseIf QRList(i + 1) - QRList(i) + 1 > 1 Then
endN = QRList(i)
Exit For
End If
End If
startindex = i + 1
Console.Writeline(CStr(startN) & " - " & CStr(endN) & vbNewLine)
Loop While startindex < QRCount
Next
Hope anyone can help on this issue, Thanks.
Austin HO

This is a perfect candidate for the Yield statement to return an IEnumerable(Of String). You can pass that resulting IEnumerable(Of String) to String.Join() to obtain the final result.
Here's a quick example:
Sub Main()
Dim QRList() As Integer = {1, 2, 3, 4, 9, 12, 13, 14, 15, 16, 41, 42, 44, 48, 49, 50}
Dim series As String = String.Join("," & vbCrLf, GetSeries(QRList))
Console.WriteLine("Original: " & String.Join(", ", QRList))
Console.WriteLine("Series:")
Console.WriteLine(series)
Console.Write("Press Enter to quit...")
Console.ReadLine()
End Sub
Public Iterator Function GetSeries(ByVal values() As Integer) As IEnumerable(Of String)
Dim startNum, endNum As Integer?
For Each value As Integer In values
If Not startNum.HasValue Then
startNum = value
endNum = value
ElseIf value = (endNum + 1) Then
endNum = value
Else
Yield startNum & " - " & endNum
startNum = value
endNum = value
End If
Next
Yield startNum & " - " & endNum
End Function
Producing the following output:
Original: 1, 2, 3, 4, 9, 12, 13, 14, 15, 16, 41, 42, 44, 48, 49, 50
Series:
1 - 4,
9 - 9,
12 - 16,
41 - 42,
44 - 44,
48 - 50
Press Enter to quit...
Just to make it extra clear what is happening, you can convert the iterator to an array like this:
Dim arrSeries() As String = GetSeries(QRList).ToArray
For i As Integer = 0 To arrSeries.Length - 1
Console.WriteLine(i & ": " & arrSeries(i))
Next
Producing:
0: 1 - 4
1: 9 - 9
2: 12 - 16
3: 41 - 42
4: 44 - 44
5: 48 - 50
And, finally, if you need to work with the actual Integer start and stop values as a set, then simply convert the iterator function to return IEnumerable(Of Tuple(Of Integer, Integer)) instead, and use this Yield statement in the function in both places:
Yield New Tuple(Of Integer, Integer)(startNum, endNum)

Your loop fails at the first comparison: If QRList(i + 1) - QRList(i) + 1 = 1.
Since the condition is not met, the execution continues to the endN = QRList(i) assignment and exits the for loop entirely.
You can simplify this process a bit.
Since you need to generate sequences of contiguous numbers, you just need to determine the starting values - values in the original series that have a distance > 1 - and a variable to store the value of the previous iteration, used to perform the comparison.
Here, I'm using a List of named tuples (List(Of (StartValue As Integer, EndValue As Integer)) to store the values in the series.
You can use a List(Of String), or another type of collection, if that's preferable or named Tuples are not available
Dim QRList As Integer() = { 9, 12, 13, 14, 15, 16, 41, 42, 44, 48, 49, 50, 1, 2, 3, 4}
' Sort the array in case it's not already sorted
Array.Sort(QRList)
Dim maxDistance As Integer = 1
Dim seriesCount As Integer = -1
' Assign starting values that are less than ([Min Value] - [Max Distance])
Dim startValue As Integer = QRList(0) - (maxDistance + 1)
Dim previousValue As Integer = startValue
Dim series As New List(Of (StartValue As Integer, EndValue As Integer))()
For Each value As Integer In QRList
' The distance is less than or equal to the maximum: update the current item
If (value - maxDistance) <= previousValue Then
series(seriesCount) = (startValue, value)
Else
' The distance is > maxDistance: add a new starting item
startValue = value
series.Add((value, value))
seriesCount += 1
End If
previousValue = value
Next
To print the elements in the series:
For Each t In series
Console.WriteLine($"{t.StartValue} - {t.EndValue}")
Next
With maxDistance = 1, the series are:
1 - 4
9 - 9
12 - 16
41 - 42
44 - 44
48 - 50
With maxDistance = 4, the series are:
1 - 4
9 - 16
41 - 50

Related

How to creat a loop to assign a value given a condition?

So I have tried different types of visual basic code for this problem that i'm trying to solve but non of them is working :(
I have a jagged array for example {{1 10 20 50 53};{5 15 25 55}}
and I want to convert this information in a binary matrix given the condition that each array in the jagged array corresponds to a row, and the element in it corresponds to a column number. Am I being clear?
The matrix has the same rows as the number of arrays in the jagged array 2, but the columns in the matrix can go for example till 200.
1 0 ... 1 ... 1 ... 1 .. 1..................0
0 0 ... 1 ... 1 ... 1 .... 1 ...............0
My last attempt was this one:
For a = 0 To noRows - 1
For b = 0 To noCols - 1
Do While d < jarray(a).GetUpperBound(0)
If array(a)(d) = b Then
matrix(a, b) = 1
Exit Do
Else
matrix(a, b) = 0
d += 1
End If
Loop
Next
Next
What should I do?
When you make a new array its values are automatically initialised to the default value of the type of variable in the array - for an Integer, that is 0. So you don't need to do the setting to zero unless there is something else that isn't shown in the question.
You have the indexes of the locations which need to be set to 1, so you can do that directly:
Option Infer On
Option Strict On
Module Module1
Sub Main()
'Dim jArray = {New Integer() {1, 10, 20, 50, 53}, New Integer() {5, 15, 25, 55}}
Dim jArray = {New Integer() {1, 3, 4}, New Integer() {2, 5, 6, 7, 9}}
' find the number of columns needed for the resultant array
Dim nCols = jArray.Max(Function(r) r.Max())
' find the number of rows needed for the resultant array
Dim nRows = jArray.GetUpperBound(0) + 1
' the resultant array is automatically initialised to zeros
Dim matrix(nRows, nCols) As Integer
' fill in the ones as specified by the locations in jArray:
For i = 0 To nRows - 1
For j = 0 To jArray(i).GetUpperBound(0)
matrix(i, jArray(i)(j) - 1) = 1
Next
Next
' show the resultant array:
For i = 0 To nRows - 1
For j = 0 To nCols - 1
Console.Write(matrix(i, j) & If(j < nCols - 1, ", ", ""))
Next
Console.WriteLine()
Next
Console.ReadLine()
End Sub
End Module
Outputs:
1, 0, 1, 1, 0, 0, 0, 0, 0
0, 1, 0, 0, 1, 1, 1, 0, 1

Get sum of combinations of array values in scala

I know that there have been a few similar questions to this but there still doesn't seem to be a definitive answer to this so I will ask it..
I have an array of values and am trying to find the correct values that will sum to a limit value or the closest it can get (without exceeding it) from any given combination.
Using this answer https://stackoverflow.com/questions/23168934/calculating-minimal-subset-with-given-sum I have this:
def getLimitArr(arr: Array[Int], limit: Int): Unit = {
scala.util.Sorting.quickSort(arr) // Array(2, 3, 4, 5, 11, 34)
var sum = 0L
var i = arr.length-1
val arr2 = ArrayBuffer[Integer]()
while (i >= 0 && sum < limit) {
if(sum + arr(i)<=limit) {
sum += arr(i)
arr2 += arr(i)
}
i -= 1 // 6, 5, 4, 3, 2, 1
}
println(arr2.mkString(", ") + " = " + sum)
}
And calling it using this in the main method:
val arr = Array(3, 34, 4, 11, 5, 2)
getLimitArr(arr, 9)
Which returns:
println(arr2.mkString(", ") + " = " + sum) // 5, 4 = 9
This is good but only if the values (that make up the sum) can be made from the highest value that is lower than the limit; in this example 5 - which works with this array as we can see. But if the limit value was 12 (getLimitArr(arr, 12)) with this array then it would return 11 = 11 rather than using 5 + 4 + 3.
I have done this using subsets but when the array is more than 10 I get the memory heap error as it is formulating all of the combinations before being able to obtain the answer.
So how would we do this by being memory efficient, using the current format or taking advantage of Scala's functional programming capabilities?
Recursion is often useful when we want to terminate as soon as the first correct answer is found.
def getLimit(nums: Array[Int], limit: Int): Array[Int] = {
val subset = nums.filter(limit.>=)
if (subset.isEmpty) Array()
else (1 to subset.length).flatMap(subset.combinations)
.find(_.sum == limit)
.fold(getLimit(subset, limit-1))(identity)
}
getLimit(Array(3, 34, 4, 11, 5, 2), 5) // res0: Array[Int] = Array(5)
getLimit(Array(3, 34, 4, 11, 5, 2), 9) // res1: Array[Int] = Array(4, 5)
getLimit(Array(3, 34, 4, 11, 5, 2), 12) // res2: Array[Int] = Array(3, 4, 5)
getLimit(Array(3, 34, 4, 11, 5, 2), 24) // res3: Array[Int] = Array(3, 4, 11, 5)
Note that the last one sums to 23 because there is no combination that sums to 24.
update
A better shortcut added and the method is now tail recursive.
def getLimit(nums: Array[Int], limit: Int): Array[Int] = {
val subset = nums.filter(limit.>=)
if (subset.sum <= limit) subset
else {
val res = (1 to subset.length).view
.flatMap(subset.combinations)
.find(_.sum == limit)
if (res.isEmpty) getLimit(subset, limit-1)
else res.get
}
}

How to calculate the sum of specific Array Elements using vb.net

I have two arrays
x = [1 1 1 0 2 3 1 1]
y = [1 2 3 4 5 6 7 8]
How to calculate the sum of y's elements for all x(i) = 1 to get the result 1+2+3+7+8 ?
I have used for loop and if then method to calculate the sum value like this
if x(i) = 1 then sum = sum + y(i)
Have other methods to get the results (sum, average, count ...) ?
Thank you.
Since you only want to sum the numbers in y corresponding to 1s in x, but not 0s in x, you can multiply x * y which looks a bit cleaner than the If. Here are a few ways
Dim x = {1, 1, 1, 0, 0, 0, 1, 1}
Dim y = {1, 2, 3, 4, 5, 6, 7, 8}
' using a for loop
Dim sum1 As Integer = 0
For i = 0 To x.Length - 1
sum1 += If(x(i) = 1, 1, 0) * y(i)
Next
Console.WriteLine(sum1)
' using LINQ #1
Dim sum2 As Integer = x.Select(Function(i, index) If(i = 1, 1, 0) * y(index)).Sum()
Console.WriteLine(sum2)
' using LINQ #2
Dim sum3 As Integer = x.Zip(y, Function(x1, y1) If(x1 = 1, 1, 0) * y1).Sum()
Console.WriteLine(sum3)
' using LINQ #3
Dim sum4 As Integer = Enumerable.Range(0, x.Length).Sum(Function(i) If(x(i) = 1, 1, 0) * y(i))
Console.WriteLine(sum4)
Console.ReadLine()
The For is very clear so I don't know why you wouldn't use it, but you can use LINQ for this as well:
Sub Main
Dim x = {1, 1, 1, 0, 2, 3, 1, 1}
Dim y = {1, 2, 3, 4, 5, 6, 7, 8}
Dim sum = y.Where(Function(v, i) x(i) = 1).Sum()
Console.WriteLine("Sum is {0}", sum)
End Sub
Prints
Sum is 21

Get next string following array of char

I have an Array of chars, which is completely random except that every character occurs at most once.
I also have a string containing only characters that are present in the array. I want this string to be "counted upwards" (if that makes sense), like "111" becomes "112", or "aaa" becomes "aab".
Let´s say the Array of chars contains 1, 2, and 3. The example above works, but when the string is "333" (should become "1111"), my function returns an empty - or wrong - string.
If I call the function again, providing the wrong string, after a few times it returns the correct value ("1111").
Why does this happen?
This is my function:
Public Function getNextString(ByVal currentStr As String, ByVal pattern() As Char) As String
'currentStr is the string which I want to count upwards, pattern() is the array of chars
Dim nextStr As String = ""
Dim currentStrArray() As Char = currentStr.ToCharArray
Dim currenStrPosition As Integer = currentStrArray.Length - 1
Dim finished As Boolean = False
Do Until finished = True
Dim newPosition As Integer = getPositionInArray(currentStrArray(currentStrPosition)) 'this is a custom function, should be self-explaining
If newPosition = Nothing Then Return Nothing
newPosition += 1
Try
currentStrArray(currenStrPosition) = pattern(newPosition)
finished = True
Catch ex As IndexOutOfRangeException
currentStrArray(currentStrPosition) = pattern(0)
currentStrPosition -= 1
End Try
If currentStrPosition < 0 Then
nextStr = pattern(0)
finished = True
End If
Loop
For i As Integer = 0 To currentStrArray.Length - 1
nextStr = nextStr & currentStrArray(i)
Next
Return nextStr
End Function
Any ideas?
EDIT:
As an example, I have the array {"1","2","3"}. My string is first "111". I want to test these strings hashsums. After testing that string, I need the next string, "112". Then, "113", "121", "122", and so on. When the string reaches "333" and the string is not the one I'm looking for, it was obviously no string with only 3 characters (all 3-character-combinations possible with the array have been tried). So I need to start again using 4 characters. That´s why "333" is supposed to become "1111".
Hope this helps.
EDIT #2:
I found the error. I redimensioned my array incorrectly, so the last index was empty. This made my strings look weird. Thank you all for your working solutions, have a good day!
Well, I would convert to a number, add 1 and then convert back to a string.
Convert the string to a number:
Function ConvertStringToNumber(input As String, pattern As Char()) As Integer
Dim number As Integer = 0
Dim charDigits = pattern.ToList()
Dim numberBase = charDigits.Count
For i As var = 0 To input.Length - 1
Dim digit = charDigits.IndexOf(input(input.Length - 1 - i))
If digit <> -1 Then
number += digit * CInt(Math.Pow(numberBase, i))
End If
Next
Return number
End Function
Convert the number back to a string:
Function convertNumberToString(number As Integer, pattern As Char()) As String
Dim charDigits = pattern.ToList()
Dim numberBase = charDigits.Count
Dim buffer = New Stack(Of Char)()
'var j = buffer.Length;
While number > 0
buffer.Push(charDigits(number Mod numberBase))
number = number / numberBase
End While
Dim sb = New StringBuilder()
While buffer.Count > 0
sb.Append(buffer.Pop())
End While
Return sb.ToString()
End Function
Now that you have conversion functions, all you have to do is something like this:
Function getNextString(ByVal currentStr As String, ByVal pattern() As Char) As String
Dim number = ConvertStringToNumber(currentStr, pattern)
number += 1
Return convertNumberToString(number, pattern)
End Function
If I understand correctly you want to increment in a specific base (length of your array). What you need is a mapping function that maps your array chars to their respective numbers, then convert from your base to decimal, increment, convert back and remap.
Can you check if this link helps? Quickest way to convert a base 10 number to any base in .NET?
I just wrote the following in c#, it seems to work.
static void Main(string[] args)
{
char[] definition = new char[] { 'a', 'b', 'c', 'd', '9', 'x', 'y', 'z', '1', '2', '3'};
string helperstring = new String(definition);
int basenumber = definition.Length;
string mynumberasstring = "333";
Console.WriteLine(mynumberasstring);
int correspondingdecimal = 0;
for (int i = 0; i < mynumberasstring.Length; i++)
{
char x = mynumberasstring[mynumberasstring.Length - i - 1];
int index = helperstring.IndexOf(x);
int magnitude = 1;
for (int j = 0; j < i; j++)
magnitude *= basenumber;
Console.WriteLine(x + " -> " + index);
correspondingdecimal += magnitude * index;
}
Console.WriteLine(correspondingdecimal + " -> " + ++correspondingdecimal);
List<int> indicesofnewnumber = new List<int>();
int newmagnitude = basenumber;
while(correspondingdecimal > 0)
{
int div = correspondingdecimal / basenumber;
int remainder = correspondingdecimal % basenumber;
Console.WriteLine("{0} -> {1} ; {2}", correspondingdecimal, div, remainder);
indicesofnewnumber.Add(remainder);
correspondingdecimal = div;
}
string newnumberasstring = "";
for (int i = 0; i < indicesofnewnumber.Count; i++)
newnumberasstring += definition[indicesofnewnumber[indicesofnewnumber.Count - 1 - i]];
Console.WriteLine(newnumberasstring);
Console.ReadLine();
}
Here's the smallest code that I can think of to convert a number into an arbitrary base with arbitrary digits:
Dim convert As Func(Of Integer, Char(), String) = Nothing
convert = Function (n, cs) _
If(n \ cs.Length = 0, "", convert(n \ cs.Length, cs)) + cs(n Mod cs.Length)
So, to convert 4 to binary I could do convert(4, { "0"c, "1"c }) and I would get 100.
As a sanity check if I view the first 32 (starting from zero) binary numbers using String.Join(", ", Enumerable.Range(0, 32).Select(Function (n) convert(n, { "0"c, "1"c }))) and I get this:
0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111, 10000, 10001, 10010, 10011, 10100, 10101, 10110, 10111, 11000, 11001, 11010, 11011, 11100, 11101, 11110, 11111
Or the first 102 decimal numbers starting from zero with String.Join(", ", Enumerable.Range(0, 102).Select(Function (n) convert(n, "0123456789".ToCharArray()))) I get this:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101
So I can now use the same kind of code to get the first 50 numbers from the digits { "1"c, "2"c, "3"c } using String.Join(", ", Enumerable.Range(0, 50).Select(Function (n) convert(n, "123".ToCharArray()))) and I get this:
1, 2, 3, 21, 22, 23, 31, 32, 33, 211, 212, 213, 221, 222, 223, 231, 232, 233, 311, 312, 313, 321, 322, 323, 331, 332, 333, 2111, 2112, 2113, 2121, 2122, 2123, 2131, 2132, 2133, 2211, 2212, 2213, 2221, 2222, 2223, 2231, 2232, 2233, 2311, 2312, 2313, 2321, 2322
This is the correct "counting" using the digits { "1"c, "2"c, "3"c }. So, it would be correct to say that "333" + 1 = "2111" in this numbering system. This is the same as saying 999 + 1 = 1000 in our decimal system - it clearly doesn't go from 999 + 1 = 0000 which is the implication that "333" + 1 = "1111" would mean in this "123" system. Strictly speaking I should have written "333" + "2" = "2111" (since "2" represents 1).
If you want all combinations then you would need to pad the strings to the left with the "zero" character - in this case 1. Then this code:
String.Join(", ", _
Enumerable _
.Range(0, 500) _
.Select(Function (n) convert(n, "123".ToCharArray()).PadLeft(4, "1"c)) _
.Where(Function (x) x.Length = 4))
...gives the following 81 numbers:
1111, 1112, 1113, 1121, 1122, 1123, 1131, 1132, 1133, 1211, 1212, 1213, 1221, 1222, 1223, 1231, 1232, 1233, 1311, 1312, 1313, 1321, 1322, 1323, 1331, 1332, 1333, 2111, 2112, 2113, 2121, 2122, 2123, 2131, 2132, 2133, 2211, 2212, 2213, 2221, 2222, 2223, 2231, 2232, 2233, 2311, 2312, 2313, 2321, 2322, 2323, 2331, 2332, 2333, 3111, 3112, 3113, 3121, 3122, 3123, 3131, 3132, 3133, 3211, 3212, 3213, 3221, 3222, 3223, 3231, 3232, 3233, 3311, 3312, 3313, 3321, 3322, 3323, 3331, 3332, 3333
...and 81 is the total number of combinations as 3 x 3 x 3 x 3 = 81.
Here are the pair of functions to convert both ways:
Function Convert(number As Integer, digits As Char()) As String
Dim r = digits(number Mod digits.Length).ToString()
If number \ digits.Length <> 0 Then
r = Convert(number \ digits.Length, digits) + r
End If
Return r
End Function
Function Convert(number As String, digits As Char()) As Integer
Dim r = Array.IndexOf(digits, number(0))
If number.Substring(1).Length > 0 Then
r = r * digits.Length + Convert(number.Substring(1), digits)
End If
Return r
End Function

How to 'make' an multi-digit integer value from single digits held in an array (VB.NET)

I have a large array of single digit integers (0 - 9) and I would like to turn a subset of the values into a single integer value. For example:
Array(0) = 4
Array(1) = 2
Array(2) = 1
Array(3) = 6
Array(4) = 7
Array(5) = 4
Array(6) = 8
Array(7) = 2
Array(8) = 9
Array(9) = 0
I would like the then make a number using (for example) array indexes 4,5 and 6;
NewInt = 748
The only way i can see of doing this is making a string and then an integer (as in CInt(Cstr(Array(5) & Array(6)...)) but this seems needlessly slow. The whole point of what I'm doing is about speeding things up.
Any suggestions?
Take a look at this.
Dim nums() As Integer = {4, 2, 1, 6, 7, 4, 8, 2, 9, 0}
Dim ans As Long = 0L
Dim useNums() As Integer = {4, 5, 6}
For Each idx As Integer In useNums
ans = (ans * 10L) + nums(idx)
Next

Resources