Find smallest number in calculated field - vba

In a MSAccess report, I have fields like: CurrentHours, Insp1DueTime, Insp2DueTime...Inspn3DueTime, etc. I want a calculated field which outputs the smallest value of (Insp1DueTime-CurrentHours), (Insp2DueTime-Currenthours), (Insp3DueTime-CurrentHours), etc.
Is there a VBA command which will do this, similar to 'smallestvalue'((currenthours-insp1duetime), (Currenthours-insp2duetime),...(currenthour-InspnDueTime))

Nope, but you can make one really easily, using a ParamArray:
Public Function SmallestValue(ParamArray Values())
Dim Value
For Each Value In Values
If IsEmpty(SmallestValue) Or SmallestValue > Value Then
SmallestValue = Value
End If
Next
End Function
A paramarray takes any input, and puts it into an array. You iterate over that array, and test if things are larger or smaller.
This works with any input, e.g.: SmallestValue(2,1,3,4) returns 1, SmallestValue(Now(), Date(), #1/1/2001#) returns #1/1/2001#, SmallestValue("banana", "apple", "pear") returns "apple"

Related

Refined list sorting by substring integer after alphabetical sorting

I have some information in a list (called listLines). Each line below is in a List(Of String).
1|This is just a header
3|This is just a footer
2|3456789|0000000|12312312313|BLUE|1|35.00
2|7891230|0000000|45645645655|BLUE|1|22.00
2|7891230|0000000|45645645658|RED|2|13.00
2|3456789|0000000|12312312316|RED|2|45.00
2|3456789|0000000|12312312317|YELLOW|5|-9.00
2|3456789|0000000|12312312315|ORANGE|3|15.00
2|7891230|0000000|45645645659|YELLOW|5|32.00
2|3456789|0000000|12312312314|GREEN|4|-20.00
2|7891230|0000000|45645645656|GREEN|4|39.00
2|7891230|0000000|45645645657|ORANGE|3|-18.50
I'm doing a listLines.sort() on the list to sort it alphabetically. Below is what I get after the .sort().
1|This is just a header
2|3456789|0000000|12312312313|BLUE|1|35.00
2|3456789|0000000|12312312314|GREEN|4|-20.00
2|3456789|0000000|12312312315|ORANGE|3|15.00
2|3456789|0000000|12312312316|RED|2|45.00
2|3456789|0000000|12312312317|YELLOW|5|-9.00
2|7891230|0000000|45645645655|BLUE|1|22.00
2|7891230|0000000|45645645656|GREEN|4|39.00
2|7891230|0000000|45645645657|ORANGE|3|-18.50
2|7891230|0000000|45645645658|RED|2|13.00
2|7891230|0000000|45645645659|YELLOW|5|32.00
3|This is just a footer
With that said, I need to output this information to a file. I'm able to do this ok. I still have a problem though. There is a sequence number in the above data at position 5 just after the listed colors (RED, BLUE, ETC..) that you can see. It's just before the last value which is a decimal type.
I need to further sort this list, keeping it in alphabetical order since position 2 is an account number and I want to keep the account numbers grouped together. I just want them to be resorted in sequential order based on the sequence number.
I was looking at another thread trying to figure out how I can do this. I found a piece of code like listLines.OrderBy(Function(q) q.Substring(35)).ToArray. I think this would probably help me if this was a fixed length file, it isn't however. I was thinking I can do some kind of .split() to get the 5th piece of information and sort it but then it's going to unalphabetize and mix the lines back up because I don't know how to specify to still keep it alphabetical.
Right now I'm outputting my alphabetical list like below so I can format it with commas and double quotes.
For Each listLine As String In listLines
strPosition = Split(listLine, "|")
Dim i As Integer = 1
Dim iBound As Integer = UBound(strPosition)
Do While (i <= iBound)
strOutputText = strOutputText & Chr(34) & strPosition(i) & Chr(34) & ","
i += 1
Loop
My main question is how do I re-sort after .sort() to then get each account (position1) in sequential order (position 5)? OR EVEN BETTER, how can I do both at the same time?
The List(Of T) class has an overload of the Sort method that takes a Comparison(Of T) delegate. I would suggest that you use that. It allows you to write a method or lambda expression that will take two items and compare them any way you want. In this case, you could do that like this:
Dim items = New List(Of String) From {"1|This Is just a header",
"3|This Is just a footer",
"2|3456789|0000000|12312312313|BLUE|1|35.00",
"2|7891230|0000000|45645645655|BLUE|1|22.00",
"2|7891230|0000000|45645645658|RED|2|13.00",
"2|3456789|0000000|12312312316|RED|2|45.00",
"2|3456789|0000000|12312312317|YELLOW|5|-9.00",
"2|3456789|0000000|12312312315|ORANGE|3|15.00",
"2|7891230|0000000|45645645659|YELLOW|5|32.00",
"2|3456789|0000000|12312312314|GREEN|4|-20.00",
"2|7891230|0000000|45645645656|GREEN|4|39.00",
"2|7891230|0000000|45645645657|ORANGE|3|-18.50"}
items.Sort(Function(x, y)
Dim xParts = x.Split("|"c)
Dim yParts = y.Split("|"c)
'Compare by the first column first.
Dim result = xParts(0).CompareTo(yParts(0))
If result = 0 Then
'Compare by the second column next.
result = xParts(1).CompareTo(yParts(1))
End If
If result = 0 Then
'Compare by the sixth column last.
result = xParts(5).CompareTo(yParts(5))
End If
Return result
End Function)
For Each item In items
Console.WriteLine(item)
Next
If you prefer a named method then do this:
Private Function CompareItems(x As String, y As String) As Integer
Dim xParts = x.Split("|"c)
Dim yParts = y.Split("|"c)
'Compare by the first column first.
Dim result = xParts(0).CompareTo(yParts(0))
If result = 0 Then
'Compare by the second column next.
result = xParts(1).CompareTo(yParts(1))
End If
If result = 0 Then
'Compare by the sixth column last.
result = xParts(5).CompareTo(yParts(5))
End If
Return result
End Function
and this:
items.Sort(AddressOf CompareItems)
Just note that this is rather inefficient because it splits both items on each comparison. That's not a big deal for a small list but, if there were a lot of items, it would be better to split each item once and then sort based on those results.

Excel vba: check if VLookup returns empty value, given that Vlookup substitutes empty with zero

So I have a table for different regions and corresponding values for different years 2014-2017. Not all regions have value for the year 2017. However, if I make a WorksheetFunction.VLookup(...) it will return 0 if the cell in question is empty.
I need to put my special value (-1) instead of 0 if the cell for a given region is empty, however, I can't differentiate between real zeroes and zeroes Vlookup returns instead of empty values. How would I do it?
EDIT: I can't use IsEmpty on individual cells, because I don't know the result's cell's address - I loop though them in a cycle without even knowing a first argument's address. All I have is the result - either a real or a fake zero.
You can check if VLOOKUP returns an empty cell.
As a worksheet formula you can use: =IF(VLOOKUP(K9,$F$2:$G$6,2,FALSE)="",-1,VLOOKUP(K9,$F$2:$G$6,2,FALSE))
As a VBA example you could use:
Public Function MyVlookup(lookup_value, table_array As Range, col_index_num As Long, Optional range_lookup As Boolean = False) As Variant
Dim ReturnValue As Variant
ReturnValue = WorksheetFunction.VLookup(lookup_value, table_array, col_index_num, range_lookup)
If IsEmpty(ReturnValue) Then
MyVlookup = -1
Else
MyVlookup = ReturnValue
End If
End Function
Worked this answer
=if(len(vlookup(a2,f$2:g$20,2))=0,"",vlookup(a2,f$2:g$20,2))
The trick is to use any checking functions not on result = VLookup(...) but rather on VLookup itself, before it had time to change emptiness to zero.
I am not sure but I think using IF, ISEmpty will solve your problem.
Use WorksheetFunction.Match to get cell address
currentCell= the cell in which you'll be putting value
concernedCell=The cell which can be empty
If(Isempty(concernedCell)) Then
currentCell.value=-1
Else
currentCell.value=concernedCell.value

vb.net - Sort datagridview column numerically

I know this has been asked quite a few times, but i'm having issues with the solutions found on most other pages.
I have a single datagridview column that i want to be sorted by number (1,2,10 instead of 1,10,2)
Best i can see online, i need to convert the column or cell to an integer value type - but i'm not sure how to do so.
I've tried grid.columns(4).valuetype = typeof(System.int32), and tried the same for cells individually.
Trying above always results in a "int32 is a type in 'system' and cannot be used as an expression" error - which i'm not sure about.
The data itself is obtained froma text file, and converted from string to integer when being added into the cell datagrid_alltracks.Rows(shutupaboutlambda).Cells(4).Value = CInt(numstring))
You can just set the DataGridView SortCompare Event to compare two integers (or two singles, or two doubles). Code wise (calling your datagridview "grid")
Private Sub grid_SortCompare(sender as Object, e as DataGridViewSortCompareEventArgs) Handles grid.SortCompare
e.SortResult = CInt(e.cellvalue1).CompareTo(CInt(e.cellValue2))
e.Handled = True
End Sub
if you are doing single or double variables, use CSng or CDbl instead of CInt
e.SortResult = CSng(e.cellvalue1).Compareto(CSng(e.CellValue2)
You can do more fancy sorting if you want, You basically need to know that e.SortResult is Positive, Negative or Zero, and your cells are sorted according to that result (Positive keep order, negative reverse order, Zero - matched - do nothing (yet)). The current row index(s) and column are available in the e arguments so you can also compare adjacent column data if the current cells are matched)
If the grid is bound to a data source you could try
datatable.Columns.Add("ColumnName", GetType(Integer))
Else you may need to use the SortCompare event on the gridview.
See here
I know I'm coming to the party late, but I found that I once I had added the data, I needed to convert the desired columns to have a data type.
I'm adding data like the following:
DataGridView1.Rows.Add(New String() {CInt(recordnum), True, "play", wszName.ToString, qiOffset.ToString, value.ToString, qiLength.ToString})
Then, after all the data has been added, I then do a simple loop and convert the column, where I can then sort it. It's set up so you can do multiple columns if need be.
Dim colnum As Integer
colnum = 0 ' set this as your column to change the data type to
For i As Integer = 0 To DataGridView1.Rows.Count - 1
Dim d As Double = Double.Parse(DataGridView1.Rows(i).Cells(colnum).Value.ToString())
DataGridView1.Rows(i).Cells(colnum).Value = CInt(d)
DataGridView1.Rows(i).Cells(colnum).ValueType = GetType(Double)
Next
Sorting can work for whatever column you adjusted. In this case, it's column 4.
DataGridView1.Sort(DataGridView1.Columns(4), System.ComponentModel.ListSortDirection.Ascending)

