Excel 2007 using VBA write formula to merged cells - vba

I have a list of values starting at G11 and extending a variable length down column G. I have a string array which contains the addresses of ranges of the G column data to be averaged. In the H column starting at H11 I have merged cells where I want the averaged values to appear. The number of G column cells to be averaged together also varies. The following code I tried failed since the formula was entered as is, rather than having the ranges(i) array value placed into the formula.
For i = 0 To range_num
ActiveCell.Formula = "=AVERAGE(ranges(i))"
Next i
ranges() contains the ranges (in string form) to be averaged. And for example:
ranges(i) = "G11:G15"
fori = 0 and range_num is the number of averages that will be done.
Each G column value is only included in 1 average, each group of values to be averaged is consecutive, and each group is directly to the left of the merged cell where I want the average to appear. Does anyone have some insight on how I could better format my code to achieve this? Any ideas are appreciated, thanks.

The reason the formula is entering 'as is' is because you'd need to concatenate it into the string.
For i = 0 To range_num
ActiveCell.Formula = "=AVERAGE(" & ranges(i) & ")"
Next i

If I have interpreted correctly, you are looking for a code snippet that will loop through a range of cells and set the formula based on the literal string found in another cell. Try the following:
sub SetCellFormulas()
dim sWS as worksheet
dim aCell as range
dim aRange as range
dim Lrow as long
set sws = thisworkbook.sheets("yourworksheetname")
'*** Locate last cell with values in column G
lrow = sws.Range("G11").end(xldown).row
'*** Set range to loop through
set arange = sws.Range("G11:G" & Lrow)
for each acell in arange
'*** set formula of cell 1 to the left of G to the concatenated formula
aCell.offset(0,1).formula = "=AVERAGE(" & acell.value & ")"
next acell
end sub

Related

Log Return calculation for each day in each column in a Dynamic Named Range

I have two Dynamic Named Ranges - HDaCClose and HDaCReturns.
In DNR HDaCClose I have multiple columns with daily close prices of financial instruments for 100 days, thus 100 rows and X columns, see below.
For the DNR HDaCReturns I need to calculate the Natural Log of returns by using the following formula: LN = n/(n - 1). In essence I need the Natural Log of today's Closing price, divided by yesterday's closing price.
I tried to get the logic of looping trough each cell in a column and performing the calculations, before moving to the calculations for each cell in the next column.
The output that I got from it is:
Obviously inaccurate...
What should be the correct approach for a loop through each cell in each column in range HDaCReturns and perform the calculations, that I need?
I happen to know from another of your questions that you can use .FormulaR1C1 to assign the formula to the correct range of cells without need for a loop. The last row would be excluded from the output calculation as has no row to compare with.
With ThisWorkbook.Worksheets("HistoricalDataandCalculations").Range ("HDaCReturns")
.Offset(2, 1).Resize(.Rows.Count - 3, .Columns.Count - 1).FormulaR1C1 = "=LN(R[-103]C/R[-102]C)"
End With
The R[-103]C/R[-102] part of the formula references the Closed Values in the current context. You might be able to replace this with a named range reference.
A really verbose way of looking at it would be as follows, where you work with the named ranges and use a array formula (.FormulaArray) to do the calculation:
Sub AddLnCalcs()
Dim wb As Workbook
Dim ws As Worksheet
Dim returnRange As Range
Dim closeRange As Range
Dim closedRangeData As Range
Set wb = ThisWorkbook
Set ws = wb.Worksheets("HistoricalDataandCalculations")
Set returnRange = ws.Range("HDaCReturns")
Set closeRange = ws.Range("HDaCClose")
With closeRange
Set closedRangeData = .Offset(2, 1).Resize(.Rows.Count - 3, .Columns.Count - 1) '.Address 'offset 2 to avoid headers of e.g. EURUSD and close and resize to exclude headers and last row and exclude date column
End With
Dim formulaOutputArea As Range
With returnRange
Set formulaOutputArea = .Offset(2, 1).Resize(.Rows.Count - 3, .Columns.Count - 1)
formulaOutputArea.FormulaArray = "=LN(" & closedRangeData.Address & "/" & closedRangeData.Offset(1, 0).Address & ")"
End With
End Sub

Saving number as text, how to automate it

