Range object returns empty values - vba

I have a set range to a variable in this fashion:
Dim srcRng As Range
Set srcRng = Range(hrwb.Worksheets(1).Range(yomColAddress)(1).Address, _
Cells(hrwb.Worksheets(1).Range(yomColAddress).row + 200, rightMostCol)(1).Address)
for some weird reason when I call
srcRng(1) 'actually instead of 1 is i that runs 1 to srcRng.Count
it doesn't return the upper leftmost cell value. Any ideas why?
(for those who are not familiar with this technique: http://www.cpearson.com/excel/cells.htm)
Informations:
at execution time the variables yomColAddress=$AL$9 and righMostCol=40
hrwb.Worksheets(1).Range(yomColAddress)(1) works as expected.
With MsgBox srcRng(1).Address & " value:" & srcRng(1).Value I get "$AL$9 value:"
The value of AL9 is the text "yom"
The actual code is:
Dim srcRng As Range
Set srcRng = Range(hrwb.Worksheets(1).Range(yomColAddress)(1).Address, Cells(hrwb.Worksheets(1).Range(yomColAddress).row + 200, rightMostCol)(1).Address)
Dim i As Integer
i = 1
While (weekDayCol = 0 And i <= srcRng.count)
If loneHebDayLetter("à", "ä", srcRng(i)) Then'loneHebDayLetter checks some conditions on a cell
weekDayCol = srcRng(i).Column
End If
i = i + 1
Wend

I think I get what goes wrong here:
The code itself is working well but not on the good data (This is a supposition but I just did some tests with a custom workbook)
Short version
Just add srcRng.Select after Set srcRng (no real interest but to understand what it does) and I think you will get what happens if my supposition is correct.
Longer version
When you do Set srcRng = ... it does create the correct Range but it is not linked to any sheet actually ... It just means remember a Range which goes from cell X to cell Y.
The point is: The sheet (let's say "sheet2") where your code is executed isn't the same as the one where the datas are (say "sheet1") so srcRng(1) is understood as Sheets("sheet2").srcRng(1) instead of Sheets("sheet1").srcRng(1) (<- that's what you want)
Even if not elegant, this should work:
Dim srcRng As Range
With hrwb.Worksheets(1)
Set srcRng = Range(.Range(yomColAddress)(1).Address, Cells(.Range(yomColAddress).row + 200, rightMostCol)(1).Address)
Dim i As Integer
i = 1
While (weekDayCol = 0 And i <= srcRng.count)
If loneHebDayLetter("à", "ä", .Range(srcRng.Address)(i).Value) Then 'I assume it take the value not the cell: if it take the cell you may get an error!
weekDayCol = srcRng(i).Column
End If
i = i + 1
Wend
End With
What is important is the use of .Range(srcRng.Address)(i).Value to access the value in the right worksheet! (That's why this trick is not needed here: srcRng(i).Column because colum numbers do not change from one sheet to an other)
(NOTE: I used with to optimize/clarify the code)
If something isn't clear tell me

Related

Finding dates and storing ranges in variables

I'm trying to find "blocks" in the worksheet that have dates in the A-column. A block is seperated by lines as you can see from the picture. The whole file is full of these blocks but I only need the blocks that have dates in the A-column. Just to be clear I don't just need the rows with the dates but the full block that contains the date.
A block in my files for example is the Range A172:G192.
Picture of the file:
[![enter image description here][1]][1]
How should I continue after selecting the first block? I probably should use the Find function starting from row 184 or the row of ResultDown variable moving down the sheet on "A" Column. However the row needs to be dynamic, so I can use it for the next block. Also I have no idea how many blocks there will be, so I would like to hear from you how to solve this aswell.
I would like to save all the blocks as different variables and then hide all the blocks in the worksheet and then just unhide the blocks that I stored in the variables.
My biggest problem is the last row.
Result2 = Range(Cells(StartcellRow, 1), Cells(1000, 1)).Find(What:="**/**/****", After:=Range(Cells(StartcellRow, 1))).Select
I keep getting an runtime error 1004
Public Sub FB_MAKRO()
Dim FBwb As Workbook
Dim FBsht As Worksheet
Dim ACol As Range
'Set variables for workbook and sheet
Set FBwb = Workbooks.Open(Filename:="C:\Users\l000xxx\Desktop\Makrot\FORCED BALANCE MAKRO\FB HARJOITUS.xls")
Set FBsht = FBwb.Sheets("Forced Balance")
Set ACol = FBsht.Range("A1:A1000")
'I want ACol variable to be the entire A-Column. Any ideas?
'For some reason the range function is not working here?
'This locates the date in A'column, so I can select the correct block
Result = Range("A3:A1000").Find(What:="**/**/****", After:=Range("A3")).Address
'This is the top left corner of the Block1 selection
ResultUp = Range(Result).End(xlUp).Offset(-1, 0).Address
Range(ResultUp).End(xlDown).Select
Range(ActiveCell, ActiveCell).End(xlDown).Select
'The ResultsDownLastRow variable is for Block2 find function
'ResultDown is the bottom right corner of the Block1
ResultsDownLastRow = Range(ActiveCell, ActiveCell).End(xlDown).Address
ResultDown = Range(ActiveCell, ActiveCell).End(xlDown).Offset(-2, 6).Address
'First Block assigned. I plan to use this in the end when I hide everything and then unhide these blocks
Block1 = Range(ResultUp, ResultDown).Select
' NEXT BLOCK STARTS HERE
'StartCellRow is the cell that the find function should start looking for Block2
'Result2 is the find function for Block2
StartcellRow = Range(ResultsDownLastRow).Row
Result2 = Range(Cells(StartcellRow, 1), Cells(1000, 1)).Find(What:="**/**/****", After:=Range(Cells(StartcellRow, 1))).Select
End Sub
'This returns value 194
StartcellRow = Range(ResultsDownLastRow).Row MsgBox StartcellRow
'This should work but doesn't. I get a syntax error
Range(Cells(StartcellRow &","& 1),Cells(1000 & "," & 1)).Find(What:="**/**/****", After:=Range(Cells(StartcellRow& ","& 1)).Select
This doesn't work either
'StarcellRow gives out value of 194
StartcellRow = Range(ResultsDownLastRow).Row
Result2 = Range("A&:StartcellRow:A648").Find(What:="**/**/****", After:=Range("A&:StartcellRow")).Select
This doesn't give me a syntax error but it's not working
I would search for all currency header and store their rownumber into an array. For each rownumber in the array i would look into the cell below (rownumber + 1). when there is a date in the cell, i would set the range in the following way:
set rangeWithDate = Range(Cells(actualRowNumberInArray - 1, 1), Cells(nextRowNumberInArray - 2, 7))
Array:
Dim array1() As long
Redim array1(5)
For i = 1 To 5
array(i) = i
Next i
Redim array1(10) ' changes the "length" of the array, but deletes old entries
For i = 1 To 10
Feld1(i) = i
Next i
Redim Preserve array1(15) ' changes the "length" of the array without deleting old entries

Create new worksheet based on text in coloured cells, and copy data into new worksheet

I have a large data set which I need to manipulate and create individual worksheets. Within column B all cells which are coloured Green I would like to make a new worksheet for. Please see screen shot.
For example I would like to create worksheets titled "Shopping" & "Retail". Once the worksheet is created, I would then like to copy all the data between the "worksheet title" (Green Cells) from columns ("B:C") & ("AI:BH") Please see screen shot below for expected output;
The code I have so far is below as you can see it is not complete as I do not know how I would go about extracting data between the "Green Cells".
Sub wrksheetadd()
Dim r As Range
Dim i As Long
Dim LR As Long
Worksheets("RING Phased").Select
LR = Range("B65536").End(xlUp).Row
Set r = Range("B12:B" & (LR))
For i = r.Rows.Count To 1 Step -1
With r.Cells(i, 1)
If .DisplayFormat.Interior.ColorIndex = 35 Then
MsgBox i
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = Cells (i,1).Value
Worksheets("RING Phased").Select
End If
End With
Next i
End Sub
Any help around this would be much appreciated.
Sorry for taking a while to get back to this, I've been somewhat busy the last few days, so I haven't had much time to be on StackOverflow.
Anyway, the way I'd go about this would be to store all the found values in an array, and then loop through that array in order to find the distance between them.
The following code works for me, using some very simplified data, but I think the principle is sound:
Option Explicit
Option Base 0
Sub wrksheetadd()
Dim r As Range, c As Range
Dim i As Long: i = 0
Dim cells_with_color() As Range: ReDim cells_with_color(1)
With Worksheets("RING Phased")
' Since it doesn't seem like the first cell you want to copy from is colored, hardcode that location here.
' This also saves us from having to test if the array is empty later.
Set cells_with_color(i) = .Range("B12")
i = i + 1
Set r = Range(.Range("B13"), .Range("B" & .Cells.Rows.Count).End(xlUp))
' Put all the cells with color in the defined range into the array
For Each c In r
If c.DisplayFormat.Interior.ColorIndex = 35 Then
If i > UBound(cells_with_color) Then
ReDim Preserve cells_with_color(UBound(cells_with_color) + 1)
End If
Set cells_with_color(i) = c
i = i + 1
End If
Next
' Loop through the array, and copy from the previous range value to the current one into a new worksheet
' Reset counter first, we start at 1, since the first range-value (0 in the array) is just the start of where we started checking from
' (Hmm, reusing variables may be bad practice >_>)
i = 1
While i <= UBound(cells_with_color)
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = cells_with_color(i).Value
' Set the range to copy - we could just do this in the copy-statement, but hopefully this makes it slightly easier to read
Set r = .Rows(CStr(cells_with_color(i - 1).Row) + 1 & ":" & CStr(cells_with_color(i).Row))
' Change the destination to whereever you want it on the new sheet. I think it has to be in column one, though, since we copy entire rows.
' If you want to refine it a bit, just change whatever you set r to in the previous statement.
r.Copy Destination:=Worksheets(CStr(cells_with_color(i).Value)).Range("A1")
i = i + 1
Wend
End With
End Sub
It probably lacks some error-checking which ought to be in there, but I'll leave that as an exercise to you to figure out. I believe it is functional. Good luck!

VBA Range is Nothing

Running the code below and what I'm hoping to see as a result is to have all columns with 0s on row 27 hidden - but depending on the frequency of the data, the range for those columns to be hidden is different. Basically anything that is in a sheet that starts with Daily/monthly/weekly will have to have columns hidden, all the rest of the sheets should be left alone.
It worked on a simple workbook using an if statement (sheets starting with X vs. all other), but when I added the case statement it broke...
The line marked down with bold is the one where I get an error:
Run-time error '1004'
Application-defined or object-defined error
I'm new to this, so please feel free to suggest a good vba tutorials website/book.
Sub Hide_Zero_Columns()
Dim WS As Worksheet
Dim Col_to_hide As Range
Dim Range_to_hide As Range
Dim X As Integer
For Each WS In ThisWorkbook.Sheets
Worksheets(WS.Name).Activate
With WS
Select Case Data_Frequency_Sheets
Case Left(WS.Name, 5) = "Daily"
Set Range_to_hide = Range("BDV$27:CWH$27")
Case Left(WS.Name, 5) = "Month"
Set Range_to_hide = Range("AY$27:CO$27")
Case Left(WS.Name, 5) = "Weekl"
Set Range_to_hide = Range("HF$27:NN$27")
Case Else
Set Range_to_hide = Range("A1:B1")
End Select
Select Case Data_Condition
Case Left(WS.Name, 5) = "Daily"
X = 1
Case Left(WS.Name, 5) = "Month"
X = 30
Case Left(WS.Name, 5) = "Weekl"
X = 7
Case Else
X = 999
End Select
If X <> 999 Then
For Each Col_to_hide In ActiveSheet.Range(Range_to_hide) '<-- Error here
If UCase(Col_to_hide) = 0 Then
Col_to_hide.EntireColumn.Hidden = True
Else: Col_to_hide.EntireColumn.Hidden = False
End If
Next Col_to_hide
End If
End With
Next
ActiveWorkbook.Worksheets("Registrations").Activate
End Sub
Since you have already defined a Range, you the problem is you are trying to evaluate: Sheet.Range(Range) which throws the error you are getting.
Since it appears you are wanting to iterate across the columns, all you need to do is change the line to this:
' Iterate across the columns in the defined range.
For Each Col_to_hide In Range_to_hide.Columns
' Each "Col_to_hide" will represent all cells within the column.
MsgBox Col_to_hide.Address
The error you're getting is because you're passing a Range object as the argument to Activesheet.Range() and it doesn't accept that because of the Range object's default value. It would be like doing this in the Immediate Window
?Range(Range("A1")).Address
You'll get the same error. Whereas with
?Range("A1").Address
You don't. You could do this too
?Range(Range("A1").Address).Address
So the thing is that when you don't specify a property for an object, like Range("A1") instead of Range("A1").Address, then the default property is used. The Range object is a bit strange, but in this case its default property is the Value property. So Activesheet.Range(Range_to_hide) is the same as Activesheet.Range(Range_to_hide.Value). And if Range_to_hide is a multi-cell range, then it's Value property returns an array, which you definitely can't pass into a Range's argument.
That's the explanation, the short answer is simply to use Range_to_hide, like
For Each Col_to_hide in Range_to_hide.Columns
Why the .Columns. Remember when I said that the Range object was a little strange. Well, unlike most objects, it has two default properties based on the context you're using it in. Earlier, the default property was Value, but in the context of a For..Each, the default value is Cells. If you don't specify .Columns in your For..Each, then it will loop through every cell in the Range. In fact, I always specify how the range is aggregated, even if it's the default Cells.

lookup a number and increment value in another cell within same row

I would like to create a macro in excel that lets me increment the counts of a part whenever I press a command button.
Currently, my concept is to use vlookup to get the existing counts for that part using the following. However, it does not increment the actual counts value in the cell, which is what I want. I suspect it's cos vlookup is only used to return a value within the cell, but the cell is not activated in the process for actual increment. Can someone please advise how I can correct it? I'm still new to vba. Thanks!!! :)
E.g. Vlookup finds C1value in Cell A5 of Sheets("Location"). It will automatically increment the value in Cell C5 by 1.
Sub FindAddTools()
Dim C1Qnty As Double
C1value = Sheets("Issue").Range("D11")
Sheets("Location").Activate
C1Qnty = WorksheetFunction.VLookup(C1value, Range("A:D"), 3, False)
C1Qnty = C1Qnty + 1
End Sub
ADD ON: an add-on to my original question. I was wondering if it is possible to do the same for an entire range?
E.g. C1value is now a range of Sheets("Issue").Range("D11:D20"). I want to find all values within this range in Sheets("Location") and increment their corresponding counts in Column C.
Is there a way to do this without repeating the same procedure for all cells of the range?
Thanks! :)
Here's my shot at it. If the value isn't matched nothing happens:
Sub FindAddTools()
Dim RangeToMatch As Excel.Range
Dim cell As Excel.Range
Dim C1Value As Variant
Dim C1Row As Variant
Set RangeToMatch = Sheets("Issue").Range("D2:D11")
For Each cell In RangeToMatch
C1Value = cell.Value
With Sheets("Location")
C1Row = Application.Match(C1Value, .Range("A:A"), 0)
If Not IsError(C1Row) Then
.Range("C" & C1Row).Value = .Range("C" & C1Row).Value + 1
End If
End With
Next cell
End Sub
I edited it so that it cycles through a range of cells to match. That range is set to D2:D11 above.
Based on your comments, I think this should do it.
NB: you don't have to Activate worksheets to perform the functions referencing their cells/ranges.
Sub FindAddTools()
Dim shIssue as WOrksheet: Set shIssue = Sheets("Issue")
Dim shLoc as Worksheet: Set shLoc = Sheets("Location")
Dim allC1Values as Range
Dim C1Value as Variant
Dim C1Qnty As Double
Dim foundRow as Long
Set allC1Values = shIssue.Range("D11:D100") '## Modify as needed.
For each C1Value in allC1Values.Cells
C1Qnty = WorksheetFunction.VLookup(C1value, shLoc.Range("A:D"), 3, False)
C1Qnty = C1Qnty + 1
foundRow = WorksheetFunction.Match(c1Value,shLoc.Range("A:A"),False)
shLoc.Range("C" & foundRow).Value = CqQnty
Next
End Sub
Be careful with this. You're immediately writing to the same cell you just "found" with the VLOOKUP function, so, obviously if you run this macro again, you're going to increment it again. But, this may be the desired functionality, if so, no problem.
NOTE: There is no error trapping for if C1Value is not found in the VLOOKUP or MATCH functions.

pulling out data from a colums in Excel

I have the following Data in Excel.
CHM0123456 SRM0123:01
CHM0123456 SRM0123:02
CHM0123456 SRM0256:12
CHM0123456 SRM0123:03
CHM0123457 SRM0789:01
CHM0123457 SRM0789:02
CHM0123457 SRM0789:03
CHM0123457 SRM0789:04
What I need to do is pull out all the relevent SRM numbers that relate to a single CHM ref. now I have a formular that will do some thing like this
=INDEX($C$2:$C$6, SMALL(IF($B$8=$B$2:$B$6, ROW($B$2:$B$6)-MIN(ROW($B$2:$B$6))+1, ""), ROW(A1)))
however this is a bit untidy and I really want to produce this same using short vb script, do i jsut have to right a loop that will run though and check each row in turn.
For x = 1 to 6555
if Ax = Chm123456
string = string + Bx
else
next
which should give me a final string of
SRM0123:01,SRM123:02,SRM0256:12,SRM0123:03
to use with how i want.
Or is ther a neater way to do this ?
Cheers
Aaron
my current code
For x = 2 To 6555
If Cells(x, 1).Value = "CHM0123456" Then
outstring = outstring + vbCr + Cells(x, 2).Value
End If
Next
MsgBox (outstring)
End Function
I'm not sure what your definition of 'neat' is, but here is a VBA function that I consider very neat and also flexible and it's lightning fast (10k+ entires with no lag). You pass it the CHM you want to look for, then the range to look in. You can pass a third optional paramater to set how each entry is seperated. So in your case you could write (assuming your list is :
=ListUnique(B2, B2:B6555)
You can also use Char(10) as the third parameter to seperat by line breaks, etc.
Function ListUnique(ByVal search_text As String, _
ByVal cell_range As range, _
Optional seperator As String = ", ") As String
Application.ScreenUpdating = False
Dim result As String
Dim i as Long
Dim cell As range
Dim keys As Variant
Dim dict As Object
Set dict = CreateObject("scripting.dictionary")
On Error Resume Next
For Each cell In cell_range
If cell.Value = search_text Then
dict.Add cell.Offset(, 1).Value, 1
End If
Next
keys = dict.keys
For i = 0 To UBound(keys)
result = result & (seperator & keys(i))
Next
If Len(result) <> 0 Then
result = Right$(result, (Len(result) - Len(seperator)))
End If
ListUnique = result
Application.ScreenUpdating = True
End Function
How it works: It simple loops through your range looking for the search_string you give it. If it finds it, it adds it to a dictionary object (which will eliminate all dupes). You dump the results in an array then create a string out of them. Technically you can just pass it "B:B" as the search array if you aren't sure where the end of the column is and this function will still work just fine (1/5th of a second for scanning every cell in column B with 1000 unique hits returned).
Another solution would be to do an advancedfilter for Chm123456 and then you could copy those to another range. If you get them in a string array you can use the built-in excel function Join(saString, ",") (only works with string arrays).
Not actual code for you but it points you in a possible direction that can be helpful.
OK, this might be pretty fast for a ton of data. Grabbing the data for each cell takes a ton of time, it is better to grab it all at once. The the unique to paste and then grab the data using
vData=rUnique
where vData is a variant and rUnique is the is the copied cells. This might actually be faster than grabbing each data point point by point (excel internally can copy and paste extremely fast). Another option would be to grab the unique data without having the copy and past happen, here's how:
dim i as long
dim runique as range, reach as range
dim sData as string
dim vdata as variant
set runique=advancedfilter(...) 'Filter in place
set runique=runique.specialcells(xlCellTypeVisible)
for each reach in runique.areas
vdata=reach
for i=lbound(vdata) to ubound(vdata)
sdata=sdata & vdata(i,1)
next l
next reach
Personally, I would prefer the internal copy paste then you could go through each sheet and then grab the data at the very end (this would be pretty fast, faster than looping through each cell). So going through each sheet.
dim wks as worksheet
for each wks in Activeworkbook.Worksheets
if wks.name <> "CopiedToWorksheet" then
advancedfilter(...) 'Copy to bottom of list, so you'll need code for that
end if
next wks
vdata=activeworkbook.sheets("CopiedToWorksheet").usedrange
sData=vdata(1,1)
for i=lbound(vdata) + 1 to ubound(vdata)
sData=sData & ","
next i
The above code should be blazing fast. I don't think you can use Join on a variant, but you could always attempt it, that would make it even faster. You could also try application.worksheetfunctions.contat (or whatever the contatenate function is) to combine the results and then just grab the final result.
On Error Resume Next
wks.ShowAllData
On Error GoTo 0
wks.UsedRange.Rows.Hidden = False
wks.UsedRange.Columns.Hidden = False
rFilterLocation.ClearContents