I'm hoping this has a simple solution. I need to take values in from a worksheet, perform calculations and insert them into a hidden worksheet (to be uploaded later into a database). I don't normally program in VBA, so I'm not sure what I'm doing wrong. My problem is this: when I copy data from the worksheet into the array, they are rounded like an integer when I've specified long. The following code snippet will give you an idea of the problem. Am I initializing the array wrong?
Dim ThisWS As Worksheet
Set ThisWS = Excel.ActiveWorkbook.Worksheets("BchSheet")
Dim BTW() As Long 'Beaker Tare Weight
ReDim Preserve BTW(Samples)
BTW(1) = ThisWS.Cells(18, 6).Value 'Value in cell is 98.7036
MsgBox (ThisWS.Cells(18, 6).Value) 'Returns 98.7036
MsgBox (BTW(1)) 'Returns 99
The Long data type, like Integer, only holds whole numbers.
Use the Double data type to store decimals, or the Currency data type if you are working with calculations where fixed-point is necessary or you don't want to deal with floating-point numbers.
Related
I have a named range in Excel called StartDates for instance. The named range only contains dates. Example code below:
Sub test2()
Dim dat_readdates() As Date
dat_readdates = Range("StartDates").Value
End Sub
This always results in the proverbial Run-time error '13': Type mismatch.
A simple change and it works fine.
Sub test2()
Dim var_readdates() As Variant
var_readdates = Range("StartDates").Value
End Sub
Is there a known rule that data must be read in as variant data type? Clearly for speed I would like to use the data type most appropriate to the data being read.
Do I need to read it as variant and then use cDate to convert to a another variable of date type? Can I do a mass conversion such as:
dat_readdates = cDate(var_readdates)
No, unfortunately. Values are fetched from Excel as Variants. When fetching a single cell, you can directly convert it to the appropriate type, i.e.
Dim x as Long
x = myCell.Value
In the code above, first myCell.Value is fetched as a Variant, then converted by VBA to long (to execute the assignment) if the conversion is possible, otherwise run-time error.
With multiple-cell ranges, it's more tricky. The range.value returns a Variant Array. Unfortunately, VBA does not provide automatic conversion of a Variant array into a typed array. Therefore the following wont work:
Dim x() as Long
x = myRange.Value
VBA does not allow conversion of arrays like with simple variables; even if the programmer is sure that all the variants in the array are of the desired type (i.e. Long in the example above).
To emphasize, forget about excel; the following wont work in VBA:
Dim varArray() ' a variant array
varArray = Array(1, 2, 3, 4, 5)
Dim longArray() as Long
longArray = varArray ' <-- type mismatch. No conversion from variant array to typed array.
I have to search for multiple text strings in an Excel column (one string at a time). This column has got around 200K values. So if I search through this column 50 times , it takes long time using the "Find" command in VBA.
I am looking for an alternative method to make this search more efficient. Probably, reading this column once in a variable and then search this variable for the text strings I want to. With this method, I hope the searches will be faster than searching in an Excel sheet column itself.
Please suggest the best option to accomplish it.
Any help is highly appreciated.
Here is something small to start with:
Open Excel file and write some values in Range E10:E34. Make sure that one of the values is Plot.
Then run the code:
Option Explicit
Sub TestMe()
Dim varArray As Variant
Dim strToSearch As String: strToSearch = "Plot"
Dim varVal As Variant
Dim lngCounter As Long : lngCounter
varArray = Range("E10:E34")
For Each varVal In varArray
If varVal = strToSearch Then
Debug.Print varVal
Debug.Print lngCounter
Exit For
End If
Next varVal
lngCounter = lngCounter + 1
End Sub
It will give you the row of the E10:E34 range, on which the value is to be found.
The code is not finished. You have to think of the following:
What happens, if we have the value more than once;
What happens, if we want array with more than one columns;
What happens, if the range should be flexible;
What happens, if we just know the start of the value in the cell, but we still want it - e.g. the value is Plot and in the cell we have Plotnik and we want to get it somehow.
Anything else.
I'll just say right off the bat that I'm a newb in coding overall. I've learned a lot along the way creating various things at work for myself and I'm learning all the time.
The problem I have right now is with a loop using application.vlookup(...). I've done a lot of searching around but I gave up because I couldn't get anything to work. I've removed the .WorksheetFunction part from the original code because apparently it doesn't change anything but at least it doesn't give VBA errors when a value is not found and acts similar to the normal function (gives #N/A).
So in the below code I have my data in column A which I need to convert using vlookup on a Query in another worksheet (and put that in the same sheet as the data, in column C).
Sub vlookup()
Dim wsdata As Worksheet
Dim lr As Long
Dim rng As Range
Set wsdata = ThisWorkbook.Worksheets("Data")
wsdata.Columns("C").Delete
wsdata.Range("D1") = "Correct number"
lr = wsdata.Cells(Rows.Count, "A").End(xlUp).Row
Set rng = wsdata.Range("A1:D" & lr)
Dim lrQuery As Long, iNr As Long
Dim wsQuery As Worksheet
Dim LookUpRange As Range
Set wsQuery = ThisWorkbook.Sheets("IDMquery")
lrQuery = wsQuery.Cells(Rows.Count, "A").End(xlUp).Row
Set LookUpRange = wsQuery.Range("D:F")
For iNr = 2 To lr
wsdata.Cells(iNr, 4).Value = Application.VLookup(wsdata.Cells(iNr, 1).Value, LookUpRange, 3, 0)
Next iNr
End Sub
I have to add that there are some issues with the formatting (or whatever they are), because the original data is external. Eg. for "17935" VarType would give 8 (string) instead of a numeric type.
In the spreadsheet it's easy to fix it with the double unary and the following formula works correctly:
=VLOOKUP(--A2,IDMquery!D:F,3,false)
Of course I could use a helper column and just use that, which might be an option if I can't do it any other way, but I would prefer strictly within VBA.
Do you guys know if this is fixable?
Cheers!
EDIT: Btw, I also changed the formatting of column A to "0" in VBA but it doesn't actually want to change these values to numeric, not sure why.
Hi I'm using VBA in Excel and need to pass in the values from two ranges into a custom function from within a cell's formula. The function looks like this:
Public Function multByElement(range1 As String, range2 As String) As Variant
Dim arr1() As Variant, arr2() As Variant
arr1 = Range(range1).value
arr2 = Range(range2).value
If UBound(arr1) = UBound(arr2) Then
Dim arrayA() As Variant
ReDim arrayA(LBound(arr1) To UBound(arr1))
For i = LBound(arr1) To UBound(arr1)
arrayA(i) = arr1(i) * arr2(i)
Next i
multByElement = arrayA
End If
End Function
As you can see, I'm trying to pass the string representation of the ranges. In the debugger I can see that they are properly passed in and the first visible problem occurs when it tries to read arr1(i) and shows as "subscript out of range". I have also tried passing in the range itself (ie range1 as Range...) but with no success.
My best suspicion was that it has to do with the Active Sheet since it was called from a different sheet from the one with the formula (the sheet name is part of the string) but that was dispelled since I tried it both from within the same sheet and by specifying the sheet in the code.
BTW, the formula in the cell looks like this:
=AVERAGE(multByElement("A1:A3","B1:B3"))
or
=AVERAGE(multByElement("My Sheet1!A1:A3","My Sheet1!B1:B3"))
for when I call it from a different sheet.
First, see the comment Remou left, since that's really what you should be doing here. You shouldn't need VBA at all to get an element-wise multiplication of two arrays.
Second, if you want to work with Ranges, you can do that by declaring your function arguments to be of type Range. So you could have
Public Function multByElement(range1 As Range, range2 As Range)
and not need to resolve strings to range references yourself. Using strings prevents Excel from updating references as things get moved around in your worksheet.
Finally, the reason why your function fails the way it does is because the array you get from taking the 'Value' of a multi-cell Range is two-dimensional, and you'd need to acces its elements with two indices. Since it looks like you're intending to (element-wise) multiply two vectors, you would do either
arrayA(i) = arr1(i,1) * arr2(i,1)
or
arrayA(i) = arr1(1,i) * arr2(1,i)
depending on what orientation you expected from your input. (Note that if you do this with VBA, orientation of what is conceptually a 1-D array matters, but if you follow Remou's advice above, Excel will do the right thing regardless of whether you pass in rows or columns, or range references or array literals.)
As an epilogue, it also looks like you're not using 'Option Explicit'. Google around for some rants on why you probably always want to do this.
I need to simply go through all the cells in a Excel Spreadsheet and check the values in the cells. The cells may contain text, numbers or be blank. I am not very familiar / comfortable working with the concept of 'Range'. Therefore, any sample codes would be greatly appreciated. (I did try to google it, but the code snippets I found didn't quite do what I needed)
Thank you.
If you only need to look at the cells that are in use you can use:
sub IterateCells()
For Each Cell in ActiveSheet.UsedRange.Cells
'do some stuff
Next
End Sub
that will hit everything in the range from A1 to the last cell with data (the bottom right-most cell)
Sub CheckValues1()
Dim rwIndex As Integer
Dim colIndex As Integer
For rwIndex = 1 To 10
For colIndex = 1 To 5
If Cells(rwIndex, colIndex).Value <> 0 Then _
Cells(rwIndex, colIndex).Value = 0
Next colIndex
Next rwIndex
End Sub
Found this snippet on http://www.java2s.com/Code/VBA-Excel-Access-Word/Excel/Checksvaluesinarange10rowsby5columns.htm It seems to be quite useful as a function to illustrate the means to check values in cells in an ordered fashion.
Just imagine it as being a 2d Array of sorts and apply the same logic to loop through cells.
If you're just looking at values of cells you can store the values in an array of variant type. It seems that getting the value of an element in an array can be much faster than interacting with Excel, so you can see some difference in performance using an array of all cell values compared to repeatedly getting single cells.
Dim ValArray as Variant
ValArray = Range("A1:IV" & Rows.Count).Value
Then you can get a cell value just by checking ValArray( row , column )
You can use a For Each to iterate through all the cells in a defined range.
Public Sub IterateThroughRange()
Dim wb As Workbook
Dim ws As Worksheet
Dim rng As Range
Dim cell As Range
Set wb = Application.Workbooks(1)
Set ws = wb.Sheets(1)
Set rng = ws.Range("A1", "C3")
For Each cell In rng.Cells
cell.Value = cell.Address
Next cell
End Sub
For a VB or C# app, one way to do this is by using Office Interop. This depends on which version of Excel you're working with.
For Excel 2003, this MSDN article is a good place to start.
Understanding the Excel Object Model from a Visual Studio 2005 Developer's Perspective
You'll basically need to do the following:
Start the Excel application.
Open the Excel workbook.
Retrieve the worksheet from the workbook by name or index.
Iterate through all the Cells in the worksheet which were retrieved as a range.
Sample (untested) code excerpt below for the last step.
Excel.Range allCellsRng;
string lowerRightCell = "IV65536";
allCellsRng = ws.get_Range("A1", lowerRightCell).Cells;
foreach (Range cell in allCellsRng)
{
if (null == cell.Value2 || isBlank(cell.Value2))
{
// Do something.
}
else if (isText(cell.Value2))
{
// Do something.
}
else if (isNumeric(cell.Value2))
{
// Do something.
}
}
For Excel 2007, try this MSDN reference.
There are several methods to accomplish this, each of which has advantages and disadvantages; First and foremost, you're going to need to have an instance of a Worksheet object, Application.ActiveSheet works if you just want the one the user is looking at.
The Worksheet object has three properties that can be used to access cell data (Cells, Rows, Columns) and a method that can be used to obtain a block of cell data, (get_Range).
Ranges can be resized and such, but you may need to use the properties mentioned above to find out where the boundaries of your data are. The advantage to a Range becomes apparent when you are working with large amounts of data because VSTO add-ins are hosted outside the boundaries of the Excel application itself, so all calls to Excel have to be passed through a layer with overhead; obtaining a Range allows you to get/set all of the data you want in one call which can have huge performance benefits, but it requires you to use explicit details rather than iterating through each entry.
This MSDN forum post shows a VB.Net developer asking a question about getting the results of a Range as an array
You basically can loop over a Range
Get a sheet
myWs = (Worksheet)MyWb.Worksheets[1];
Get the Range you're interested in If you really want to check every cell use Excel's limits
The Excel 2007 "Big Grid" increases
the maximum number of rows per
worksheet from 65,536 to over 1
million, and the number of columns
from 256 (IV) to 16,384 (XFD).
from here http://msdn.microsoft.com/en-us/library/aa730921.aspx#Office2007excelPerf_BigGridIncreasedLimitsExcel
and then loop over the range
Range myBigRange = myWs.get_Range("A1", "A256");
string myValue;
foreach(Range myCell in myBigRange )
{
myValue = myCell.Value2.ToString();
}
In Excel VBA, this function will give you the content of any cell in any worksheet.
Function getCellContent(Byref ws As Worksheet, ByVal rowindex As Integer, ByVal colindex As Integer) as String
getCellContent = CStr(ws.Cells(rowindex, colindex))
End Function
So if you want to check the value of cells, just put the function in a loop, give it the reference to the worksheet you want and the row index and column index of the cell. Row index and column index both start from 1, meaning that cell A1 will be ws.Cells(1,1) and so on.
My VBA skills are a little rusty, but this is the general idea of what I'd do.
The easiest way to do this would be to iterate through a loop for every column:
public sub CellProcessing()
on error goto errHandler
dim MAX_ROW as Integer 'how many rows in the spreadsheet
dim i as Integer
dim cols as String
for i = 1 to MAX_ROW
'perform checks on the cell here
'access the cell with Range("A" & i) to get cell A1 where i = 1
next i
exitHandler:
exit sub
errHandler:
msgbox "Error " & err.Number & ": " & err.Description
resume exitHandler
end sub
it seems that the color syntax highlighting doesn't like vba, but hopefully this will help somewhat (at least give you a starting point to work from).
Brisketeer