I have a 7702216772 number inside a cell. If I put a ' before the fist digit and click Enter Excel transforms the number to a text and puts a green triangle at the left top of the cell:
I have many rows of similar numbers all of which need to be transformed into text. However clicking each and adding ' before the first symbol and clicking Enter would take a lot of time. Is there any way to do it programatically?
I tried using formula: ="'"&H4 but it doesn't do what's expected - the green triangle never appears on the result cell.
I also tried setting cell format to Text, but the green triangle doesn't appear in that case too.
I need the green triangle to appear at the upper left corner, just like at the picture!
If all your number are in a single column, the following code will do it:
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
'declare and set your worksheet, amend as required
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
'get the last row with data on Column A
For i = 1 To LastRow 'loop from row 1 to last
ws.Cells(i, "A").Value = "'" & ws.Cells(i, "A").Value 'add the ' before the number
Next i
End Sub
Change the "A" to whichever column you are using.
Just Select the cells you wish to process and run this short macro:
Sub Textify()
Dim rng As Range, r As Range
Set rng = Selection.Cells.SpecialCells(2, 1)
For Each r In rng
r.Value = "'" & r.Value
Next r
End Sub
Non VBA answer; I'm using Column G in this answer but it depends on where your numbers are. You'll have to change the cell but I think you will be ok with this.
In an empty cell, enter formula: ="'"&G4
Use the fill handle or Ctrl+D to fill it down to the length of Column G's values.
Select the whole of Column G's values and copy them to the clipboard
Select the same range in Column G, right-click, select Paste Special and choose Values
I have tested it now for several times and it worked always
Cells(xx, xx).FormulaR1C1 = "'" & Cells(xx, xx).Value
Same would work for ActiveCell or whatever you like.

Extract Row Locations to Use as Reference

I populated an excel sheet with the locations of blank cells in my sheet using suggestions from this post. So I have a Column A filled with locations in the following format
$X$1 or $X2:$X$4.
What I am trying to do is use those row numbers from the column explain above to populate a separate column. I want to use the row numbers as a reference in what to populate for the column. So a Column B looking something like
=$B$1 or =$B$2:$B$4 (took 1 and 2-4 and used it as row number for reference call)
Both columns are referencing a different sheet so please excuse any column naming.
I'm not sure if this is going to require VBA or if I can get away with just using a formula, I expect VBA due to desired specifics. I've looked at post like this and this. But neither of these fully encompass what I'm looking for. Especially since I want it to express all the contents in a $B$2:$B$4 case.
My intuition on how to solve this problem tells me, parse the string from Column A for the 1st number then check if it's the end of the string. If it is, feed it to the reference that populates Column B, if not then find the 2nd number and go through a loop that populates the cell (would prefer to keep all the content in one cell in this case) with each value for each reverence.
i.e.
=$B2
=$B3
=$B4
My question is how do I go about this? How do I parse the string? How do I generate the loop that will go through the necessary steps? Such as using the number as a reference to pull information from a different column and feed it neatly into yet another column.
If (for example) you have an address of $X2:$X$4 then
Dim rng As Range
Set rng = yourSheetReference.Range("$X2:$X$4")
If you want to map that to the same rows but column B then
Set rng = rng.Entirerow.Columns(2)
will do that. note: it's not so clear from your question whether you're mapping X>>B or B>>X.
Once you have the range you want you can loop over it:
For Each c in rng.Cells
'do something with cell "c"
next c
Something like this should work for you:
Sub Tester()
Dim shtSrc As Worksheet, c As Range, rng As Range, c2, v, sep
Set shtSrc = ThisWorkbook.Worksheets("Sheet1") '<< source data sheet
Set c = ActiveSheet.Range("A2") '<<range addresses start here
'process addresses until ColA is empty
Do While c.Value <> ""
'translate range to (eg) Column X
Set rng = shtSrc.Range(c.Value).EntireRow.Columns(24)
sep = ""
v = ""
'build the value from the range
For Each c2 In rng.Cells
v = v & sep & c2.Value
sep = vbLf
Next c2
c.Offset(0, 1) = v '<< populate in colB
Loop
End Sub
Try this code:
Sub Test()
Dim fRng As Range ' the cell that has the formula
Set fRng = Worksheets("sheet1").Range("A1")
Dim tWS As Worksheet 'the worksheet that has the values you want to get
Set tWS = Worksheets("sheet2")
Dim r As Range
For Each r In Range(fRng.Formula).Rows
'Debug.Print r.Row ' this is the rows numbers
Debug.Print tWS.Cells(r.Row, "N").Value 'N is the column name
Next
End Sub

IF THEN VBA MACRO - Update one column if contents of another = 100%

I have a workbook with "Results" being sheet 3, this being the worksheet I want to use.
I have tried a few formulaes to try and add a macro to do the following:
I have column G with percentages. I then have column I where I would like there to be a result saying TRUE/FALSE where the contents of G are equal to 100%. Column G is formatted to percentage with two decimals.
Some considerations: I have my first row being a Hyperlink to another sheet, then my headings, then the first row of "results". I have 457 rows, if there is a measurement of the range, perhaps it could be on A?
I keep getting this error 9 with my range and have got a bit stuck.
Thanks in advance!
Sub PartialHits1()
Dim rng As Range
Dim lastRow As Long
Dim cell As Range
With Sheet3
lastRow = .Range("G" & .Rows.Count).End(xlUp).Row
Set rng = .Range("G1:G" & lastRow)
For Each cell In rng
If cell.Value = 100
Then
cell.Range("I1:I1").Value = 100
End If
Next
End With
End Sub
(I have hacked this a bit, just was trying to get it to set as 100 instead of the TRUE/FALSE Also was playing around near the Sheet 3 part as I got errors.)
RangeVariable.Range can refer only to a cell within RangeVariable, so you can't refer to column I in this way. Try: .Range("I"&cell.row)=100.
Also your criteria is probably wrong, if you have 100% in a cell it's actual value is 1.
And last question: why do you want to do this with VBA, it would be much more simple with worksheet function =IF(G3=1,100,"")

