Formula to verify row values and retrieve columns - excel

I have a table in Excel as shown in the attached image (Until Column F) and would like to see the final column "Result":
ExcelData:
The Result column should retrieve Column Names where the row values is "IF".
I can use the IF function in Excel, but that would be a long statement that I would have to write to retrieve the final output if there are like 20 columns that I would have to check.

If you are okay with creating a UDF (User Defined Function), then you can essentially create your own worksheet function to do this.
In this function, you will do a quick loop in the input range, and check if any cell in that range is equal to "IF". If so, you will strip the column letter out of the .Address property.
Public Function getIFCols(ByVal rng As Range) As String
Dim retVal As String, cel As Range
For Each cel In rng.Cells
If cel.Value = "IF" Then
retVal = Trim(retVal & " " & colLtr(cel))
End If
Next
getIFCols = Replace(retVal, " ", ", ")
End Function
Private Function colLtr(colRng As Range) As String
colLtr = Split(colRng.Address, "$")(1)
End Function
You will call this function as such:
=getIFCols(B1:F1)
The great thing about this function is that it doesn't require a set amount of columns. So if you need 3 columns checked on one row and 10 on another, it's pretty dynamic.

For Excel Online you can try the texjoin
=TEXTJOIN(",",TRUE,IF(B2:G2="IF",$B$1:$G$1,""))

Related

Convert numbers to non-numbers in Excel

I notice that numeric values like 123456 can be considered as numbers or non-numbers in Excel. Mixing numbers and non-numbers may result in unexpected results of = or XLOOKUP.
For instance, in the following worksheet, the formula of D3 is =ISNUMBER(C3) and the formula of D4 is =ISNUMBER(C4). Their values are not the same. Then =C3=C4 in another cell will return FALSE; =XLOOKUP(C3,C4,C4) will return #N/A.
So one solution to avoid such surprises is that I would like to convert all these numeric values from numbers to non-numbers, before applying formulas on them.
Does anyone know if it is possible to undertake this conversion by manual operations (select the range, then...)?
Does anyone know how to achieve this conversion by a subroutine in VBA (select the range, then run the VBA subroutine, then the selected range will be converted)?
If you firstly write numbers in a range, let us say "C:C", formatted as General, any such a cell will return TRUE when you try =ISNUMBER(C4).
If you preliminary format the range as Text and after that write a number, this will be seen by Excel as a String (non-numbers, as you say...) and =ISNUMBER(C4) will return False.
Now, if you will try formatting the range as Text after writing the numbers these cells will not be changed in a way to make =ISNUMBER(C4) returning FALSE. In order to do that, you can use TextToColumns, as in the next example:
Private Sub testTextToCol()
Dim sh As Worksheet, rng As Range
Set sh = ActiveSheet
Set rng = sh.Range("C:C")
rng.TextToColumns Destination:=rng, FieldInfo:=Array(1, 2)
End Sub
It will make the existing =ISNUMBER(C4), initially returning TRUE, to return FALSE...
Of course you cannot compare apples to oranges, thus strings are not comparable to integers/longs/numbers. Make sure that all you compare are apples.
In a routine this would be s.th. like
Option Explicit
Sub changeFormat():
' Declare variables
Dim Number As Variant
Dim check As Boolean
'Converts the format of cells D3 and D4 to "Text"
Range("D3:D4").NumberFormat = "#"
'Assign cell to be evaluated
Number = Range("D3")
Debug.Print Number 'Prints '123'
check = WorksheetFunction.IsText(Trim(Sheets("Tabelle1").Cells(4, 3)))
Debug.Print check 'Prints True
'Converts the format of cells D3 and D4 to "Numbers"
Range("D3:D4").NumberFormat = "0.00"
'Compare Cells
If Range("D3").NumberFormat = Range("D4").NumberFormat Then Range("D5").Value = "Same Format"
End Sub
Also see the docs

How can I make this function iterate through both of ranges?

