I know that I can select a range when I have cell names. For example Range(A1:B30).select in VBA.
Is there a way to select a range with values written in the cells? I know the following code does NOT work but only that you know what I mean. Range(217:216,9).select.
I want to search for a value and specify the range but how can I do this?
I have already searched the web but I can not find a solution. I also tried to record the macro but it only recorded the code with cell names. I am new to VBA and I can't find a solution for my problem somewhere else. I hope you can understand what I mean.
Say we have:
and we want the range with values between (and including) 10 and 20. This code:
Sub RangeFromValues()
Dim v1 As Long, v2 As Long
Dim r1 As Range, r2 As Range, rng As Range
v1 = 10
v2 = 20
Set r1 = Range("A:A").Find(what:=v1, after:=Range("A1"))
Set r2 = Range("A:A").Find(what:=v2, after:=r1)
Set rng = Range(r1, r2)
rng.Select
End Sub
will produce:
Public Sub TestMe()
Range("1:10").Cells.SpecialCells(xlCellTypeConstants).Select
End Sub
would select only the values, which are not formulas in the first 10 rows:
To select the formulas: type xlCellTypeFormulas instead of xlCellTypeConstants. Just note, that if you do not have anything to Select, it would throw an error.
Another approach, you only need to specify x (low boundary), y (upper boundary) and rngTest (range where you wish to select cells):
Sub Test()
Dim x As Long, y As Long
Dim rngTest As Range
Set rngTest = Range("A1:J20"): x = 5: y = 10
Call RangebyValue(x, y, rngTest)
End Sub
Private Sub RangebyValue(ByVal x As Long, _
ByVal y As Long, _
ByVal rngTest As Range)
Dim vArr(), i As Long, j As Long, rngSelect As Range
vArr = rngTest.Value
For i = LBound(vArr, 1) To UBound(vArr, 1)
For j = LBound(vArr, 2) To UBound(vArr, 2)
If vArr(i, j) >= x And vArr(i, j) <= y Then
If Not rngSelect Is Nothing Then
Set rngSelect = Union(rngSelect, rngTest.Cells(i, j))
Else
Set rngSelect = rngTest.Cells(i, j)
End If
End If
Next j
Next i
rngSelect.Select
End Sub
I think that #Vityata's solution would work well to select all cells that have any value. If you are trying to find a specific value,
Following #Foxfire And Burns And Burns,
I would recommend using a loop through those cells.
For example:
Sub findTheNine()
Dim rng As Range
For Each rng In Range("I7:L11")
If rng.Value = 9 Then
rng.Select
End If
Next rng
End Sub
However, this would only select one of the '9's you wanted to find
Related
I'm trying to get a sub to work that will color fields based on when the values "TRUE" or "FALSE" appears. I've already asked the below question, and have arrived at the code, also below.
VBA Excel Format Range when value is found
Option Explicit
Public Sub MarkCellsAbove()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet2")
Dim v As Variant
Dim i As Long, j As Long, n As Long, m As Long, r As Long, y As Long
Dim rng As Range
Dim rCell As Range
Dim DynamicArea As Range
Dim t As Double
' get last row in column C
n = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
' get last column from A
y = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
' set dynamic area to above values
Set DynamicArea = ws.Range(Cells(1, 1), Cells(n, y))
' clear existing colors over the WHOLE column to minimize file size
DynamicArea.Interior.ColorIndex = xlColorIndexNone
For Each rCell In DynamicArea
Select Case rCell.Text
Case "TRUE"
Set rng = rCell.Offset(-2, 0)
rng.Interior.ColorIndex = 4
Case "FALSE"
Set rng = rCell.Offset(-2, 0)
rng.Interior.ColorIndex = 5
End Select
Next
End Sub
This works well - I am able to color the cell 2 rows above where FALSE or TRUE is found. However - I would like to color not just this cell, but all cells in the range specified by Offset. So, if I specify 8 cells above, I would like to color 8 cells.
I hope someone can help - I'm so close to finishing this!
Try
Set rng = Range(rCell.Offset(-8, 0), rCell.Offset(-1, 0))
Note that you will get a runtime error if rCell is not at least in row 9
I need to search a worksheet by a particular value in a specific column. I have to do something with values in other columns of the found rows. What is the most simple and efficient way to get all row numbers that have the search value in that specific column?
Thanks.
You could try something like that:
Public Function Test(str As String, rng As Range) As Variant
Dim xVal As Variant, Arr() As Variant
Dim i As Long
ReDim Arr(0 To 100)
For Each xVal In rng
If xVal.Value = str Then
Arr(i) = xVal.Row
i = i + 1
End If
Next
If i Then
ReDim Preserve Arr(0 To i - 1)
Test = Arr
Else
Test = 0
End If
End Function
(Done by phone. May contain errors.)
If you are looking for happiness in some region of a worksheet, the select that region and run:
Sub FindingHappiness()
Dim s As String, rng As Range, r As Range
Dim msg As String
Set rng = Intersect(Selection, ActiveSheet.UsedRange)
s = "happiness"
For Each r In rng
If InStr(1, r.Text, s) > 0 Then
msg = msg & vbCrLf & r.Row
End If
Next r
MsgBox msg
End Sub
Note that using this technique will allow you to search in a single row, or in a single column, or in a block of cells, or all the cells on a worksheet, or even in a disjoint group of cells.
I'm trying to combine the values in a range of cells. I created a range as follows:
Dim rng As Range
Set rng = Application.Union(Range("A1:A3"), Range("C1:E2"))
For Each Address In rng
Debug.Print Address.Address
Next
I want to read the cells in horizontal order: A1,C1,D1,E1,A2,C2... etc.
But instead they're being read A1,A2,A3,C1,C2,.. etc
How can I read them horizontally?
Thanks
You have to deal with the Range.Areas within the Range object created with the Union method.
Sub laterally()
Dim r As Long, c As Long, a As Long
Dim mnRW As Long, mxRW As Long, mnCL As Long, mxCL As Long
Dim rng As Range
With Worksheets("Sheet1") '<~~ ALWAYS set the worksheet!
Set rng = Union(.Range("A1:A3"), .Range("C1:E2"))
Debug.Print rng.Address(0, 0)
mnRW = Rows.Count: mxRW = 0
mnCL = Columns.Count: mxCL = 0
With rng
For a = 1 To .Areas.Count
With .Areas(a)
mnRW = Application.Min(mnRW, .Rows(1).Row)
mxRW = Application.Max(mxRW, .Rows(.Rows.Count).Row)
mnCL = Application.Min(mnCL, .Columns(1).Column)
mxCL = Application.Max(mxCL, .Columns(.Columns.Count).Column)
End With
Next a
For r = mnRW To mxRW
For c = mnCL To mxCL
If Not Intersect(.Cells, .Parent.Cells(r, c)) Is Nothing Then _
Debug.Print .Parent.Cells(r, c).Address(0, 0)
Next c
Next r
End With
End With
End Sub
After collecting the extents of the unioned range, each possible cell is looped through and the Intersect method is used to determine whether it belongs in the union or not.
A union of ranges doesn't care in which order the cells are added - whether it's by row or by column. So your original loop to unify the ranges is fine.
If you're concerned about the order in which you read the data, simply read by row or by column accordingly, with an inner and an outer loop. So for example, after you build the range, do as follows:
Dim Col as Variant, Rw as Variant
For Each Col in rng.Columns
For each Rw in Col.Rows
Debug.Print Rw.Address
Next Rw
Next Col
Just thought this might be an interesting addition to Chris' solution?
(I changed the range slightly to highlight some of the advantages)
Dim Rng As Range, Col As Variant, Rw As Variant
Set Rng = Application.Union(Range("A3:A5"), Range("C2:E4"))
For Each Rw In ActiveSheet.UsedRange.Rows
For Each Col In Rw.Columns
If Not Intersect(Col, Rng) Is Nothing Then Debug.Print Intersect(Col, Rng).Address
Next Col
Next Rw
I have looked through the other posts about this and have tried adapted the strategies that were recommend by using Set ActiveWorkbook and Set Active Worksheet and I still get the same error. I hope another set of eyes can help out as I am still very new to VBA and I am not all that comfortable with it yet.
Basically the idea is to copy the cells from column f to column j as values as long as the cells of F do not match the cells of J. I get the row count of column E and use that as my count in the for loop.
Code is here:
Private Sub CalculateRewards_Click()
CopyPaste
End Sub
Sub CopyPaste()
Dim n As Integer
Dim i As Integer
n = Sheets("Calculate").Range("E:E").Cells.SpecialCells(xlCellTypeConstants).Count
i = n
For Counter = 1 To n
Set curCell = Sheets("Calculate").Range("F2:F" &i)
If "$F" &i <> "$J" &i Then
Sheets("Calculate").Range("$F:$F" &i).Copy
Sheets("Calculate").Range("$J:$J" &i).PasteSpecial (xlPasteValues)
Application.CutCopyMode = False
End If
i = i + 1
Next Counter
End Sub
Thanks for the help
Also Edit:
Link to Excel Sheet that has a before page, after first transaction sheet ,and a after second transaction sheet: https://www.dropbox.com/s/n2mn0zyrtoscjin/Rewards.xlsm
CHange this:
Set curCell = Sheets("Calculate").Range("F2:F" &i)
If "$F" &i <> "$J" &i Then
Sheets("Calculate").Range("$F:$F" &i).Copy
Sheets("Calculate").Range("$J:$J" &i).PasteSpecial (xlPasteValues)
Application.CutCopyMode = False
End If
To this:
Set curCell = Sheets("Calculate").Range("F2:F" & i)
If curCell <> Sheets("Calculate").Range("$J" & i) Then
Sheets("Calculate").Range("$J:$J" &i).Value = curCell.Value
End If
May need to do some more teaking as I notice you're working with SpecialCells which essentially filters the range, so iterating For i = 1 to n... probably does not work. Maybe something like:
Dim rngCalc as Range
Set rngCalc = Sheets("Calculate").Range("E:E").Cells.SpecialCells(xlCellTypeConstants)
For each curCell in rngCalc.Cells
If curCell <> curCell.Offset(0, 4) Then
curCell.Offset(0, 4).Value = curCell.Value
End If
Next
EDIT: this sub will calculate the points for the last transaction (identified as the furthest-right column containing transactions) and write them down in column C.
Option Explicit
Sub UpdateCurrentPurchase()
Dim CalcSheet As Worksheet
Dim LastTransRange As Range, TargetRange As Range
Dim LastTransCol As Long, LastTransRow As Long
Dim PurchaseArray() As Variant
Dim Points As Long, Index As Long
'set references up-front
Set CalcSheet = ThisWorkbook.Worksheets("Calculate")
With CalcSheet
LastTransCol = .Cells(2, .Columns.Count).End(xlToLeft).Column '<~ find the last column
LastTransRow = .Cells(.Rows.Count, LastTransCol).End(xlUp).Row
Set LastTransRange = .Range(.Cells(2, LastTransCol), .Cells(LastTransRow, LastTransCol))
Set TargetRange = .Range(.Cells(2, 6), .Cells(LastTransRow, 6)) '<~ column F is the Current Purchase Col
LastTransRange.Copy Destination:=TargetRange '<~ copy last transactions to Current Purchase Col
End With
'pull purchases into a variant array
PurchaseArray = TargetRange
'calculate points
For Index = 1 To LastTransRow
Points = Int(PurchaseArray(Index, 1) / 10) '<~ calculate points
CalcSheet.Cells(Index + 1, 3) = Points '<~ write out the points amount in col C
Next Index
End Sub
ORIGINAL RESPONSE: I think the below will get you where you're going. That being said, it seems like simply overwriting column J with column F (as values) might be the fastest way to an acceptable answer, so if that's the case we can re-work this code to be much quicker using Range objects.
Option Explicit
Private Sub CalculateRewards_Click()
CopyPaste
End Sub
Sub CopyPaste()
Dim LastRow As Long, Counter As Long
Dim cSheet As Worksheet '<~ add a worksheet reference to save some typing
'set references up front
Set cSheet = ThisWorkbook.Worksheets("Calculate")
With cSheet
LastRow = .Range("E" & .Rows.Count).End(xlUp).Row '<~ set loop boundary
'loop that compares the value in column 6 (F) to the value in
'column 10 (J) and writes the value from F to J if they are not equal
For Counter = 1 To LastRow
If .Cells(Counter, 6).Value <> .Cells(Counter, 10).Value Then
.Cells(Counter, 10) = .Cells(Counter, 6)
End If
Next Counter
End With
End Sub
I want to count no of different cells which are selected using VBA.
Consider if we select five distinct cells - D5, C2, E7, A4, B1.
Is there a way I can count these number of cells.
Secondly how can I retrieve data in these cells. Lets say I want to store it in an array.
Thank you for the help.
Dim rngCell as Range, arrArray() as Variant, i as integer
Redim arrArray(1 to Selection.Cells.Count)
i = 1
For each rngCell in Selection
arrArray(i) = rngCell.Value
i = i + 1
Next
Looks like you got it mostly figured out, but here is something to load it into an array if you want it:
Public Sub Example()
Dim test() As Variant
test = RangeToArray(Excel.Selection, True)
MsgBox Join(test, vbNewLine)
End Sub
Public Function RangeToArray(ByVal rng As Excel.Range, Optional ByVal skipBlank As Boolean = False) As Variant()
Dim rtnVal() As Variant
Dim i As Long, cll As Excel.Range
ReDim rtnVal(rng.Cells.Count - 1)
If skipBlank Then
For Each cll In rng.Cells
If LenB(cll.Value) Then
rtnVal(i) = cll.Value
i = i + 1
End If
Next
ReDim Preserve rtnVal(i - 1)
Else
For Each cll In rng.Cells
rtnVal(i) = cll.Value
i = i + 1
Next
End If
RangeToArray = rtnVal
End Function
Thankfully I got a way around it by doing - Selection.Cells.Count
It returns me the cell count for selected cells.
But I am still stuck with dynamically assigning this value to an array as in ---
I = Selection.Cells.Count Dim ValArr(I)