I have 3 excel formulas that I would like to fill ranges in a spreadsheet with, how can I make sure the cells change with the rows? - vba

=IF(AND(G2<>100,TODAY()>=H2, TODAY()<=I2), E2, " ")
=IF(N2=" ", " ",NETWORKDAYS(H2,TODAY()))
=IF(OR(O2 = " ", O2 <= 0), " ", (O2/N2)*100)
These are the three formulas, I want to make sure that as they are inserted into the worksheet the cell references will still change to match the rows they are on, as they would in a normal spreadsheet. Any advice would be much appreciated! (To clarify, I need to fill the ranges using VBA as the code I'm using clears the worksheet every time it is run.)

you could use FormulaR1C1 property of range object, which uses the "R1C1" notation for range addresses
for instance inserting your first formula in "A1" would be:
Range("A1").FormulaR1C1 = "=IF(AND(RC7<>100,TODAY()>=RC8, TODAY()<=RC9), RC5, "" "")"
where the pure R would assume the current cell row index, while C7 stands for a fixed (not varying with host cell position) 7th column index reference, and so on

If i have interpreted your question correctly, you need something like the below:
Option Explicit
Sub InsertFormula()
Dim i As Long
Dim n As Long
n = Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To n
Cells(i, 1).Formula = "=IF(AND(G2<>100,TODAY()>=H2, TODAY()<=I2), E2, "" "")"
Next i
End Sub
replace the 1 in n=... with whichever column has the most rows of data
replace for i = 1 to whichever row it must begin form
You will notice i have added extra quotations to the end of the formula, this is needed as quotes in a formula in VBA must be enclosed... in more quotes lol
Apply this concept for the other formulas :)

Instead of absolute references like G2 you can use something along
.FormulaR1C1 = "=SUM(RC[-2]:R[5]C[-2])"
where R and C reference the offset from the current cell (positive: right or down, negative: up or left).
Use it in a way similar to this:
Dim c
For Each c In Selection
c.FormulaR1C1 = "=SUM(RC[-2]:R[5]C[-2])"
Next c

Relative References are adjusted when you set the formula to range of cells:
[A1:B2].Formula = "=C$1" ' now the formula in B2 will become "=D$1"
You can also set multiple formulas at once:
Range("K2:M9").Formula = Array("=IF(AND(G2<>100,TODAY()>=H2, TODAY()<=I2), E2, "" "")", _
"=IF(N2="" "", "" "",NETWORKDAYS(H2,TODAY()))", _
"=IF(OR(O2 = "" "", O2 <= 0), "" "", (O2/N2)*100)" )
or if each row has different formula:
[A1:Z3] = [{"=1";"=2";"=3"}]

Related

Excel VBA - Expand range from using just one column to using multiple