How to build non-consecutive ranges of rows based on cell contents?

I'm just getting started with VBA for Excel. I used VB and Java in college nearly ten years ago and was competent with it then, but am essentially starting over. (Um, not like riding a bike.)
I am trying to understand the methods to build a range that isn't just declared as A1:J34 or whatever. My Googling is challenged in that when searching for 'range' and terms that indicate what I seek, I get an avalanche of hits far more advanced than what I need, mostly hits that don't even address the basic summary info I need.
So, here's the basics of it:
Excel 2011 on Mac.
The sheet has data from A to M, down to 1309.
It's a repeating pattern of heading rows followed by data rows. Ugh. Seems like the person creating the sheet was more thinking about printing from the sheet than the organisation of the data. I need to clean it and 3 more like it up to use in a pivot table, and it's useless in this silly repeating layout.
Heading rows are as follows:
Last Name, First Name, then 10 date cells.
Data rows under the headings are the names, of course, and then a 1 or 0 for attendance.
Anywhere from 20 to 30 names under each heading. Then it repeats. And the dates change every few sets, picking up where the last set left off.
What I need to do right now:
I'm trying to assemble a range into a range variable by adding all the rows beginning with a specific value (in column A). In my case that value is the string "Last Name", so I can have the range variable holding all the cells in all rows that begin with "Last Name". This will then capture all the cells that need to be in date format. (I'm doing it so I can then make sure the date headings are all actually IN date format - because they are NOT all in date format now, many are just 'General' cells.)
My questions:
When telling a range object what it's range IS, how do you feed it cells/rows/columns that are not just a block defined by start and end cells entered by the person writing the code but based on row criteria? Eg: Create a Range that has rows 1, 34, 70, 93, and 128 from columns A to I based on presence of "First Name" in A.
What are the most common methods to do this?
Which of these is best suited to my need and why?
Here's a working example that demonstrates finding the "Last Name" rows, contructing a range object that includes all those rows, and then iterating through that object to search for non-date values. The code could be speeded up greatly by reading the data range into an array of variants and then searching the array for both the last name rows and the "bad dates" within those rows. This is especially true if you have a very large number of rows to check.
Sub DisjointRng()
Dim checkCol As String, checkPattern As String
Dim dateCols()
Dim lastCell As Range, usedRng As Range, checkRng As Range
Dim cell As Variant
Dim usedRow As Range, resultRng As Range, rngArea As Range
Dim i As Long, j As Long
checkCol = "A" 'column to check for "Last Name"
checkPattern = "Last*"
dateCols = Array(3, 5) 'columns to check for date formatting
With Worksheets("Sheet1")
'find the bottom right corner of data range; we determine the used range
'ourselves since the built-in UsedRange is sometimes out-of-synch
Set lastCell = .Cells(.Cells.Find(What:="*", SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, LookIn:=xlFormulas).Row, _
.Cells.Find(What:="*", SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, LookIn:=xlFormulas).Column)
Set usedRng = .Range("A1:" & lastCell.Address)
'the column of values in which to look for "Last Name"
Set checkRng = .Range(checkCol & "1:" & checkCol & usedRng.Rows.Count)
End With
'step down the column of values to check for last name & add
'add found rows to range object
For Each cell In checkRng
If cell.Value Like checkPattern Then
'create a range object for the row
Set usedRow = Intersect(cell.EntireRow, usedRng)
If resultRng Is Nothing Then
'set the first row with "Last Name"
Set resultRng = usedRow
Else
'add each additional found row to the result range object
Set resultRng = Union(resultRng, usedRow)
End If
End If
Next cell
For Each rngArea In resultRng.Areas
'if found rows are continguous, Excel consolidates them
'into single area, so need to loop through each of the rows in area
For i = 1 To rngArea.Rows.Count
For j = LBound(dateCols) To UBound(dateCols)
If Not IsDate(rngArea.Cells(i, dateCols(j))) Then
'do something
End If
Next j
Next i
Next rngArea
End Sub
You can use the Union operator, like this
Dim r As Range
Set r = Range("A1, A3, A10:A12")
or this
Set r = Union(Range("A1"), Range("A3"), Range("A10:A12"))
You can the iterate this range like this
Dim cl as Range
For Each cl in r.Cells
' code cell cl
Next
or this
Dim ar as Range
For each ar in r.Areas
' code using contiguous range ar
For each cl in ar.Cells
' code using cell cl
Next
Next