So I have been trying to build a function to increment the cells using a For loop
=IFERROR(IF(AND(N$10>=PivotTabel!$L5;N$10<=PivotTabel!$M5);"1";"0.5");"X") &
IF(AND(N$10>=Sheet1!$L125;N$10<=Sheet1!$M125);"yes";"n") &
IF(AND(N$10>=Sheet1!$L126;N$10<=Sheet1!$M126);"yes";"n") &
IF(AND(N$10>=Sheet1!$L127;N$10<=Sheet1!$M127);"yes";"n")
This formula is to check if a date is between a date period ( for example to check if 01.01.2020 is between 02.08.2019 and 20.02.2020 if True in the first case i print "1" and other cases i print "yes").
As you can see above is the formula that i need to write in VBA to iterate through a specified number of cells which are $L125:$L139 for one column, and the other column $M125:$M139
I wrote the function below but it gives me #VALUE!
Function Date_Increment(MaxCount As Integer) As String
' This function does incrementation of date ranges
Dim Result As String
Static i As Integer
i = 125
Dim cell As Range
'MaxCount is the number times to iterate just to test
'My idea is to use the function COUNT("L125:L139") to return the number of cells that are not blank
For Each i In MaxCount
' this is the cell i want to run formula in
Range("GUI!D12").Formula = "=IFERROR(IF(AND(D$10>=PivotTabel!$L5;D$10<=PivotTabel!$M5);""1"";""0.5"");""X"") & IF(AND(D$10>=Sheet1!$L" & i & ";D$10<=Sheet1!$M" & i & ");""yes"";""n"")"
Result = Range("GUI!D12").Formula
i = i + 1
Next cell
'Next j
DateRange_Increment
End Function
My question is : How can I make this function iterate through both of ranges? Or If there is a simple solutions using only formulas i would also use it. In addition please check if i have errors in the function.
Any help i would really appreciate it.

How do I update formula range in a macro?

I have a formula in A1 that is fed with data from a different workbook. It's only a reference to this other workbook, there aren't any calculations.
This second workbook is updated on a monthly basis so the cell I'm interested in referring to is offset one cell to the right each month.
How can I write a macro that tells my current formula in A1 to use the same formula but moving it one place to the right? It'd be something like: [Book1]Sheet1!C15 to [Book1]Sheet1!D15. Thanks!
Use Range.Precedents to get the cells a particular Range depends on.
'get the cell:
Dim theCell As Range
Set theCell = ActiveSheet.Range("A1")
'get its first "precedent" Range:
Dim precedent As Range
Set precedent = theCell.Precedents(1)
'rewrite the formula, offsetting the precedent by 1 column:
theCell.Formula = "=" & precedent.Offset(ColumnOffset:=1).Address(External:=True)
Obviously this makes a lot of assumptions and will need to be adjusted to your specific needs, but you don't need to parse any formulas to offset its precedent cells when you're looking at a formula that's simply =SomeCellAddress.
First put this small UDF in a standard module:
Public Function NextCellOver(s As String) As String
arr = Split(s, "!")
addy = Range(arr(1)).Offset(0, 1).Address
NextCellOver = arr(0) & "!" & addy
End Function
It will accept a string that ends with something like !Z99 and return a string ending with !AA99. (Basically one column to the right.)
Then enter:
Sub marine()
With Range("A1")
.Formula = NextCellOver(.Formula)
End With
Application.CalculateFullRebuild
End Sub
To apply this to the cell in question.

How to do SUMIFS text match with multiple criteria from another column?

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

Searching multiple worksheets from an excel 2007 function