I have a working piece of code that looks at a columns value, copies those values, and strips off the 'speed' component string of that value - Turning '200 Mbps' into just '200', etc.
They updated the source data on me and the values are now in three columns - AC, AD, AE instead of just AC now. So values can exist in either column and any row, can be Gbps and Mbps, etc.
At the end of the day, I need the total of the three columns and X number of rows. I have sample data below.
How can I (or can I even) modify this existing code to account for the extra two columns. I am wondering if the Dictionary approach is correct at this point. It was originally added at someone else's suggestion.
Dim Cla As Range
With CreateObject("scripting.dictionary")
For Each Cla In wbFrom.Sheets("Sheet0").Range("AC9", Range("AC" & Rows.Count).End(xlUp))
Cla.Value = Replace(Cla.Value, " Mbps", "")
Cla.Value = Replace(Cla.Value, " Gbps", "")
If Not .exists(Cla.Value) Then
.Add Cla.Value, Nothing
End If
Next Cla
wbTo.Sheets("Sheet1").Range("D13").Resize(.Count).Value = Application.Transpose(.keys)
End With
I don't really understand the If and With loops and how they combine with keys and Transpose like this. ((Thanks TinMan for the info))
I've tried moving this out, but having this outside the loop breaks the code. Is there something in this section that I need to update?
If Not .exists(Cla.Value) Then
.Add Cla.Value, Nothing
End If
Next Cla
Some sample data looks like this: Notice each element is on its own row.
AC AD AE
300
123
72
200
101
The 300 gets paste where it belongs but nothing else adds up or get grabbed I think. Also, when the data looks like THIS, it pastes two values instead of just one:
Notice the 300 and 123 are now on the same line, 300 gets paste into the destination cell and 123 gets paste into two cells below that.
AC AD AE
300 123
72
200
101
Example 1
Use Range.Resize() to extend the number of columns targeted.
For Each Cla In wbFrom.Sheets("Sheet0").Range("AC9", wbFrom.Sheets("Sheet0").Range("AC" & Rows.Count).End(xlUp)).Resize(, 3)
Next
Example 2
Set a helper variable to the Range and use Range.Resize() to extend the number of columns targeted.
Dim Target As Range
With wbFrom.Sheets("Sheet0")
Set Target = .Sheets("Sheet0").Range("AC9", .Range("AC" & Rows.Count).End(xlUp)).Resize(, 3)
Debug.Print Target.Address
End With
Addendum
This will strip away the " Mbps" and " Gbps" as well as insert the sum the a;; the numbers into Range("D13").
With wbFrom.Sheets("Sheet0")
Set Target = .Range("AC9", .Range("AC" & .Rows.Count).End(xlUp)).Resize(, 3)
For Each Cla In Target
Cla.Value = Replace(Cla.Value, " Mbps", "")
Cla.Value = Replace(Cla.Value, " Gbps", "")
Next Cla
.Range("D13").Value = WorksheetFunction.Sum(Target)
End With
I'm not sure that you are describing the problem correctly. Clearly, using the dictionary, get you a list of unique distinct values. But you stated that:
At the end of the day, I need the total of the three columns and X
number of rows.
The existing code leads me to believe that you are going to be doing a count of different speeds...how many 200 Mbps, how many 72 Mpbs, etc.
Either that, or the previous code didn't work as intended.
Assuming that you described the problem correctly and all you want is the total bandwidth then this should do the trick...
Dim LastRow As Long, Value As Long, Sum As Long, Count As Long
' Get the last row, looking at all 3 columns "AC:AE"
LastRow = FindLastRow(wbFrom.Sheets("Sheet0").Range("AC:AE"))
' Iterate through all 3 columns
For Each Cla In wbFrom.Sheets("Sheet0").Range("AC9:AE" & LastRow)
' Use Val() to get just the numeric value and the inline IIF() statment to automatically adjust the speed
Value = Val(Cla.Value) * IIf(InStr(Cla.Value, "Gbps") > 0, 1000, 1)
' Check if there is a Value, if so, Add it to the Sum (and increment the count)
If Value > 0 Then
Sum = Sum + Value
Count = Count + 1
End If
Next Cla
' Write the Sum to the other Workbook (not sure if you need the Count)
wbTo.Sheets("Sheet1").Range("D13") = Sum
And a function to find the last cell in a range (even if the list is filtered)
Public Function FindLastRow(r As Range) As Long
' Works on Filtered Lists/Hidden rows
Const NotFoundResult As Long = 1 ' If all cells are empty (no value, no formula), this value is returned
FindLastRow = r.Worksheet.Evaluate("IFERROR(LARGE(ROW('" & r.Worksheet.Name & "'!" & r.Address & ")*--(NOT(ISBLANK('" & r.Worksheet.Name & "'!" & r.Address & "))),1)," & NotFoundResult & ")")
End Function
From the comments on your question I gather that you want the sum of the original inputs that are contained in columns AC, AD and AE. Such sum you want to store it at cell d13. Since I have limited input, this is the least ugly code I can provide:
nRow1 = ActiveSheet.Cells(ActiveSheet.Rows.Count, "AC").End(xlUp).Row
nRow2 = ActiveSheet.Cells(ActiveSheet.Rows.Count, "AD").End(xlUp).Row
nRow3 = ActiveSheet.Cells(ActiveSheet.Rows.Count, "AE").End(xlUp).Row
nRow = Application.Max(nRow1, nRow2, nRow3)
input_range = Range("AC9:AE" & nRow)
acum = 0
For Each cell In input_range
If Not IsEmpty(cell) Then
temp = Split(cell, " ")
acum = acum + CInt(temp(0))
End If
Next
Range("D13").Value = acum

How can I refer to a data in a different row?

