I am relatively new to VBA and I am working on the following exercise from WiseOwl in order to improve my coding skills. The goal of the task is to populate random numbers within cells B3:G3 randomly, where no random number can be the same. In trying to accomplish that, I have done the following macro
Sub LuckyDip()
Dim i As Variant
Dim Num As Integer
For Each i In Worksheets(1).Range("B3:G3")
Num = WorksheetFunction.RandBetween(1, 59)
Cells(3, i).Offset(0, 1).Value = Num
Next i
End Sub
The problem with the code is the following is that instead of populating random numbers within each cell consecutively within the range of B3:G3, it instead populates the random numbers by the random number generated within those cells in the range. For example, if in the cell B3, the random number is 30, then the program will populate a random number in row 3 and cell 30, rather than actually replacing the random number 30 which was originally in cell B3. The problem I know lies in my For Each loop, however, I am not sure how to change it accordingly where the random number does not refer to a specific cell at all, and rather just acts as a value that I can replace. Thanks for any assistance in advance.
You should learn right off the bat that reading/writing cells, one-by-one is a terrible idea. It's really, really slow. And while it won't be noticeable in the tiny range in this question, it becomes painful with larger ranges. So just get into the habit of reading and writing ARRAYS to the worksheet instead of individual values.
For example:
Sub LuckyDip()
Dim j&, v
With Worksheets(1).Range("B3:G3")
v = .Value2
With WorksheetFunction
For j = 1 To UBound(v, 2)
v(1, j) = .RandBetween(1, 59)
Next
End With
.Value2 = v '<--v is an array of values, written to the sheet in one go.
End With
End Sub
With
For Each i In Worksheets(1).Range("B3:G3")
i is a range object and not a number and as such:
Cells(3, i)
will refer to the value in i to be the column number.
You want a standard for loop:
For i = 2 to 7
Where 2 is the column number of B and 7 the column number of G
Sub LuckyDip()
Dim i As Long
Dim Num As Long
For i = 2 To 7
Num = WorksheetFunction.RandBetween(1, 59)
ActiveSheet.Cells(3, i).Offset(0, 1).Value = Num
Next i
End Sub
One note: As you can see, I added ActiveSheet one should get in the habit of always denoting the parent sheet to all range/cell objects. Ideally using the codename.
Related
Say I have an Excel sheet with 10,000 rows and two columns. All 20,000 cells are filled with numbers and there is no missing data. I want to have a third column, the values of which are the sum of Column A and Column B. For example, C70 = A70 + B70, and C82 = A82 + 82, and the like.
The only problem is I want to do it for only a portion of the rows, say from row 125 to row 8954. I don't care about the rest of the values. And I don't want to do it by dragging the grid using the mouse. Is that possible?
If you have access to SEQUENCE() (Currently only available to Office 365 Insiders) then yes it is possible:
=INDEX(A:A,SEQUENCE(1000,,ROW(),1))+INDEX(B:B,SEQUENCE(1000,,ROW(),1))
Where 1000 is the number of rows desired. Place the formula in the first cell desired and it will automatically fill the rest.
I believe you need some logic about what is going on, related to the start and end row.
You can use an if-statement or sumifs() for this... will do an if-statement so i can specify not meeting the requirements as null.
With Start row as 2 and end row as 4 (see image), you can use this formula, and drag it down to the bottom of all columns:
=IF(AND(ROW(A2)<=F$2,ROW(A2)>=E$2),SUM(A2:B2),"")
Notice in the image that C5 has no value; this is due to the conditions of the if-statement being false.
Another idea, a simple macro that will do what you want by asking the user what the starting and end row is.
Sub test()
Dim startrow As Integer 'variable to hold first row
Dim endrow As Integer 'variable to hold last row
startrow = InputBox("Enter the start row")
endrow = InputBox("Enter the end row")
'loops through you desired range calculating what you want
Dim i As Integer
For i = startrow To endrow
Cells(i, 4).Value = Cells(i, 1).Value + Cells(i, 2).Value
Next
End Sub
Just change the values to suit your needs in what cells you want to add up and where you want the sum to go.
I've set up a two-dimensional array, and I want to count the number of filled cells in column H for each trial in each block. I then want to print the number of filled cells next to the last row of data for each trial, into column T.
The problem I'm getting is that when I try to run the macro, Excel stops responding, and after restarting, I get the error message in the title.
Here is the code:
Sub dotcountanalysis2()
' create multidimensional array
Dim Participant() As Variant
Participant = Worksheets("full test").Range("A7", Range("S:S")).Value
Dim Block As Variant
Block = Columns(2)
Dim Trial As Variant
Trial = Columns(3)
' define column H as boolean variable
Dim Pressed As Boolean
Pressed = True
' begin analysis after practice trials
For Each Block In Participant
For Each Trial In Participant
pressedcount = Range("H:H").Cells.SpecialCells(xlCellTypeConstants).Count
If Cells(, 8) = Pressed Then
Range("T:T").Value = pressedcount
End If
Next Trial
Next Block
End Sub
The error is on line:
pressedcount = Range("H:H").Cells.SpecialCells(xlCellTypeConstants).Count
I'm also not sure that my syntax is correct to make it count for each trial, as I have tried stepping into the code, and it gives the total number of filled cells in column H (562), and prints it in every cell in column T. I think it's also going way past the 7011 rows of data I have, to the maximum possible number of rows.
Here is a sample of my data
The most relevant problem is probably the fact that you create a huge variant array of values and then loop two times through it's values.
The Participant array contains 1048576*19 = 19922944 values. (assuming 1048576 rows in your sheet)
Now you loop through these values and for every value you loop through each value again, giving you 19922944*19922944 = 396923697627136 iterations. So that's why excel doesn't respond.
However, within each iteration, you don't even use the value...?
If you want to calculate that number of Pressed in column H and write that number to column T, why would you load all values of columns A to S into the array?
Here is what I would do in VBA
Dim pressedCount As Long
Dim myCell As range
Dim pressedRange As range
With Worksheets("full test")
pressedCount = Application.WorksheetFunction.CountA(.Columns("H"))
If pressedCount = 0 Then Exit Sub 'make sure there are cells or else the next line will fail
Set pressedRange = .Columns("H").SpecialCells(xlCellTypeConstants)
For Each myCell In pressedRange.Cells 'only loop through the cells containing something
.Cells(myCell.Row, "T").Value = pressedCount
Next myCell
End With
I used the With block so I don't have to write the sheet before every range which you should because otherwise it assumes you mean the active sheet.
Note that this assumes that there can be no other values than "Pressed" in column H, not even a header. If there is a header, start at row 2 and use .Range(.Cells(2, "H"), .Cells(.Rows.Count, "H")) instead of .Columns("H")
However this could also be achieved using a Formula like =IF($H7="Pressed",COUNTA(H:H),"")
in my job I have very often to create prefixes. Since last week I think that I can do faster my job with a Macro in excel, but I never have developed in VBA before and need your help.
We get a list with article numbers from any supplier and then I have to create the prefixes for our System. Our System is looking for the prefix and then it knows the supplier. If the first 6 chars are exactly the same with another supplier, so the prefixes from both supplier is getting longer to 7 chars. If it's the same again, the prefix is getting an eight char and so on.
Example:
article numbers from supplier_1:
04012384724993
04012384473373
04012384111453
...
article numbers from supplier_2:
12345671846219
12345629946120
12345629815294
...
article numbers from supplier_3:
12345694724109
12345694715268
12345694724773
...
Now you see that first chars from each supplier are the same.
For Supplier_1 all numbers beginning with "040123", so that's the first prefix.
Supplier_2 ans 3 have the same first 6 chars, so here we use one more to identificate him.
Supp_2 -> "1234567" and "1234562"
Supp_3 -> "1234569"
Supplier_2 have now 2 prefixes, because the 7th position is different in some article numbers, but not the same like supplier_3 is using at this position.
Now I have an excel sheet with column A and B.
In column A I paste all article numbers from supplier_1 and in column B I paste these from supplier_2.
Now I want to run a macro, that create a variable "search" with the first 6 chars from Cell A1 and check it against Column B. If one of the numbers in Column B is the same then variable "search" get additional the next char from Cell A1 and check again. If now the first 7 chars couldn't be found in Column B, it's the first prefix from supplier_1. I want to paste it into column D.
Now the variale "search" get the first 6 chars from the next Cell in column A, (A2), later A3, ... and check it against Column B.
I don't know, how to get the first 6 chars into the variable "search".
Can someone help me please?
Thank you very much.
It is always a good idea to approach big problems in as small chunks as possible. Indeed, it may be a good start to assign the first few characters of a string to a variable. A quick Google search would most likely point you to the Left function. Here's a link to the MSDN page for more information on how it works and how to use it.
I also suggest that you enable the Immediate Window, which can be done with Ctrl + G by default. Using Debug.Print will be one of your strongest tools while writing new code.
Sub Example1()
Dim searchRange As Range
Dim search As String
Set searchRange = ThisWorkbook.Worksheets("Sheet1").Range("A1")
search = Left(searchRange.Value, 6)
Debug.Print search
End Sub
Here's a snippet to give you an idea of how you might go about solving the next step of the problem.
Sub Example2()
Dim compareRange As Range
Dim cell As Range
' SpecialCells is one of many ways to find all populated cells
Set compareRange = ThisWorkbook.Worksheets("Sheet1").Columns(2) _
.SpecialCells(xlCellTypeConstants)
For Each cell In compareRange
Debug.Print cell.Value
Next
End Sub
Please remember that StackOverflow is here to help with specific coding problems that you can't get over with the resources you can find online.
I had some difficulty deciphering exactly what you needed but here is what I have come up with.
I am going to assume that you know how to insert a module into your excel spreadsheet, copy code, and run a macro. If you do not know how please let me know and I will try to assist further.
If this is not what you need please provide me with a sample data set and the answers that you would like to get from the macro so that I can compare as I develop. Best of luck with your projects!
VBA module code
'This subroutine will take the contents of column A cells and search column B for matching digits
' if they are not found it will copy the current search term into column D
Option Explicit
Sub searchPrefix()
Dim cellContents As String
Dim tempSearchVariable As String
Dim isFound As Boolean
Dim quantitySearchCharacters As Integer
Dim entryCounter As Integer
Dim i As Integer
isFound = False
quantitySearchCharacters = 6
entryCounter = 0
'counts number of entries in column A
Cells.Range("A1").Select
Do Until IsEmpty(ActiveCell)
entryCounter = entryCounter + 1
ActiveCell.Offset(1, 0).Select
Loop
' gets value of comparison cell in column A
For i = 0 To entryCounter - 1
cellContents = Cells(1 + i, 1).Value
tempSearchVariable = Left(cellContents, quantitySearchCharacters)
Cells.Range("B1").Select
Do Until IsEmpty(ActiveCell)
' detects if B1 column cell content matches the current search terms and then adds more characters if required
If Left(ActiveCell.Value, quantitySearchCharacters) = tempSearchVariable Then
quantitySearchCharacters = quantitySearchCharacters + 1
tempSearchVariable = Left(cellContents, quantitySearchCharacters)
isFound = True
End If
If isFound Then
isFound = False 'reset flag
Cells.Range("B1").Select
Else
ActiveCell.Offset(1, 0).Select
End If
Loop
Cells(1 + i, 4).Value = tempSearchVariable ' prints the discovered unique prefix to column D
Next i
End Sub
hope all is well.
I am slightly stuck on a VBA function called randbetween in Excel.
Nature of the problem is that I need to create random numbers based on a bunch of other numbers, about 50,000 other numbers in total.
The random numbers I generate must be between 1 and X. X being the other numbers in column D1:D50,000.
As an example: if cell D1 contains the number 5, then I need to create a random number between 1 and 5 in Cell A1. then move on to D2,D3,D4.....etc and create random numbers for each one accordingly, A2,A3,A4...etc.
I tried to use the following but unfortunately the offset part doesn't work. I want to dynamically work through each cell.
the code is as follows:
r = WorksheetFunction.RandBetween(1, Offset(A1, n, 9))
'where n = 2
Most grateful for any help,
Your use of OFFSET is the wrong syntax. You would need somthing like
Range("A1").Offset(RowOffset, ColumnOffset)
But there is a much better approach to achieve your stated goal. Use Range.FormulaR1C1
Sub Demo()
Dim rng As Range
' Define range
Set rng = [A1:A50000]
' Put formulas into the range
rng.FormulaR1C1 = "=RANDBETWEEN(1,RC4)"
'optional, replace formulas with values
rng.Value = rng.Value
End Sub
I have a downloaded bank statement on SHEET1 (ALL).
I have several widgets running along the side one of which
=SUMIF(C:C,H3,D:D)
Searches the Descriptions for the value in H3 (EG: * WAGES *) and totals up the corresponding value in D.
I now need to expand that so that it copies the entire ROW onto a new Spreadsheet.
I'd also like, if possible, to start with an input box so I can search for multiple things at once.
Various code that I have seen / tried will only work for exact values in Row C. But with the bank statement its never the same twice and I'd like it to wildcard the search if possible.
Thanks for your time.
Kind Regards
Alex Nicol
I have recently written VBA code just like this. Where I use the word payments, you can use the word Wages and include your wildcards like so:
a.Cells(b.Row, 16).Value LIKE "*Wages*"
Sub ShortTerm()
Dim a As Range, b As Range
Dim i As Long
Dim j As Long
Dim p As Long
Dim value1 As Variant
i = 4 'the start row for pasting
Set a = ThisWorkbook.Sheets("Payments").UsedRange
For Each b In a.Rows
'in the next line change 16 to reflect the column where WAGES is found
If a.Cells(b.Row, 16).Value = "Short Term" Then
For j = 1 to 16
value1 = a.Cells(b.Row, j).Value
ThisWorkbook.Sheets("DestinationSheet").Cells(i, j).Value = value1
Next
i = i + 1
End If
Next
End Sub
Obviously I am only copying 16 columns and so if that is all you want, this should work. If you need more, make that loop larger. There is probably a way to copy the whole row, but I had originally only wanted specific cells and I had wanted them reorganized which is why I did it the way I did.
See the post on my blog here:
http://automatic-office.com/?p=355