Dynamic insert function - excel

It is a requirement that I use Excel to solve this issue.
In col A I have 0s and 1s with various quantities of 0s between the 1s. Every time a 1 appears I want the difference between two numbers given in two columns next to my binary column. However I wish to get the results from this calculation stated next to the previous 1.
I'd cope with different software, but how do I achieve this with Excel?

=IF(A4=1,OFFSET(B4,MATCH(1,A5:A$1000,0),0)-OFFSET(C4,MATCH(1,A5:A$1000,0),),"")
in D4 and copied down to suit seems to work.
Edit:
=(IF(A$=1, ,"") is as in: IF(logical_test,value_if_true,value_if_false) where value if false is (blank), expressed as "".
The value_if_true is the difference between ColumnB and ColumnC values, each ‘located’ from an OFFSET function as in =OFFSET(reference,rows,cols,height,width).
references are to the appropriate column for the row into which the formula is inserted (ie B4 and C4) from which the values required are ‘south’ by a variable amount.
MATCH, as in =MATCH(lookup_value, lookup_array, [match_type]) is to determine the extent of the offset on a case-by-case basis. In reverse order, the parameters here are match_type = 0 (to require an exact match) and lookup_array is as much of ColumnA as required. Initially chosen as up to Row1000 (by A$1000) but can be extended as far as necessary, subject to row limit for the relevant Excel version.
The first parameter lookup_value) is of course 1 since that is the flag for the rows that contain the values to be subtracted.
Without a $ between A and 5 in the MATCH functions the size of the array automatically decreases (top cell row reference increases) as the formula is copied down, hence finds the next instance (rather than the same one over and over again).

With VBA, I'd first set the formulas to show results in same line as the "ones". (Suppose I used the D column for that.)
= if(A1 = 1; B1 - C1; "")
Then, in VBA window, do the following:
Dim i as integer
Dim Filled as Collection
Set Filled = new Colleciton 'this collection will stored filled lines
'store filled lines
for i = 2 to 1000 'use your table limit
if Sheet1.Cells(i, 4).Value <> "" then 'the 4 is for D column, i for the line
Filled.Add i
end if
next
'now fill the E column with displaced values
for i = 1 to Filled.Count - 1
Sheet1.Cells(Filled(i), 5).Value = Sheet1.Cells(Filled(i+1), 5).Value
next
'please note there's a missing line (the last), it's up to you to decide how to fill it
'sorry I cannot debug this code
I'd associate that to some sheet event or to a button.

Related

How to filter rows based on column value without using FILTER function in excel?