I have worksheets A, B and C. Worksheet A contains a column with dates. B and C each contain two columns: one with a date and one with a value. For example
worksheet A:
A B
1 2001-01-01 ---
2 2001-01-02 ---
worksheet B:
A B
1 2001-01-01 1
worksheet C:
A B
1 2001-01-02 2
I'd like to have a function =Search(W, date) that when run from worksheet A returns a value assigned to date in worksheet W. For example Search(C, "2001-01-02")=2.
This is an abstract version of searching for currency rates at given dates: multiple worksheets contain rates for currencies, so when we search, we know what worksheet (currency) to pick.
How to define such a function? I tried passing parameters to a custom macro, but excel keeps giving me cryptic errors. It's seems easy to use a macro that uses the selected cell as a source, but a function would be better.
EDIT: my attempt, doesn't work
Function FindRate()
Dim FindString As String
Dim Rate As String
Dim Src As Range
Dim Found As Boolean
MsgBox sheet_name
Rate = "Not found "
Set Src = Application.ActiveCell
FindString = "2006-12-19"
Sheets("cur CHF").Activate
Found = False
For Each c In [A1:C2000]
If c.Value = FindString Then
Rate = c.Offset(0, 1).Value
Found = True
Exit For
End If
Next
MsgBox Rate
'FindRate = Rate
End Function
Function Rate(cname As String)
Dim sheet_name As String
Dim c2s As New Collection
c2s.Add "cur worksheet name", "cur"
sheet_name = c2s.Item(cname)
Call FindRate(sheet_name)
End Function
What you are really doing is a lookup. There is a VLOOKUP function built into Excel that will do exactly what you want. The syntax is
VLOOKUP(lookup_value, table_array, col_index_num, [range_lookup])
This will look up the value lookup_value in the table table_array. It will find an exact match in the first column if range_lookup is false, otherwise it will find the closest value (faster, but data must be sorted).
It will return the value in the col_index_num column.
In your case, if you want the value from sheet B corresponding to "2012-01-01", you would do
=VLOOKUP("2012-01-01", Sheet2!A2:B1000, 2, false)
You may have to mess around with converting the date string into a date value, etc. If you had added the values on Sheet2 as dates, you would want to use
=VLOOKUP(DATEVALUE("2012-01-01"), Sheet2!A2:B1000, 2, false)
since that function correctly converts the string "2012-01-01" to something Excel recognizes as a DATE.
Now if you don't know a priori which sheet you will need to access (because that's a variable), you may have to write yourself a VBA function:
Function myLookup(value, curr)
Dim dval As Long, luTable As Range, s As Worksheet, c As Range
' if user types date as string, convert it to date first...
If VarType(value) = vbString Then
dval = DateValue(value) ' this doesn't work if dval hasn't been declared as `long`!
Else
dval = value
End If
' see if `curr` is the name of a defined range; if so, use it
On Error GoTo notArange
' if the next line doesn't generate an error, then the named range exists:
Set luTable = Range(curr)
' so let's use it...
GoTo evaluateFunction
notArange:
' If we got here, "curr" wasn't the name of a range... it must be the name of a sheet
' first, tell VBA that we're done handling the last error:
Resume here
here:
On Error GoTo noSheet
Set s = ActiveWorkbook.Sheets(curr)
Dim firstCell As Range, lastCell As Range
Set firstCell = s.Range("a1")
Set lastCell = s.Range("b1").End(xlDown) ' assuming data in columns A and B, and contiguous
Set luTable = Range(firstCell, lastCell)
evaluateFunction:
myLookup = Application.WorksheetFunction.VLookup(dval, luTable, 2, False)
Exit Function
noSheet:
' get here if currency not found as either sheet or range --> return an error message
myLookup = curr & " not found!"
End Function
This has been tested on a small sample, and it worked. A few things to note:
You can name the range where the conversion is kept ("euro", "dinar", "yen", ...) instead of keeping each on a separate sheet. You can then pass the name of the range (make it the same as the name of the currency for convenience) as a parameter to your function, and access it with Range(currency). This also gets around the problem of "hard-wiring" the size of the range
The function will check for the existence of a named range, and use it if it exists. If it doesn't, it will look for a sheet with the correct name
If you use an "invalid currency name", this will be reflected in the return value (so myLookup("01-01-2012", "Florins") will return "Florins not found!"
Instead of assuming a lookup table of a certain length, I determine the size of the table dynamically, using the End(xlDown) construct
I allow date to be passed in as a String, or as a DATEVALUE. The function notices the string and converts it
Right now I am setting the range_lookup parameter to False. This means that there must be an exact match, and values that are not present will generate errors. If you prefer to return "the best match", then you set the parameter to True. Now the risk is that you will return garbage when the date requested is outside of your limits. You could solve this by setting the first and last value of the exchange rate column to "no valid data". When the lookup function returns, it will show this value.
This is a simple FindCell function I use a lot is simply extends Excels search function but from what you have got should suit fine. It returns a range however it is simple enough to get the value from the return range. I use it as follows (with comments added for your sake):
Function FindCell(SearchRange As Range, SearchText As Variant, OffsetDown As Integer, OffsetRight As Integer) As Range
'Do a normal search range call using the passed in range and text.
'First try looking formula
Set FindCell = SearchRange.Find(What:=SearchText, LookAt:=xlWhole, LookIn:=xlFormulas, _
MatchCase:=True, SearchOrder:=xlByRows).Offset(OffsetDown, OffsetRight)
'If nothing is found then look in values
If FindCell Is Nothing Then
Set FindCell = SearchRange.Find(What:=SearchText, LookAt:=xlWhole, LookIn:=xlValue, _
MatchCase:=True, SearchOrder:=xlByRows).Offset(OffsetDown, OffsetRight)
End If
End Function
This can be used a function for the rate (you could of course combine the two functions but I use FindCell for many applications so have kept it seperate):
Function GetRate(sWorksheetName As String, theDate As Date) As Double
Dim returnRange As Range
'Call the FindCell function specifying the range to search (column A), and the date and then offset one cell to the right for the value
Set returnRange = FindCell(ThisWorkbook.Worksheets(sWorksheetName).Columns("A:A"), sDate, 0, 1)
'Check if we've found something. If its Nothing then we haven't
If Not returnRange Is Nothing Then GetRate = returnRange.Value
End Function
You can test it in a Sub like so:
Sub Test()
MsgBox "Value is " & GetRate("Sheet2", "2001-01-01")
End Sub
By accepting the GetRate as a date type it shouldn't matter what format the date is in the worksheet.

Resources