How do I get a function to refer to the cell it is residing in?

I am writing a function and I need the function to refer to a cell offset from where it resides in. I've got rows of data with each column containing a specific variable. The function resides in the last column, and is supposed to check if each variable defined in it matches the variables in the row preceding it. Each match is supposed to score a +1 to the final value of the function. Here's a short version of my function.
Public Function cellscore(testvar1)
totalscore = 0
If testvar1= activecell.Offset(-1, -10) Then
cellscore = totalscore + 1
End If
End Function
What do I replace activecell with, so that the function runs correctly for each row of data that I have?
I'm new at VBA so please bear with me if this is a simple question. Thanks in advance.
The easiest way would be to pass a numeric value to the function which represents the row number. This means that your code would become something like:
Public Function cellscore(testvar1,RowNum as Long)
totalscore = 0
If testvar1= Range("SOMECELL").Offset(RowNum-2, -10) Then
cellscore = totalscore + 1
End If
End Function
You will need to change SOMECELL to represent the cell in the first row. For example if your formula is placed in the T column, then you would replace SOMECELL with T1.
The clever part now comes with how you call the function within the worksheet. You do this by using the formula:
=cellscore(SOMEVALUE,ROW())
Obviously replace SOMEVALUE with the value you are testing for. Now regardless of which row this formula is placed it will reference the current one when calling the function. Hope this helps!

VB.Net Replace not working?

Not sure if I'm doing something wrong or not, basically my code starts at "111111111" and counts up by adding "1" to the original number every time the thread is able to. I want the method to skip 0's in the sequence though, instead of going to "111111120" after "111111119" I would like it to go straight to "111111121".
Private Sub IncreaseOne()
If count < 999999999 Then
count += 1
Else
done = True
End If
If CStr(count).Contains("0") Then
MsgBox("theres a 0 in that...darn.")
CStr(count).Replace("0", "1")
End If
End Sub
*note, my message box displays when it is suppose to but, 0s are not changed to 1s
Replace returns a string with the effects of the Replace, It doesn't work in place....
(Remember, in NET the strings are immutable objects)
Dim replaced = CStr(count).Replace("0", "1")
However you need to convert the string obtained to an integer and reassign to count.
count = Convert.ToInt32(replaced)
Replace is a function that returns a sting.
In other words, you need a variable to hold the result, like this:
Dim newValue = CStr(count).Replace("0", "1")