Hi all,
I want to use a formula to filter the rows based on the name chosen in cell D2. From what I searched in google, I only can see people using FILTER function which is very easy. However, FILTER function is only available if we subscribe to 365 office. May I know is there any way to achieve what I want for non 365 office user? Any help will be greatly appreciated!
As far as I understand, hiding the values different than D2 will take care of your need. I am using a similar macro for this task and below I modified it for you to hide the values different than D2. It will start checking values from active cell and loop through until it finds a null value. You can try it and modify it according to your needs. Then you can assign a keyboard shortcut or put a button for it into quick access toolbar, if you are going to use this frequently.
Sub hideByD2()
Dim i, j
i = ActiveCell.Row
j = ActiveCell.Column
k = Cells(2, 4).Value
Do Until Cells(i, j) = ""
If Cells(i, j) <> k Then
Rows(i).Select
Selection.EntireRow.Hidden = True
Else
End If
i = i + 1
Loop
MsgBox "hide process completed successfully"
End Sub
Manage to find the solution.
Formula:
G5 = =IFERROR(INDEX($C$5:$C$14,AGGREGATE(15,6,1/($C$5:$C$14=$D$2)*(ROW($C$5:$C$14)-ROW($C$4)),ROW()-ROW($C$4))),"")
H5 = =IFERROR(INDEX($D$5:$D$14,AGGREGATE(15,6,1/($C$5:$C$14=$D$2)*(ROW($C$5:$C$14)-ROW($C$4)),ROW()-ROW($C$4))),"")
I5 = =IFERROR(INDEX($E$5:$E$14,AGGREGATE(15,6,1/($C$5:$C$14=$D$2)*(ROW($C$5:$C$14)-ROW($C$4)),ROW()-ROW($C$4))),"")
Drag down these 3 formula to the cells below and should work.
Say you have this layout (just the first two columns of your data, moved to a1). Here are two formulas, one that contains FALSES (if you don't care) and one that removes them (because you probably do):
=IF(A4:A13=B1,B4:B13)
=IFERROR(SMALL(IF(A4:A13=B1,B4:B13), ROW(A4:A13)-3), "")
The first one is pretty straightforward. The second one is very similar. It just passes those results to SMALL, which will return the kth smallest value form the array ignoring FALSE values. To get it to evaluate the entire array, you also send it an array of 1 to n, generated with ROW(), and since the range starts in A4 you have to adjust by -3 to make the array start at 1. If you didn't want to have to figure out the offset, you could do this, but we're rapidly losing readability:
=IFERROR(SMALL(IF(A4:A13=B1,B4:B13), ROW(A4:A13)-MIN(ROW(A4:A13))+1), "")
When SMALL gets your list of matching values (with the falses), it will a match for each number in the ROW array you send it, and if it runs out of actual numbers it will start returning errors, which is why you wrap the whole thing in IFERROR.
This will work for numeric values. If you have to support any value, you can still do it:
=IFERROR(
INDEX(
B:B,
SMALL(
IF(A4:A13=B1, ROW(A4:A13)),
ROW(INDIRECT("1:"&ROWS(A4:A13)))
),
0
),
"")
In this case, instead of returning the matching values with SMALL, you will return the matching row numbers, then you will pass those to INDEX, wrapping the whole thing in IFERROR. I used a slightly different method to generate the dynamic indexes:
ROW(INDIRECT("1:"&ROWS(A4:A13)))
which will return an array from 1 to the number of rows in the passed range, but any of the methods to generate the sequence will work.

Feeding pairwise compared values into a large table

I've got a few large (500-1000) datasets in the following format using only the first two rows.
id
value
a-b
number
a-c
number
a-d
number
...
number
b-c
number
b-d
number
and so on
They compare two values and save their difference while skipping previously done comparisons. I want to put them in a table like this:
id
a
b
c
d
e
a
/
number
number
number
number
b
number
/
number
number
number
c
number
number
/
number
number
d
number
number
number
/
number
e
number
number
number
number
/
The lower left half of this table is easily prepared with offset, but how do I feed the values into the upper right half?
Is there a way to mostly automate doing this?
If i understand what you are trying to do, I would suggest to do this in 2 steps:
Set up formulas in the results table to "read" data
Have a macro to "save" data
First fill your results table with this formula (example for cell B2) - keep your offset formula in bottm left half
=IF(B$2=$A3, "\", VLOOKUP(IF(B$2>$A3,$A3&"-"&B$2, B$2&"-"&$A3), $H$3:$I$35, 2, FALSE))
This will give you \ if the row and column id or the same or vlookup on row_id-column_id/column_id-row_id if they are different ensuring they are ordered "low-high" always to give you your mirror across the diagonal. Some might argue the duplication of vlookup is inefficient but more inefficient than an offset and figuring our how to paste one formula into one diagonal and a different one in the other? Who really knows and it is simple and it works IMHO
Next put the data for the current "pass" into the observations table and your values will appear in the table via the VLOOKUP
Finally you need a little macro to replace the formula with the value and run it after every set of results is acquired, so that you don't lose this data when you put a subsequent set of new values in your results table
Option Explicit
Sub replace_formula_with_value()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
Dim r_in As Range
' this is where my input data start
Set r_in = Range(ws.Range("B3"), ws.Range("B3").End(xlDown).End(xlToRight))
Dim r As Range
For Each r In r_in
If Not IsError(r.Value) Then
If r.Value <> "\" Then
'overwrite formula with value by setting value to the evaluation of the formula in the cell
r.Value = r.Value
End If
End If
Next r
End Sub
If you dont want to have a macro then you need to keep ALL data in your observations table and just build it up over time.

Getting Percentage value to show in the correct format

I have two worksheets with data from different sources. I need to copy the data to a single worksheet and remove duplicates. To achieve this objective, I need all the data formatted the same on both worksheets. All of this is already coded except with one column of data I am having issues. These columns contain a representation for percentage. In worksheet A, the value is showing as .4386 which equates to 43.86%. I use this code that converts the value without issue:
Worksheets("Verification").Range("F2:F2000").NumberFormat = "0.00%"
In worksheet B, the same data is shown as 43.86, but the above code changes it to 4386.00%. I also tried changing this line to .NumberFormat = "General\%" and this almost works, but returns a value of 44%. What do I need to add to my code to get this to show 43.86% on worksheet B?
Sorry for the slow reply in comments - I will just submit an answer.
Like Ralph said, it's really better to make sure they are the same number.
43.1 and .431 are not the same number.
For Each c In [A1:A10]
If c.Value < 1 Then
c.Value = c.Value * 100
End If
c.NumberFormat = "0.00\%"
Next c
Results:
You are stating that .4386 on worksheet A is the same data [...] as 43.86 on worksheet B. So, Excel is correct to convert 43.86 to 4386.00%. Maybe you need a conditional formatting: when the number is smaller or equal to 1 then format it "0.00%" and otherwise format it as "0.00""%""".
Yet, I would assume that you'll be running into problems when comparing the data between the sheets with this solution. Hence, I would divide all numbers on sheet B by a 100 first to really make them comparable.
Note, that just by making numbers "look alike" they are not the same. Example: write in cell A1 the value 1000 and in cell B1 also 1000. Then change the number format for A1 to 0 and the number format for B1 to 0, (or to 0. outside the US). A1 will show 1000 while B1 will show 1. If you ask in cell C1 =A1=B1 you will get a TRUE as the answer.

Excel formula for count cells if not null, invalid or 0

I have a formula that I want to use to check if a cell does not have an 'invalid' value in it. However, it is also counting empty cells, and cells that have anything in it that isn't equal to zero:
=COUNTIF(A2:A200,"<>0")
This only checks if the cell does not have a value of "0". What can I add to it so that it will not count empty cells, or cells with values like:
#######
VALUE?
r
etc. All I want is to count how many cells have a number in them that does not equal 0, or an error.
The array formula below first counts all non-0 and non-null values and then subtracts the count of cells that contain errors.
You need to press CTRL + SHIFT + ENTER to properly execute this formula:
=COUNTIFS(A2:A200,"<>0", A2:A200,"<>"&"", A2:A200,"<>"&"NIL") - SUM(IF(ISERROR(A2:A200),1,"")) - SUM(IF(ISNA(A2:A200),1,""))
You can nest up to 7 valid or invalid entries. If you need to have more than that you should perhaps designate a column for your "black list" of entries that you can add to when an occurrence causes a count that you don't believe should be valid. For example:
=IF(ISERR(VLOOKUP(A1,Sheet1!E:E,1,FALSE))=FALSE,1,0)
Where column "E" is your list of values that are considered invalid. Drag this down next to your criteria and sum.
Edit: I wasn't aware of countifs. So you have a couple of solutions, here, depending on your preference.
This formula will count only numbers <> 0, excluding blanks, error messages, etc. BUT it will not exclude the cells that display ###### (but really contain a number) if the reason for that is a column that is too narrow, or a negative date or time value.
=SUMPRODUCT(--ISNUMBER(A2:A200))-COUNTIF(A2:A200,0)
If you really want to avoid counting cells that display ####### when the underlying contents is a number not equal to zero, you will need to use a UDF to act on the Text property of the cell. In addition, narrowing or widening the column to produce that affect will not trigger a calculation event that would update the formula, so you need to somehow do that in order to ensure the formula results are correct.
That is why I added Application.Volatile to the code, but it is still possible to produce a situation where the result of the formula does not agree with the display in the range being checked, at least until the next calculation event takes place.
To enter this User Defined Function (UDF), alt-F11 opens the Visual Basic Editor.
Ensure your project is highlighted in the Project Explorer window.
Then, from the top menu, select Insert/Module and
paste the code below into the window that opens.
To use this User Defined Function (UDF), enter a formula like
=CountNumbersNEZero(A2:A200)
in some cell.
Option Explicit
Function CountNumbersNEZero(rg As Range) As Long
Application.Volatile
Dim C As Range
Dim L As Double
For Each C In rg
If IsNumeric(C.Text) Then
If C.Text <> 0 Then L = L + 1
End If
Next C
CountNumbersNEZero = L
End Function

Excel conditional formatting for the entire row with more than one formula

After 3 hours of searching I still didn't find an answer, here is what I am trying to do:
I am trying to fill with green any row that has WBS in it and with Red any row that has ACT in it and Blue any row that has EPR in it. It works for the first formula then when I try to add the second one every thing get messed up.
what i have understood is that you need to search a keyword in a row and if its found in any cell of that row then color it.
May be we can do it with conditional formatting but i have another idea. We can create a simple search function in Excel VBA. Something like this:
=search_row(A1:F1,"EPR")
The function will return 1 if EPR is found in any cell of specified row. Now if you create two different columns at the end of data columns, name first with WPS and second with EPR and write this function in it. Like
G1 =search_row(A1:F1,"WPS")
H1 =search_row(A1:F1,"EPR")
Drag it to end. Now sort the columns. First for WPS from higher to lower. Then color all rows having 1 in a single select. Similarly do the same with EPR (H1) column.
To use this function you can download the macro file from the following URL:
http://asimishaq.com/myfiles/SearchHighlight.xlsm
Now to run it first of all enable macros, and then re-open your data file and then open this macro file. As long as this macro file is opened you can use this function. Following is the VBA code if you want to create the macro yourself:
Function search_row(sRow As Range, Keyword As String)
Dim i As Integer
Dim Found As Integer
For i = 1 To sRow.Columns.Count
If InStr(1, LCase(sRow.Cells(1, i)), LCase(Keyword)) > 0 Then
search_row = 1
End If
Next
End Function
I had a go at making a function similar to asim-ishaq to determine if your search term exists in the row for fun :) then tried to apply it to highlighting rows, turns out I dont know how to use conditional formatting very well! Figured it out in the end, hopefully I've explained it well enough.
With this you will have to have (one) extra column at the end of your data to contain the result.
It might be possible to not require the extra column by putting the function inside the conditional formatting, however I couldn't get it to work (didn't try very hard). This isn't a great loss as it's much simpler to edit the formula if its on the workbook, instead of having to go through each conditional rule to edit it, should you need to edit it in the future.
To get the formatting to work you will need to create a number of rules (one per keyword)
You want to create a rule of the type shown below, in the formula box you need something along the lines of: =INDIRECT("D" & ROW())=0 where D is the column containing the result of the function below and 0 is the index of the keyword you're highlighting.
In my example, the formula in the D Column is: =SearchRow(An:Cn,"ABS","KBS","JBS") (where n is the row the formula is on)
Set the formatting as desired then press OK, when you return to the rule manager you will need to update the Applies to value, which should be a range that covers all the data you want to highlight. In my example it was $A$1:$C$3
My function below takes 2+ Arguments, The first is the range to search. The second (and any subsequent ones) are search terms.
The function will return a number. -1 for no matches and 0+ for the found search term. The number depends on the position in the arguments.
A1 = "ABS"
B1 = "SBA"
A2 = "SBA"
B2 = "ABS"
A3 = ""
B3 = ""
C1 = "=SearchRow(A1:B1, "ABS", "SBA")"
C2 = "=SearchRow(A2:B2, "ABS", "SBA")"
C3 = "=SearchRow(A3:B3, "ABS", "SBA")"
C1 > 0
C2 > 1
C3 > -1
The function will always return the first result, searching left to right comparing each cell to the Keywords in order. Using my example, if a cell contained "SBA ABS" the result would be 0 (for ABS). I guess your cells will probably only contain one keyword though so this shouldn't be a problem?
Public Function SearchRow(ByVal Row As Range, ParamArray Keyword() As Variant) As Integer
Dim Column As Integer
Dim Value As String
Dim Index As Integer
Dim Result As Integer
For Column = 1 To Row.Columns.Count
Value = LCase(Row.Cells(1, Column))
Result = -1
For Index = LBound(Keyword) To UBound(Keyword)
If InStr(1, Value, LCase(Keyword(Index))) > 0 Then
Result = Index
Exit For
End If
Next Index
If Result > -1 Then
Exit For
End If
Next Column
SearchRow = Result
End Function

Resources