Hi i want to show an output to a specific cell at B column but i really don't have any idea on how to make it to show. Example: if "A2" has the record i want "B2" to show the output. If "A100" has the record, i want "B100" to show the output
Sub Testing()
Dim cell As Range
For Each cell In Range("A2:A4")
If cell.Value = "yes.com" Then
Range("B2:B4").Value = "Correct"
End If
Next
End Sub
The code above shows the output data "Correct" from "B2" to "B4" but what i want it to show on only the specific cell. Please Help
Currently you are looping through a range object. Per cell. One a small dataset this is fine but in your current attempt you'll need to change:
Range("B2:B4").Value = "Correct" for cell.Offset(0,1).value = "Correct"
As per my comment, you can do this a bit smarter/faster. Looping through worksheet cells is slow, certainly on large datasets (a 1000 rows is not that many yet to be honest). Nonetheless it's good to know that a good practice is to go through arrays. Let me show you below:
Sub Testing()
Dim lr As Long, x As Long
Dim arr As Variant
With Sheet1 'Change accordingly
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
arr = .Range("A2:B" & lr)
For x = LBound(arr) To UBound(arr)
If arr(x, 1) = "yes.com" Then
arr(x, 2) = "correct"
End If
Next x
.Range("A2:B" & lr).Value = arr
End With
End Sub
So you can see a few things that will be helpfull:
A reference to a sheet (through a CodeName to refer to a range's parent. Without it, the macro will simply reference the ActiveSheet which is for obvious reasons not always the correct one.
I have made use of a dynamic sized array. The lr variable will get the last used row in column A, so you don't have to work through full qualified references no more.
The arr variable is an array which takes the values from the specified range into memory. Running through data in memory is much quicker than a loop/iteration over worksheet cells. This will become much more noticable when you would have even larger datasets.
I wrote the array back to the range in one go instead of several writings.
Hopefully that helped =)
As previously mentioned a 1000 rows is still not that much. Allthough I suggest you stick with the Array approach, you can also Evaluate column A and fill column B accordingly in one go instead of stepping through a range object. It's an array formula in disguise so not very quick on actual large datasets.
Sub Testing()
Dim lr As Long
Dim rng As Range
With Sheet1 'Change accordingly
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
Set rng = .Range("A2:B" & lr)
rng.Columns(2).Value = .Evaluate("IF(" & rng.Columns(1).Address & "=""yes.com"",""correct"","""")")
End With
End Sub
Just try:
Sub test()
Dim i As Long
For i = 1 To Rows.Count
If Cells(i, 1) = "yes.com" Then Cells(i, 2) = "Correct"
Next
End Sub
It will loop through entire A column.
Alternatively, you can enter in B1 formula:
=IF(A1="yes.com","Correct","")
and drag it all the way down.
Related
I have the following worksheet:
I want to convert the range from C15 to last row/column to array.
I have tried the following code but is not working:
Sub rangeToArray()
Dim arr() As Variant
arr = Range("C15", Range("C15").End(xlDown).End(xlToRight))
End Sub
I get this:
Could someone help me please with this? I would like to get the range from C15 to last row/column and based on different criteria to sort it and copy rows to a different spreadsheet with the same format. I want to convert the range into an array as I have over 30k rows and will work faster.
Thank you!
arr = Range("C15", Range("C15").End(xlDown).End(xlToRight)) is just another way of saying arr = Range("C15").CurrentRegion
On top of that this would currently refer to the ActiveSheet, therefor you might want to try the following:
Sub rangeToArray()
Dim arr() As Variant
With Sheet1 'Change to whichever CodeName your sheet has
arr = .Range("C15").CurrentRegion
End With
End Sub
Note: As said in my comment, CurrentRegion will not work correctly once you start having gaps in your data. Therefor you might want to rework the code to check for the last used row in column C:C and the last used column in row 15:
Sub rangeToArray()
Dim arr() As Variant
Dim lr As Long, lc As Long
With Sheet1 'Change to whichever CodeName your sheet has
lr = .Cells(.Rows.Count, 3).End(xlUp).Row
lc = .Cells(15, .Columns.Count).End(xlToLeft).Column
arr = .Range(.Cells(15, 3), .Cells(lr, lc))
End With
End Sub
Based on this answer with the most reliable way to find the last row and column, the following range is possibly the most reliable way to select all your data to last row and column:
Arr = Range(Cells(15, 3), Cells(Range("A" & Rows.Count).End(xlUp).Row, _
Cells(1, Columns.Count).End(xlToLeft).Column))
Please note, it would be best to specify the sheet for every Cell and Range statement.
I have an Excel workbook with 250,000 rows and 10 columns and I want to split up the data into different workbooks. My idea was to filter the list so that Excel/VBA doesn't have to go through all 250,000 rows every time my code says to look for something in the data.
However, I've run into one specific problem with Sort and also have a general question regarding hidden rows and SpecialCells(xlCellTypeVisible). First off, here's the code:
Option Explicit
Sub Filtering()
Dim wsData As Worksheet
Dim cell As Variant
Dim lRowData As Long, lColData As Long
'filter
Set wsData = ThisWorkbook.Sheets(1)
lRowData = wsData.Cells(Rows.Count, 1).End(xlUp).Row
wsData.Range("A:A").AutoFilter Field:=1, Criteria1:="Name1"
For Each cell In wsData.Range(wsData.Cells(2, 1), wsData.Cells(100, 1)).SpecialCells(xlCellTypeVisible)
Debug.Print cell.Value
Next cell
'sort
lColData = wsData.Cells(1, Columns.Count).End(xlToLeft).Column
wsData.Range(wsData.Cells(1, 1), wsData.Cells(lRowData, lColData)).SpecialCells(xlCellTypeVisible).Sort Key1:=wsData.Range("B1:B100"), Order1:=xlDescending, Header:=xlYes ' returns error because of SpecialCells
End Sub
"Run-time error '1004': This can't be done on a multiple range selection. Select a single range and try again." This occurs in the last line, in
wsData.Range(wsData.Cells(1, 1), wsData.Cells(lRowData, lColData)).SpecialCells(xlCellTypeVisible).Sort Key1:=wsData.Range("B1:B100"), Order1:=xlDescending, Header:=xlYes. It only happens when I use SpecialCells(xlCellTypeVisible), so wsData.Range(wsData.Cells(1, 1), wsData.Cells(lRowData, lColData)).Sort Key1:=wsData.Range("B1:B100"), Order1:=xlDescending, Header:=xlYes works.
My thinking in using SpecialCells(xlCellTypeVisible) was that only then VBA would skip the filtered cells. I've tried it out, though, and to me it seems .Sort skips them anyway, with or without SpecialCells(xlCellTypeVisible) - can someone confirm this?
And this leads to my more general question: One thing I'm not quite clear on is when does Excel/VBA skip filtered rows and when it doesn't. To loop through the visible cells, I need to use SpecialCells(xlCellTypeVisible). With .Sort I (maybe) don't? And this question will always pop up for any operation I'll do on these filtered lists.
This made me wonder: should I work with my original sheet where part of the data is hidden or should I temporarily create a new sheet, copy only the data I need (= excluding the rows I've hidden with the filter) and then work with that? Would this new sheet make it quicker or easier in any way? What is better in your experience?
Your first error occurs when you attempt to copy nonadjacent cell or range selections e.g multiple nonadjacent rows within the same column (A1, A3, A5). This is because Excel "slides" the ranges together and pastes them as a single rectangle. Your visible special cells are nonadjacent, and therefore can't be copied as a single range.
It seems that excel is looping through all of the cells in your range, not just the visible ones. Your debug.print is returning more rows than just those that are visible.
I would take a different approach to tackling your problem by using arrays, which VBA is able to loop through extremely quickly compared to worksheets.
Using this approach, I was able to copy 9k rows with 10 columns based on the value of the first column from a sample size of 190k in 4.55 seconds:
EDIT: I did some messing around with the arrays which brought the time down to 0.45 seconds to copy 9k rows based on the first column from an initial 190k using the following:
Option Explicit
Sub update_column()
Dim lr1 As Long, lr2 As Long, i As Long, j As Long, count As Long, oc_count As Long
Dim arr As Variant, out_arr As Variant
Dim start_time As Double, seconds_elapsed As Double
Dim find_string As String
start_time = Timer
' change accordingly
find_string = "looking_for"
With Sheets("Sheet1")
' your target column in which you're trying to find your string
lr1 = .Cells(Rows.count, "A").End(xlUp).Row
lr2 = 1
' all of your data - change accordingly
arr = .Range("A1:J" & lr1)
' get number of features matching criteria to determine array size
oc_count = 0
For i = 1 To UBound(arr, 1)
If arr(i, 1) = find_string Then
oc_count = oc_count + 1
End If
Next
' redim array
ReDim out_arr(oc_count, 9)
' write all occurrences to new array
count = 0
For i = 1 To UBound(arr, 1)
If arr(i, 1) = find_string Then
For j = 1 To 10:
out_arr(count, j - 1) = arr(i, j)
Next j
count = count + 1
End If
Next
' write array to your target sheet, change sheet name and range accordingly
Sheets("Sheet2").Range("A1:J" & (oc_count + 1)) = out_arr
End With
seconds_elapsed = Round(Timer - start_time, 2)
Debug.Print (seconds_elapsed)
End Sub
It isn't super clean and could probably do with some refining, but if speed is important (which it often seems to be), this should do the job well for you.
As per bm13563 comment you are copying nonadjacent cells.
Also using a Sort will be altering your base data which could have an impact if you ever need to determine how it was initially ordered in the future.
Working with filters can become quite complex so a simpler (and not particularly slow) method could be to do a string search with your filtering value in your chosen column and then loop through the instances returned performing actions on each result.
The (slightly adapted) code below from David Zemens would be a good starting point (copied from Find All Instances in Excel Column)
Sub foo()
Dim fnd As String, FirstFound As String
Dim FoundCell As Range, rng As Range
Dim myRange As Range, LastCell As Range
Set huntRange = Range("A:B")
Set LastCell = myRange.Cells(myRange.Cells.Count)
Set FoundCell = myRange.Find(what:="January", after:=LastCell, LookIn:=xlValues)
If Not FoundCell Is Nothing Then
FirstFound = FoundCell.Address
Do
'Do your actions here, you can get the address of the found cell to return row etc.
MsgBox (FoundCell.Value)
Set FoundCell = myRange.FindNext(FoundCell)
Loop While (FoundCell.Address <> FirstFound)
End If
Set rng = FoundCell '<~~ Careful, as this is only the LAST instance of FoundCell.
End Sub
This is quite simple, I am aware, but something is going wrong for me. I simply want to subtract the values I have in column B from the values I have in column C and place these results in column Q.
I have assigned my strFormula(1) as a variant and then applied the equation to the strFormula(1). I have altered the following code from #Manhattan here on Stack Overflow :)
Sub FormulasNoLoops()
Dim strFormulas(1) As Variant
With ThisWorkbook.Sheets("Sheet1")
strFormulas(1) = "=(C2-B2)"
.Range("Q2:Q130").Formula = strFormulas
.Range("Q2:Q130").FillDown
End With
End Sub
There is no error when I run the script but also no result in column Q.
Ideally, I do not even want to enter the last cell of the column but maybe use .End(xlUp) somewhere.
Thanks all!
first
Dim strFormulas(1) As Variant
is creating an array with two items, 0,1
For one formula I would avoid the variable totally.
But if you want to use it just make it a string without the (1)
Dim strFormulas As String
Then load it:
strFormulas = "=(C2-B2)"
Also when you apply the formula to the whole range there is no need to fill down:
Sub FormulasNoLoops()
With ThisWorkbook.Sheets("Sheet1")
.Range("Q2:Q130").Formula = "=(C2-B2)"
End With
End Sub
Sub test()
Dim lastrow As Long
Dim rng As Range
last_row = ThisWorkbook.Worksheets("sheet1").Cells(Rows.Count, 1).End(xlUp).Row
Set rng = Range("C2:C" & last_row)
rng.Formula = "=B2-A2"
End Sub
Thank you all for helping with my earlier query. I have included my next obstacle as a separate thread and hope that doesn't violate any rules/etiquette.
I now have a search facility that creates a list of potentially relevant diagnoses:
What I'd like to be able to do is work down the list of potentially relevant diagnoses and manually eliminate those that are not relevant by placing a "x" in the adjacent cell. I would then like to press a button and for all checked diagnoses to be appended to a list on another sheet (titled "List"):
In an ideal world, repeating the search/select/button process would then simply append new diagnoses to the same list, i.e. identify the next blank cell in a column on "List" and carry on from there. One potential difficulty is that I need to copy the diagnosis text out of each cell rather than the formula that's actually there.
Gary's Student has answered a similar query previously with this script but it doesn't quite get me far enough as it takes data from a single cell and doesn't distinguish between text/formulae:
Sub ButtonCode()
Dim N As Long
N = Cells(Rows.Count, "A").End(xlUp).Row + 1
Cells(N, "A").Value = Range("C3").Value
End Sub
Can anyone help?
You may want to try something like I've provided below. Notice that you can grab the cells that you specify by the all the 'x' checkmarks by using the .Offset property. Code below:
Sub move_diagnoses()
Dim diagnosesheet As Worksheet
Dim copysheet As Worksheet
Dim last_diagnosis_row As Integer
Dim last_list_row As Integer
Dim loserange As Range
Dim losecell As Range
'Set your worksheets first
Set diagnosesheet = Worksheets("Diagnoses")
'I titled the worksheet you have the diagnoses on as 'Diagnoses' since you didn't specify
Set copysheet = Worksheets("List")
'Now set the range (i.e. collection of cells) that enumerate all the potential diagnoses
'First find the last row in the diagnoses column
'Then find the last used row in the 'List' worksheet
last_diagnosis_row = diagnosesheet.Range("E" & Rows.Count).End(xlUp).Row
last_list_row = diagnosesheet.Range("A" & Rows.Count).End(xlUp).Row
Set loserange = diagnosesheet.Range("D2:D" & last_diagnosis_row)
'Notice the loserange (i.e. the range that contains the all the checkmarks is defined from D2 onwards
For Each losecell In loserange.Cells
If Trim(losecell.Value) = "x" Then
copysheet.Cells(last_list_row, 1).Value = losecell.Offset(0, 1).Text
copysheet.Cells(last_list_row, 2).Value = losecell.Offset(0, 2).Text
last_list_row = last_list_row + 1
End If
Next losecell
End Sub
I'm new to vba programming and I would like to work on a function to fix salutations in an excel file.
To start, I would just like to append a Dear " to a name in the first column, and put this value in the next column, so that I would end up with the name in the first column and "Dear name" in the next column.
The function I have so far, is putting "Dear " in the next column, but it is not appending that to the text in the first column. Could someone help me correct my code?
Sub letterSalutationFixer()
Dim letterSalutationColumn As Range
Set letterSalutationColumn = Columns(1)
For Each Cell In letterSalutationColumn
Cell.Offset(, 1).Value = "Dear " & Cell.Text
Next
End Sub
PS. I do realise that I don't necessarily need to do this programmatically since it doesn't take that long to do with the functions already available, but I eventually want to expand this to fix other data with more complexity - and just thought I could start with something simple.
Many thanks in advance!
The reason it's blank is that Cell is equivalent to the whole column. You're close though. If you did...
For Each Cell In letterSalutationColumn.Cells
..l it would cycle through each cell.
However, the way it's written, it would cycle through each cell in the whole column, which could crash Excel, or at least slow things way down.
Here's a reworked version of what you're trying to do. It only acts on the cells in column A with content:
Sub Salutation()
Dim ws As Excel.Worksheet
Dim LastRow As Long
Dim NameRange As Excel.Range
Dim cell As Excel.Range
Set ws = ActiveSheet
With ws
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
Set NameRange = .Range("A2:A" & LastRow)
For Each cell In NameRange
cell.Offset(, 1) = "Dear " & cell.Text
Next cell
End With
End Sub
It also declares all variables, something you want to get in the habit of doing. Do a search on Option Explicit to learn how to force yourself to.
It also uses a With statement to fully qualify Object references, so that instead of just referring to Column(1) or Range(something) you're specifying that it's in ws, which has been set to the ActiveSheet.
Another way is the VBA alternative of
Using a formula in column B that runs the concatenation against the used part of column A (ie in B1 ="Dear " &A1 etc)
The formula then is copied over itself as a value to remove the formula
code
Sub QuickCon()
Dim rng1 As Range
Set rng1 = Range([a1], Cells(Rows.Count, "A").End(xlUp))
With rng1.Offset(0, 1)
.FormulaR1C1 = "=""Dear "" &RC[-1]"
.Value = .Value
End With
End Sub