I need to fill a cell with the first non-empty entry in a set of columns (from left to right) in the same row - similar to coalesce() in SQL.
In the following example sheet
---------------------------------------
| | A | B | C | D |
---------------------------------------
| 1 | | x | y | z |
---------------------------------------
| 2 | | | y | |
---------------------------------------
| 3 | | | | z |
---------------------------------------
I want to put a cell function in each cell of row A such that I will get:
---------------------------------------
| | A | B | C | D |
---------------------------------------
| 1 | x | x | y | z |
---------------------------------------
| 2 | y | | y | |
---------------------------------------
| 3 | z | | | z |
---------------------------------------
I know I could do this with a cascade of IF functions, but in my real sheet, I have 30 columns to select from, so I would be happy if there were a simpler way.
=INDEX(B2:D2,MATCH(FALSE,ISBLANK(B2:D2),FALSE))
This is an Array Formula. After entering the formula, press CTRL + Shift + Enter to have Excel evaluate it as an Array Formula. This returns the first nonblank value of the given range of cells. For your example, the formula is entered in the column with the header "a"
A B C D
1 x x y z
2 y y
3 z z
I used:
=IF(ISBLANK(A1),B1,A1)
This tests the if the first field you want to use is blank then use the other. You can use a "nested if" when you have multiple fields.
Or if you want to compare individual cells, you can create a Coalesce function in VBA:
Public Function Coalesce(ParamArray Fields() As Variant) As Variant
Dim v As Variant
For Each v In Fields
If "" & v <> "" Then
Coalesce = v
Exit Function
End If
Next
Coalesce = ""
End Function
And then call it in Excel. In your example the formula in A1 would be:
=Coalesce(B1, C1, D1)
Taking the VBA approach a step further, I've re-written it to allow a combination of both (or either) individual cells and cell ranges:
Public Function Coalesce(ParamArray Cells() As Variant) As Variant
Dim Cell As Variant
Dim SubCell As Variant
For Each Cell In Cells
If VarType(Cell) > vbArray Then
For Each SubCell In Cell
If VarType(SubCell) <> vbEmpty Then
Coalesce = SubCell
Exit Function
End If
Next
Else
If VarType(Cell) <> vbEmpty Then
Coalesce = Cell
Exit Function
End If
End If
Next
Coalesce = ""
End Function
So now in Excel you could use any of the following formulas in A1:
=Coalesce(B1, C1, D1)
=Coalesce(B1, C1:D1)
=Coalesce(B1:C1, D1)
=Coalesce(B1:D1)
If you know there will not be any overlap across columns, or want the overlap, then this is a pretty fast way to solve for a coalesce. The below formula does not apply to your values and columns, but rather to my mock-up so you will need to adjust to make it relevant.
=LEFT(TRIM(CONCATENATE(Q38,R38,S38,T38,U38,V38,W38,X38,Y38)),1)
With the updated IFS function in excel you don't need to nest. You can try something like
Create a blank cell to the right
Then enter.
=IFS(ISBLANK(A1),B1,ISBLANK(A1),C1,ISBLANK(A1),D1)
Highlight column and paste as needed.
If you only want to coalesce to 0, which is a very common use case, you can simply use the SUM() function around a single value. As a convenience that one treats all blanks as zero, and it's extra convenient since it's so short.
Not a generic solution like the other answers, but a helpful shortcut in many cases where that's exactly what you want.
Inside the array enter the variables that are not allowed.
Function Coalesce(ParamArray Fields() As Variant) As Variant
Dim v As Variant
For Each v In Fields
If IsError(Application.Match(v, Array("", " ", 0), False)) Then
Coalesce = v
Exit Function
End If
Next
Coalesce = ""
End Function
Depending on how many cells you want to check, you can chain together multiple ISBLANK checks.
For instance, when checking columns A, B, then C:
=IF(ISBLANK(A1),IF(ISBLANK(B1),C1,B1),A1)
For columns A, B, C, and D:
=IF(ISBLANK(A1),IF(ISBLANK(B1),IF(ISBLANK(C1),D1,C1),B1),A1)
... and so on.
Late to the party and leveraging #AndyMC's answer you can use the following to evaluate vlookups, index(match()) etc. to coalesce your formula statements.
Public Function Coalesce(ParamArray Fields() As Variant) As Variant
Dim v As Variant
For Each v In Fields
If Not IsError(v) Then
Coalesce = v
Exit Function
End If
Next
Coalesce = CVErr(xlErrNA)
End Function
and use it in your Worksheet as followed: =Coalesce(INDEX(SHEET1!$A:$AF,MATCH(Main!$Q36,SHEET1!$I:$I,0),MATCH(Main!D$34,SHEET1!$1:$1,0)),INDEX(SHEET2!A:CR,MATCH(Main!$Q36,SHEET2!$M:$M,0),MATCH("SOMETHING",SHEET2!$1:$1,0)))
For the first statement that does not return #N/A it will return the actual matched value.
Related
I have userform with three fields (COMNAME; COMCOMPANY; TXTVALUE) I tried to pass value from column F to TXTVALUE (textbox) when COMNAME and COMCOMPANY (combo boxes) values are matched row values in columns C and D.
Dim var1 As Integer
Dim var2 As Integer
With Application.WorksheetFunction
var1 = .Match(Me.COMCOMPANY.Value, sheet.range("C7:C10"), 0)
var2 = .Match(Me.COMNAME.Value, sheet.range("D7:D10"), 0)
TXTVALUE.Value = .Index(sheet.range("F7:F10"), var1, var2)
End With
Table looks like this
column C |column D |Column F
PA | CT | 750
RS | HA | 550
PA | CS | 358
When i execute this macro the result in txtvalue is good if is select PA (COMCOMPANY) and CT (COMNAME) - the TXTVALUE is 750 but when I select PA (COMCOMPany) and CS (COMNAME) - the macro return error "Unable to get index property of WorksheetFunction Class".
How to make this code or code like this to work. Thank you
You seem to have some confusion as to how the index function (and possibly the match function) works.
The match function works like this:
MATCH(x, rng, intExact)
means to find the FIRST value x in the range rng and the intExact parameter is defining whether you want the first value less than, exactly equal to, or more than the value x. In your case intExact = 0 and you are searching for values exactly equal. This function will return the row number that the first match is found, beginning with the top of the range.
Assuming that you have displayed cells D6:F9 in your case var1 will return 1 for PA. Var2 will return 1 for CT and 3 for CS. There is no relation between the two variables. Var1 is searching in the D column for your input, and Var2 is searching in the F column for your second input.
The INDEX function works like this:
INDEX(rng, lngRow, lngCol)
means to go in the rng and get the specified row, lngrow and column lngcol starting from the upper left part of the range. In your first case, you select row 1 and column 1 in range F7:F10, which happens to be 750. It is a coincidence that this works, because the match in the E column happens to be in the first row.
In your second case, you try to select the value in row 1 and column 3, but there is only 1 column in your range, so you get an error.
You need to combine this with a lookup column.
column C |column D |Column F | Column Z
PA | CT | 750 | PACT
RS | HA | 550 | RSHA
PA | CS | 358 | PACS
And use the function:
Var1 = .Match(Me.COMCOMPANY.Value & Me.COMNAME.Value, _
sheet.range("Z7:Z10"), 0)
TXTVALUE.Value = .Index(sheet.range("F7:F10"), var1)
You can omit the column parameter of the Index function.
If you are unsure what is happening, it is best to step through the code, noting what the variables are. In the case of the WorksheetFunction function you can then actually put the function in a cell and supply the values that the variables are to troubleshoot the issue.
Use Application.Match function to avoid possible errors being thrown and handle them in the returned variable of Variant type
Dim var1 As Variant
Dim var2 As Variant
With Application
var1 = .Match(Me.COMCOMPANY.Value, sheet.range("C7:C10"), 0)
If Not IsError(var1) Then
var2 = .Match(Me.COMNAME.Value, sheet.range("D7:D10"), 0)
If Not IsError(var2) And var1 = var2 Then Me.TXTVALUE.Value = sheet.range("F7:F10").Rows(var1)
End If
End With
Let us imagine that in ListObject table rows there are some strings or some patterns I would like to find, and if found I would like to format the entire row. My example may be a dump from a recordset via VBA and ACE OLEDB into an excel ListObject table and I would like to format it:
col1 | col2 | col3 | col4
-----+-----------+---------------+----------
1. | empty | AAA |
2. | empty | AAA001value | value
3. | empty | AAA002value | value
4. | Total | desc | value
5. | empty | BBB |
6. | empty | BBB001value | value
7. | empty | BBB002value | value
8. | Total | desc | value
For instance I would like to
Bold the entire row in table where the word Total shows up.
Insert a row above where the ???, three letters string denoting a group shows up.
I would probably manage to come up with the code operating on a standard non ListObject table and add rows for the entire worksheet but I have no idea how to search and change rows in ListObject Table.
In VBA code you can find your string patterns by using like compare operator or using Instr() function like below samples:
If (strCell like "*Total*") Then
' True for case-sensitive strCell string like "Total", "aTotalb" and not for "total"
End If
If (strCell like "*[A-Z][A-Z][A-Z]00[1-2]*") Then
' True for strCell strings with 3 Upper-Case letters And "00" after that and also "1" or "2" after that like "AAA001" or "ZBC002"
If (InStr(1, strCell, "AAA") > 0) Then
' With this InStr() you can remove 'ZBC002'
End If
...
End If
If (strCell like "*[*][*][A-Z][A-Z][A-Z][*][*]*"
' True for strCell like "a**AAA**b" or "asd**ZCB**"
If (InStr(1, strCell, "AAA") > 0) Then
' With this InStr() you can remove "asd**ZCB**"
End If
End If
And for highlighting you can use a code like this:
Sheets(1).Rows(1).Font.Bold = True
I am working with Excel 2013 and I need to fill the range of cells by random numbers. To be exact, Range(B1:B4) fill by numbers in order to value of cells in previous column, I mean in Range(A1:A4). I really have no idea how to fill that using VBA if there is that condition, otherwise it's simple.
Here is a scetch of cells
# | A | B |
----------------------
1 | Yes | 1 |
----------------------
2 | No | 2 |
----------------------
3 | Maybe | 3 |
----------------------
4 | No | 2 |
----------------------
If all you need is random numbers, you don't need VBA. Just set your cell formula equal to:
"=RANDBETWEEN(1,3)"
However, your random numbers will change every time your worksheet is calculated. To avoid this, you can define the following sub and associate it, for example, with an action button:
Sub makeRand()
Dim targetRange As Range
Dim xlCell As Range
Dim upperBound As Integer
Dim lowerBound As Integer
Set targetRange = Range("B1:B4")
upperBound = 3
lowerBound = 1
Randomize
For Each xlCell In targetRange
xlCell.Value = Int((upperBound - lowerBound + 1) * Rnd + lowerBound)
Next xlCell
End Sub
If all you need to do is set this with vba, this should give you values 1, 2, or 3:
Range("B1:B4").Formula = "=RANDBETWEEN(1,3)"
If you only need an Excel formula, you can always just paste =RANDBETWEEN(1,3) into the formula bar.
If you're trying to define column B values based on column A values, just use:
Range("B1:B4").Formula = "=IF(A1 = ""Yes"", 1, IF(""No"", 2, If(""Maybe"", 3, ""ERROR"")))"
If neither of those are what you want, you're going to have to clarify better.
I was hoping to write a Macro that does a very repetitive task for me but entering VBA is harder than expected. I will learn how to program macros for excel when I have some time because it seem extremely useful, but I can't spend 5 to 12 hours this week.
Maybe someone here can help!
I have a few excel files that follow this pattern:
Column C - Column D
--------------------
text | (empty)
number | (empty)
number | (empty)
text | (empty)
number | (empty)
text | (empty)
text | (empty)
number | (empty)
text | (empty)
number | (empty)
Where text and number alternate randomly for a few thousand cells. I need column D to hold, when column C is a number, the difference with previous number, otherwise it must stay blank:
Column C - Column D
--------------------
text | (empty)
3 | (empty)
14 | (=C3-C2) : 11
text | (empty)
16 | (=C5-C3) : 2
text | (empty)
text | (empty)
21 | (=C8-C5) : 5
22 | (=C9-C8) : 1
So the algorithm is:
var previousNumberCell
var i = 1
for all (selected) cells/rows
if (Row(i).column(C) holds number) {
Row(i).column(D).value = "=C"+i+"-"C"+previousNumberCell
previousNumberCell = i;
}
i++
End
I don't care if for the first or last cell it doesn't work.
Thank you so much for the help, or if you can point me to where I can find the answer to this.
EDIT: this is a simplified version of the problem, there are 2 things I don't know how do well with excel macros: select a cell, and tell if cell is a number... for the record, number cells have been converted from text to number format.
Give this a shot:
Sub MyMacro()
Dim rng as Range
Dim cl as Range
Dim lastNum as Range
Set rng = Range(Selection.Address) 'Make sure that your active SELECTION is correct before running the macro'
If Not rng.Columns.Count = 1 Then
MsgBox "Please select only 1 column of data at a time.",vbCritical
Exit SUb
Else:
For each cl in rng
If IsNumeric(cl.Value) Then
If lastNum Is Nothing Then
cl.Offset(0,1).Formula = "=" & cl.Address
Else:
cl.Offset(0,1).Formula = "=" & cl.Address & "-" & lastNum.Address
End If
set lastNum = cl
End If
Next
End If
End Sub
Do you require VBA?
Insert a new Column before column C
Column C with your values becomes column D
You might need columnheaders..
In cell C2 put: =IF(E2=0;0;SUM(E$2:$E2)) this identifies rows with number
In cell E2 put: =IF(ISNUMBER(D2);1;0) this sets order for each row with a number to use next in vlookup
in cell F2 put: =IF(ISNUMBER(D2);ABS(D2-VLOOKUP(MAX($C$1:C1);$C$1:D1;2;0));"")
Autofill columns C, E and F.
In column F you get your results, except first, which is "#VALUE"
Hi you can do this with an if formula and a named formula . if (isnumber ,named formula,0)
named formula (=lookup formula)
I am trying to autofill from a named range to its right neighbour cells. The original range contains combined cells and is 4x4 cells in size. My problem is that the Autofill-method needs a Destination:=Range("...") and does not take something like Destination:=xlSameAreaToTheRight.
The solution would be if I could do something like
Range("origin").AutoFill Destination:=( Range("origin").Area + Rows(4) ), _
Type:=xlFillDefault
So how can I find the rows and cols of a named range in the format A1:D4 and add like 4 cols to it?
Thnks for your support!
I am not sure what you want to get as result, but here are the answers to your particular questions:
You can get the Range as String in the format $A$1:$D$4 using .Address, and to get the amount ofthe columns just add .Columns.Count to the named range. (Using offset is should be no problem to add the amount of the columns to the active cell within your range.)
Dim TestRange As String
Dim AmountColumns As Integer
TestRange = ActiveWorkbook.Sheets(1).Range("Test").Address
AmountColumns = ActiveWorkbook.Sheets(1).Range("Test").Columns.Count
Thanks to stema's answer I found a (quite simple) solution:
To add rows or cols to a named range and use it as AutoFill destination one can simply do
Range("origin").AutoFill Destination:=( Range("origin").Range("A1:H2") ), _
Type:=xlFillDefault
what would autofill four columns and two rows to our "origin":
| 1 | 2 |
| 1,1 | 1,2 | 2,1 | 2,2 |
This leads to:
| 1 | 2 | 3 | 4 |
| 1,1 | 1,2 | 2,1 | 2,2 | 2,7 | 3,12 | 3,54 | 3,96 |
Here's one way to use #stema's suggestion.
Sub namedfill()
Dim locOrig As String
Dim firstCell As String
Dim lastCell As String
'Pluck address of top-left and bottom-right cells from .Address string
locOrig = Range("Origin").Address
firstCell = Left(locOrig, 4)
lastCell = Right(locOrig, 4)
Range("Origin").AutoFill Destination:=Range(firstCell, Range(lastCell).Offset(0, 4)), Type:=xlFillDefault
End Sub