I am fairly new to VBA and need some help in understanding what i am doing wrong.
I have 3 columns. If the value in the first column is zero, i want to set the third column as "N/A" . If the value in first column is not zero, then the third column should be second column / first column.
First column is c4:c6
Second column is d4:d6
Third column is e4:e6
Dim k As Range
For Each k In ws.Range("c4:c6")
If k = 0 Then
Range("e4:e6").Value = "N/A"
Else
Range("e4:e6").Formula = "=d4/c4"
End If
Next
Alternatively
Sub demo()
Range("E4:E6").FormulaR1C1 = _
"=IF(RC[-2]=0,""N/A"",RC[-1]/RC[-2])"
End Sub
Once you've defined the range you're going through, don't reference ranges again inside your loop unless you want that entire loop to be affected during each journey through the loop.
So if k=0, you don't want all of the e values to be "N/A". You only want the one related to the k cell you're looking at that has a zero.
You can use offset for this. First time through the loop, k is cell c4. So, if c4=0 then e4=N/A. e4 is the same as c4.offset(0,2). That is, it's in the same row, but two columns over.
You can also use k as a reference point to help build the formula in e, so that it's not the exact same formula each time.
With that in mind, let's rewrite your code:
Dim k As Range
Dim ws As Worksheet
Set ws = ActiveSheet
For Each k In ws.Range("C4:C6")
If k = 0 Then
k.Offset(0, 2).Value = "N/A"
Else
k.Offset(0, 2).Formula = "=" & k.Offset(0, 1).Address & "/" & k.Address
End If
Next
If you set everything in terms of k, you make sure it's all relevant, line after line.
Lets say we have 5000 rows with random values (blanks, numbers, characters). I need to show type of all the cells in these 5000 rows in a single cell using a formula. Is it actually possible? I've tried to CONCATENATE(CELL("type";array)) and ctrl+shift+enter but it didn't work (it returns the type of the first cell in the array).
If you want to know, this is for finding a cell with text rather than values or blanks in a very big file. Maybe you have a better solution.
Thanks in advance.
UPD: thanks for macros but I can't use them in this workbook, I need a formula-solution.
UPD: I've got how to do it with conditional formatting => new rule => use a formula to determine... => use ISTEXT('first cell of the range') formula
But still, is it possible to create the formula?
The best way to go about this is to use MACROs
Here is my sample code:
Sub Button2_Click()
numRows = 10 ' Number fo rows to loop through, in your case 5000
'loop through each cell located in column 1
'Check its type
'Concatenate each one in 1 cell on the 8th column
For i = 1 To numRows
Sheet1.Cells(1, 8).Value = Sheet1.Cells(1, 8).Value & TypeName(Sheet1.Cells(i, 1).Value) & ","
Next i
End Sub
You can adapt this small user defined function to your needs.
Say we are looking at cells in the first row, from A1 through D1 and we want to concatenate their types into a single cell. Enter the following UDF() in a standard module:
Public Function KonKaType(rIN As Range) As String
Dim r As Range
For Each r In rIN
s = "cell(""type""," & r.Address(0, 0) & ")"
KonKaType = KonKaType & Evaluate(s)
Next r
End Function
and then in the worksheet cell E1 enter:
=KonKaType(A1:D1)
I've got a formula that's been puzzling me for a while - I feel I'm close but the solution is evading me so I'm turning to you wizards. This questions is similar to Excel VLOOKUP and SEARCH combination.
Problem:
I want to look up a value which is a pair of codes separated by a dash, ex.
01-05
A1-B2
AB-90
, within columns A and B and return a result from C.
The issue is that I'm searching in two columns, which may include multiple codes separated by commas:
Col A Col B Col C
01 05, B2 Result1
A1 B2 Result2
AB, AC 90, 91, 92 Result3
I was thinking that a =if(isnumber(search( function would be the key but I can't figure how to have it check the entire column and once found, check the column next to it for the 2nd part of the code.
Ideally, the formula would perform as such, where in the above example, if I were to run this formula on the criteria 01-05 it would return Result1.
Appreciated!
the "formula" approach is, to my knowledge, quite verbose and cumbersome as follows:
=IF(
ISNA(
IFERROR(MATCH(LEFT(D1,SEARCH("-",D1)-1),Codes!$A$1:$A$100,0),
IFERROR(MATCH("*"&LEFT(D1,SEARCH("-",D1)-1)&",*",Codes!$A$1:$A$100,0),
MATCH("*,"&LEFT(D1,SEARCH("-",D1)-1)&"*",Codes!$A$1:$A$100,0)))
*
IFERROR(MATCH(RIGHT(D1,LEN(D1)-SEARCH("-",D1)),Codes!$B$1:$B$100,0),
IFERROR(MATCH("*"&RIGHT(D1,LEN(D1)-SEARCH("-",D1))&",*",Codes!$B$1:$B$100,0),
MATCH("*,"&RIGHT(D1,LEN(D1)-SEARCH("-",D1))&"*",Codes!$B$1:$B$100,0)))
),
"Not Found",
IF(IFERROR(MATCH(LEFT(D1,SEARCH("-",D1)-1),Codes!$A$1:$A$100,0),
IFERROR(MATCH("*"&LEFT(D1,SEARCH("-",D1)-1)&",*",Codes!$A$1:$A$100,0),
MATCH("*,"&LEFT(D1,SEARCH("-",D1)-1)&"*",Codes!$A$1:$A$100,0)))
<>
IFERROR(MATCH(RIGHT(D1,LEN(D1)-SEARCH("-",D1)),Codes!$B$1:$B$100,0),
IFERROR(MATCH("*"&RIGHT(D1,LEN(D1)-SEARCH("-",D1))&",*",Codes!$B$1:$B$100,0),
MATCH("*,"&RIGHT(D1,LEN(D1)-SEARCH("-",D1))&"*",Codes!$B$1:$B$100,0))),
"Different rows",
INDEX(Codes!C:C,IFERROR(MATCH(RIGHT(D1,LEN(D1)-SEARCH("-",D1)),Codes!$B$1:$B$100,0),
IFERROR(MATCH("*"&RIGHT(D1,LEN(D1)-SEARCH("-",D1))&",*",Codes!$B$1:$B$100,0),
MATCH("*,"&RIGHT(D1,LEN(D1)-SEARCH("-",D1))&"*",Codes!$B$1:$B$100,0))))
)
)
where I used a (hopefully) more readable format and assumed:
"Codes" as the sheet name whose columns "A" ("first" code), "B" ("second" code) and "C" ("Results") are placed
codes pairs are to be placed in column "D" of any sheet
formula is to be placed in column "E" adjacent to above mentioned column "D" cells
you may want to consider a "VBA" approach like the following
Sub main()
Dim codesSht As Worksheet
Dim cell As Range, found As Range, codesRng As Range
Dim index1 As Long
Set codesSht = ThisWorkbook.Worksheets("Codes") '<== change "codes" sheet reference as per your needs
Set codesRng = codesSht.Range("A:B").SpecialCells(xlCellTypeConstants, xlTextValues)
With ThisWorkbook.Worksheets("Results") '<== change "Results" sheet reference as per your needs
For Each cell In .Range("D1:D" & .Cells(.Rows.count, "D").End(xlUp).Row).SpecialCells(xlCellTypeConstants, xlTextValues)
Set found = codesRng.Resize(, 1).Find(What:=Split(cell.Value, "-")(0), LookIn:=xlValues, LookAt:=xlPart)
If Not found Is Nothing Then
index1 = found.Row
Set found = codesRng.Offset(, 1).Resize(, 1).Find(What:=Split(cell.Value, "-")(1), LookIn:=xlValues, LookAt:=xlPart)
If Not found Is Nothing Then If found.Row = index1 Then cell.Offset(, 1).Value = codesRng(index1, 3)
End If
Next cell
End With
End Sub
If you put the code you are looking for in Column D, then your formula in Column E, the following formula will accomplish what you are looking for ...
=IF(OR(ISERROR(FIND(LEFT(D2,2),A2)),ISERROR(FIND(RIGHT(D2,2),B2)),LEN(D2)=0),"",C2)
And then fill it down.
The formula searches for the left two characters from the code in Column A. If it's not found, an error is thrown. It also looks for the right two characters from the code in Column B. If it's not found, an error is thrown. If the code you are looking for is blank, no error is thrown, so we need to check that case.
So if there is an error searching for the left part, or an error searching for the right part, or there is no code to look for, return a blank. Otherwise, return the result.
Below are some examples ...
Updated based on comments
On Sheet1, the data looks like this ...
... On Sheet2, we have results like this ...
Where Cell B2 contains this formula (filled down)
{=CONCAT(IF(ISERROR(FIND(LEFT(A2,2),Sheet1!$A$1:$A$3)),"",IF(ISERROR(FIND(RIGHT(A2,2),Sheet1!$B$1:$B$3)),"",IF(LEN(A2)<1,"",Sheet1!$C$1:$C$3))))}
Updated due to version-itis
When all else fails, go to VBA. Attached is an example Function. It gets the same results as shown above. It is invoked with formula in Column B, filled down ...
=FindResult(A2,Sheet1!$A$1:$A$3,Sheet1!$B$1:$B$3,Sheet1!$C$1:$C$3)
Code ...
Function FindResult(inString As String, LeftRange As Range, RightRange As Range, ReturnRange As Range) As String
Dim strArr() As String
Dim myCellLeft As Range, myCellRight
'initial
FindResult = ""
If LeftRange Is Nothing Then GoTo Done:
If RightRange Is Nothing Then GoTo Done:
If ReturnRange Is Nothing Then GoTo Done:
' get the two halfs
strArr = Split(inString, "-")
If UBound(strArr) < 1 Then GoTo Done:
' Search the left range for the left half, the right range for the right half
For Each myCellLeft In LeftRange
If InStr(1, myCellLeft.Value, strArr(0)) > 0 Then
For Each myCellRight In RightRange.Rows(myCellLeft.Row)
If InStr(1, myCellRight.Value, strArr(1)) > 0 Then
FindResult = ReturnRange.Rows(myCellLeft.Row)
Exit For
End If
Next myCellRight
If FindResult <> "" Then Exit For
End If
Next myCellLeft
' clean up
Done:
Erase strArr
Set myCellLeft = Nothing
Set myCellRight = Nothing
End Function
I have a sheet of data and am attempting to check column E10 TO I610 to see if the values in there are more than 11538 and the value in cell J5 is "weekly". If the conditions are true, add the values that are more than 11538 and multiply them by 8.4. How do I go about doing this?
Not too strong with vba so please bear with me.
If schedType = "Weekly" And Range("E10,I610").Value > 11538 Then
Range("H6").Value = "WOW"
ElseIf schedType = "Monthly" Then
Range("H6").Value = "10"
End If
I tried the above way to achieve what I want. Though the code above wont do the exact calculations im after, its just a test. Like I said, I'm attempting to search the range E10 to I610 for any values greater than 11538, then total them and finally find 8.4% of the total.
Its a bit complicated and any assistance is greatly appreciated.
This doesn't work for a lot of reasons, not the least of which is this:
Range("E10,I610").Value
For starters, Range("E10,I610") is a range of only two cells, you guessed it: E10 and I10. Use a colon to create a continuous range object, Range("E10:I610"). Furthermore, the .Value property of a multi-cell range will always, only return the value in the top, left cell.
So, since the value of E10 was not > 11538, the first If statement returns False, and the rest of your code within that block is omitted.
Then, it will continues to fail because you have not structured the code correctly.
There are several ways to work with multiple cells/ranges, I will give you one example which is not very efficient, but it will work for your purposes. I will use a For each loop to iterate over every cell in the Range("E10:I610"), and then check those values against 11538, summing the values greater than 11538.
Sub TotalCells()
Dim schedType as String
Dim rng as Range
Dim cl as Range
Dim myTotal as Double
Set rng = Range("E10:I610")
schedType = Range("J5").Value
'## Check what schedType we are working with:
If schedType = "Weekly" Then
For each cl in rng.Cells
If cl.Value > 11538 Then myTotal = myTotal + cl.Value
Next
'## Multiply the sum by 8.4%
myTotal = 0.084 * myTotal
'## Display the result:
MsgBox "The weekly total is: " & myTotal, vbInformation
ElseIf schedType = "Monthly" Then
' You can put another set of code here for Monthly calculation.
End If
## Print the total on the worksheet, cell H6
Range("H6").Value = myTotal
End Sub
As I said, this is not efficient, but it illustrates a good starting point. You could also use formulas like CountIfs or SumIfs or use the worksheet's AutoFilter method and then sum the visible cells, etc.
In the future, it is always best to post all, or as much of your code as possible, including the declaration of variables, etc., so that we don't have to ask questions like "What type of variable is schedType?"
I wish to categorize my transactions in a way where I can alter the categories on the fly. I think it's easier explained by showing what I have.
I have the following tables
Transactions
A: Date
C: Name
D: Amount
Fast Food List:
L: Name (partial name since going to be doing string search)
I wish to sum the transaction amount based on multiple criteria, such as date and category. Here's a formula that works:
=SUMIFS(D:D,A:A,"*03/2013*",C:C,"*"&L3&"*")
There's one fundamental problem: it only supports ONE item from the Fast Food List. Is there any way I can simply do a text stringth search across the entire Fast Food names?
""&L3&"" to ""&L:L&"" or something?
Here are some things I've tried.
1) Modify the SUMIFS criteria ""&L3&"" with a boolean UDF. The issue I run into here is that I can't figure out how to pass the current Row being looped by SUMIF into the function.
Public Function checkRange(Check As String, R As Range) As Boolean
For Each MyCell In R
If InStr(Check, MyCell.Value) > 0 Then
checkRange = True
End If
Next MyCell
End Function
If I could send Check to this function, well I would be set.
2) Replace the sum_range of the SUMIFS with a UDF that returns the range of rows
Public Function pruneRange(Prune_range As Range, Criteria_range As Range) As Range
Dim Out_R As Range
Dim Str As String
ActiveWorkbook.Sheets("Vancity Trans").Activate
' Loop through the prune_range to make sure it belongs
For Each Cell In Prune_range
' loop through criteria to see if it matches current Cell
For Each MyCell In Criteria_range
If InStr(Cell.Value, MyCell.Value) > 0 Then
' Now append cell to Out_r and exit this foreach
' Str = Str & Cell.Address() & ","
Str = Str & "D" & Cell.Row() & ","
Exit For
End If
Next MyCell
Next Cell
' remove last comma form str
Str = Left(Str, Len(Str) - 1)
' use str to set the range
Set Out_R = Range(Str)
' MsgBox (Str)
Set pruneRange = Out_R
End Function
This works for a regular SUM loop, but for some reason it returns #Value when I try using it in a SUMIF or SUMIFS. Another issue is that even in the SUM loop if use C:C instead of C1:CX where X is however many rows, it crashes excel or takes forever to loop through. I'm guessing it's because excel doesn't know when to stop in a UDF unless I somehow tell it to?
Try this formula
=SUMPRODUCT(SUMIFS(D:D,A:A,"*03/2013*",C:C,"*"&L3:L30&"*"))
By using a range (L3:L30) for the final criterion the SUMIFS formula will generate an "array" (of 28 values - one for each value in L3:L30) ...and SUMPRODUCT is used to sum that array and get the result you want