I've got an Excel file with N rows and M columns. Usually data are organized one per row, but it can happens that a data occupy more than a row. In this case how can I express that the second (or next) row has to refer to the first row?
In this example, AP.01 has got 5 rows of description, so how can I say that the other 4 rows refer also to the first code?
EDIT once that I did the association I have to export my Excel file into an Access DB. So I want to see the tables with the correct data.
If I have only one row for the description I wrote this code and it works:
If grid(r, 3).Text.Length > 255 Then
code.Description = grid(r, 3).Text.ToString.Substring(0, 252) + "..."
Else
code.Description = grid(r, 3).Text.ToString
End If
Instead if I have more than one row for the description I wrote this code and it doesn't work:
Do While grid(r, 1).ToString = ""
If grid(r, 1).ToString = "" And grid(r, 3).ToString IsNot Nothing Then
Dim s As String
s = grid(r, 3).ToString
code.Description = grid((r - 1), 3).ToString & s
End If
Loop
If it is a one-off, try the below. This will basically put a formula in every cell that refers to the cell immediately above it:
Select column A (from top until bottom of list (row N)
Press ctrl + g to open the GoTo dialogue
Press Special
Select Blanks from the radio buttons
The above will select all the blank cells in column A. Now enter = and press up arrow. Enter the formula by holding down ctrl while pressing enter. That will enter the same formula in every cell.
Try
Sub Demo()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet3") 'change Sheet3 to your data sheet
With .Range("A:A").SpecialCells(xlCellTypeBlanks)
.FormulaR1C1 = "=R[-1]C"
.Value = .Value
End With
End Sub
From your question I Guess that, you must be define a variable for last column Value. and check the value in respective column, if it is empty then use column value if not empty then take current value as last value.
'Dim LastValue as string
LastValue = sheet("SheetName").cells(i,"Column Name").value
for i = 2 to LastRow '>>>> here i am assume you run code in for loop from row to
'to last count row(LastRow as variable)
'Put your sheet name at "SheetName" and column index (like "A","B","C"...) at "Column Name"
if sheet("SheetName").cells(i,"Column Name").value <>"" then
LastValue = sheet("SheetName").cells(i,"Column Name").value
end if
'(Do your stuff using LastValue , you may generate lastvalue 1, lastvalue2 ..etc)
next'for loop end here

VBA/Excel Find a string in a cell to determine the next cell's value

I need to find a string in a cell, and if that string is found, the cell next to it has to put that string in the next cell. In layman's terms:
IF "Medina" is found in a C3
put "ME" in cell D3;
ELSEIF "Brunswick" is found in C3
put "BR" in D3;
ELSE
put "OTH"in D3;
And this has to go through out the sheet, i.e., C4 & D4, C5 & D5, etc.
Thanks in advance
Sounds like you're looking for the InStr function (see this link: https://msdn.microsoft.com/en-us/library/8460tsh1(v=vs.90).aspx). Basically we want to check if there is a non-zero answer for this, which indicates the string can be found.
So you could do something like below. Note I just put in 40 as your last row arbitrarily, but you can change this as you need.
Dim indStartRow As Integer
Dim indEndRow As Integer
Dim i As Integer
indStartRow = 3
indEndRow = 40
For i = indStartRow To indEndRow
If InStr(Sheets("Sheetname").Range("C" & i).Value, "Medina") > 0 Then
Sheets("Sheetname").Range("D" & i).value = "ME"
ElseIf InStr(Sheets("Sheetname").Range("C" & i).Value, "Brunswick") > 0 Then
Sheets("Sheetname").Range("D" & i).value = "BR"
Else
Sheets("Sheetname").Range("D" & i).value = "OTH"
End If
Next i
I don't think you need VBA for this, Excel Formulas are sufficient. If you want your Selection criteria to be easily extendable, use this:
=IFERROR(INDEX($AB$1:$AB$2,MATCH($A1,$AA$1:$AA$2,0)),"Other")
This Formula is in B1, while A1 contains Medina, Brunswick, etc. AA1:AA2 contain Medina...Brunswick and AAB1:AAB2 ME...BR. It can be moved to a different sheet, and you can extend it by selecting the last cell and inserting whole rows. Later you may want to use named ranges, too.

Merge or concatenate column by adjacent cell value

Is it possible to concatenate cells in a column dependent on values (ID) in another column, and then output as a string (possibly in another sheet as it would be cleaner)?
E.g.
ID Text
1234 a
1234 b
1234 c
4321 a
4321 b
4321 c
4321 d
Output:
1234 a b c
4321 a b c d
Issues:
Column IDs aren't in order (but can be sorted).
Different amounts of each ID
This seemed like a possible VBA solution
from How to merge rows in a column into one cell in excel?
Function JoinXL(arr As Variant, Optional delimiter As String = " ")
'arr must be a one-dimensional array.
JoinXL = Join(arr, delimiter)
End Function
Example usage:
=JoinXL(TRANSPOSE(A1:A4)," ")
So I thought maybe if INDEX and MATCH etc could be used in conjuction with TRANSPOSE it could work. Just not sure how to go about it.
I can have a column of the unique IDs in another sheet.
This solution is nice because it works even in cases where:
The text you're concatenating contains spaces.
You want to use a delimiter other than a space (ie a comma).
First, add "Transpose" to your custom function
Function JoinXL(arr As Variant, Optional delimiter As String = " ")
'arr must be a one-dimensional array.
arr = Application.Transpose(arr)
JoinXL = Join(arr, delimiter)
End Function
For vertical arrays, use this formula (replace comma+space with your delimiter of choice, and "{}" with your garbage characters of choice):
{=SUBSTITUTE(SUBSTITUTE(JoinXL(IF($A$2:$A$31=D3,$B$2:$B$31,"{}"),", "),"{}, ",""),", {}", "")}
For horizontal arrays, use this formula:
{=SUBSTITUTE(SUBSTITUTE(JoinXL(IF(TRANSPOSE($E$19:$AH$19)=D12,TRANSPOSE($E$20:$AH$20),"{}"),", "),"{}, ",""),", {}", "")}
Make sure you enter the formulas as array formulas (Ctrl+Shift+Enter after typing formula in cell).
While no convenient function like your cited example, consider using a dictionary of collections with the ID column as the key. Below macro assumes data begins at A2 (column headers in first row) with result outputting in D and E columns:
Sub TransposeValuesByID()
Dim i As Integer, lastrow As Integer
Dim valDict As Object
Dim innerColl As New Collection
Dim k As Variant, v As Variant
Set valDict = CreateObject("Scripting.Dictionary")
lastrow = Cells(Rows.Count, "A").End(xlUp).Row
For i = 2 To lastrow
If Range("A" & i) = Range("A" & i + 1) Then
innerColl.Add Range("B" & i)
Else
innerColl.Add Range("B" & i)
valDict.Add CStr(Range("A" & i).Value), innerColl
Set innerColl = Nothing
End If
Next i
i = 2
For Each k In valDict.keys
Range("D" & i) = k
For Each v In valDict(k)
Range("E" & i) = Trim(Range("E" & i) & " " & v)
Next v
i = i + 1
Next k
End Sub
Since all you want is space between you can use your code with a couple changes.
If your data is Vertical you need to transpose the array to make it a one dimensional array:
Function JoinXL(arr As Variant, Optional delimiter As String = " ")
'arr must be a one-dimensional array.
arr = Application.Transpose(arr)
JoinXL = Join(arr, delimiter)
End Function
If it is horizontal then use what you have.
The main change is how you call it.
Use the following array formula:
=TRIM(JoinXL(IF($A$2:$A$8=C2,$B$2:$B$8,"")," "))
being an array it needs to be confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode. If done correctly then Excel will put {} around the formula.
The If passes an array of values or blanks depending on if the cell is equal to the criteria.
Put this in the first cell Hit Ctrl-Shift-Enter. Then drag/copy down
I've come up with a work around, that although a little cumbersome works quite nicely.
Table has to be sorted by ID.
In another sheet.
ID: (Column A)
1234
MIN REF: (Column B)
=ADDRESS(MATCH(A2,OCCRANGE,0)+1,6)
MAX REF: (Column C)
=ADDRESS(MATCH(A2,OCCRANGE)+1,6)
RANGE: (Column D)
=CONCATENATE("'OCCS COMBINED'!",B2,":",C2)
STRING: (Column E)
{=IF([#[MIN REF]]=[#[MAX REF]],INDIRECT(CONCATENATE("'OCCS COMBINED'!",[#[MIN REF]])),JoinXL(TRANSPOSE(INDIRECT(D2)), " "))}

Replace Numerical Reference of a formula VBA?

For i = 1 To 5
changeto = CStr(Sheet15.Cells(1, i).Formula)
newindustry = getfirstword(Worksheets("Industry Insert Template").Range("C1"))
'grab the index position of the comma and exclamation mark
intchangeto = InStr(changeto, ",")
finalchangeto = InStr(changeto, "!")
'extract the worksheet substring
finalindustry = Mid(changeto, intchangeto + 1, finalchangeto - intchangeto - 1)
If finalindustry <> "'Multiples & EPS'" And finalindustry <> "Technicals" Then
finalformula = Replace(changeto, finalindustry, newindustry)
Cells(1, i).Formula = finalformula
End If
Next
Currently this is my macro to adjust the worksheet name.
I want to adjust only the numerical reference.
For example:
=VLOOKUP($B1,Industrials!$CA$41:$GG$41,C$8)
I want to be able to change the cells 41 in the vlookup to reflect the correct row. How would I go through all the cells and change the formula to reflect this?
Use Replace:
Example :
Cells (1, i).Formula = Replace(Cells (1, i).Formula, oldRow, NewRow)
If the value of i is related to the row number, you can use it in place of old row etc.