using INDIRECT function with dynamic array formula syntax (Excel O365) - excel

I am using the new dynamic array functions introduced in excel in 2018 (e. g. SEQUENCE, UNIQUE etc. functions).
I have a list of cell references that are that are generated dynamically, and would like to apply the INDIRECT function to these list items. A simplified example:
cell A1: =SEQUENCE(5) (results in rows column A values 1,2,3,4,5 as expected)
cell B1: ="A"&A1# (results in rows column B values A1, A2, A3, A4, A5 as expected)
cell C1: =INDIRECT(B1#) this should give me rows in column C values 1,2,3,4,5, but in fact gives me #VALUE ,#VALUE ,#VALUE ,#VALUE ,#VALUE
So the formula properly recognizes the number of rows of the original dynamic array, but for some reason does not dereference the cells properly. The strings seem to be of the proper format - a simple string function such as LEN also works: setting C1 to =LEN(B1#) results in 5 rows of the value 2.
The syntax per se seems to be OK.. for the special case of =SEQUENCE(1) in cell A1 everything works as intended. I tried the R1C1 reference format also, same result
EDIT
Overall I am trying to achieve the following
import a list form a non-Excel data source list is not a dynamic array, it's just a TSV import. I don't now beforehand how many items are in this list, and it can vary a lot
do several different calculations on values of this list.
so far my approach was to use the COUNT function to determine the number of items in the imported list, and then use that to create the second list using SEQUENCE and INDEX to retrieve values.
the problem arises for some calculations where the data contains references to other rows so I have to use indirect addressing to get at that data

The INDIRECT function cannot accept an array for an argument.
In other words:
=INDIRECT({"a1","a2"}) --> #VALUE! | #VALUE!
So you could, for example, refer to each cell in column B as a single cell:
eg:
C1: =INDIRECT(B1)
and fill down.
Depending on how you are using this, you could also use the INDEX function to return an individual element
To return the third element in the array generated by B1#:
=INDIRECT(INDEX(B1#,3))
EDIT:
After reading your comment, and depending on details you have not shared, you may be able to use a variation of the INDEX function.
For example, to return the contents of A1:A5, based on your SEQUENCE function, you can use:
=INDEX($A:$A, SEQUENCE(5))
but exactly how to apply this to your actual situation depends on the details.

As Rosenfeld points out, INDIRECT() does not accept an array as an input. If you need a function that:
"acts" like INDIRECT()
can accept an array as an input
can return an array as an output
Then we can make our own:
Public Function Indirect_a(rng As Range)
Dim arr, i As Long, j As Long
Dim rngc As Long, rngr As Long
rngc = rng.Columns.Count
rngr = rng.Rows.Count
ReDim arr(1 To rngr, 1 To rngc)
For i = 1 To rngc
For j = 1 To rngr
arr(j, i) = Range(rng(j, i).Value)
Next j
Next i
Indirect_a = arr
End Function
and use it like:
Since it creates a "column-compatible" array, it will spill-down dynamically in Excel 365.It can be used in versions of Excel prior to 365, but it must be array-entered into the block it occupies.

You can use the following formula
=BYROW(B1#,LAMBDA(a,INDIRECT(a)))

Related

Retrieve ranges instead of Strings from array formula result in Excel

Having the nest Formula:
"=IF(Hoja1!$A$4=$A$15:$A$22),IF($B$4=$B$15:$B$22),IF($F$15:$F$22=0,$A$15:$A$22)))"
The resulting array is like so:
{FALSE\FALSE\FALSE\FALSE\FALSE\"Title 6"\FALSE\FALSE}
get an array that is set of booleans, and in this case I get String, but what I want to get is Ranges so I can know the position of that gotten non False result in the resulting array.
I know I could do the same using loops in VBA but my goal was to make it using formulas.
I don't know, maybe there is some built-in function that retrieves ranges that I don't know of.
Alternatively, I thought that having a known Range like $A$15:$F$22 I could get the index numbers of the resulting array that has a non false value and make a Range.Cells(index1, index2) using the 2 indexes of the array.
Also, I thought that using the MATCH function could do it.
For example
Match(<>False, {false\"string value"},0)
And so retrieve the row number that I can then use in a .Cell().
But It does not work. Can I do also a excluding match?
What do you suggest? Any easy/fast solution for this?
Reference to get the date:
Data source
I found a solution that was almost in front of my eyes.
I haven't tried using more than one possible search in an array constant (result of an aray formula).
It works for sure in array constant results that has one non empty/non-false results.
Using MATCH() was the solution.
Using the previous conditional formula I got an array with all results FALSE, except for one. Since the non empty/false result is a String and I want to know what is the range that result is in, using the data source range I extract that cell using the row index taken from the MATCH function, and then with simple VBA I have the range I wanted, like so.
Dim F As String
Dim Res As Integer
Dim R As Range
Set R = Range("$A$15:$A$22")
F = "=MATCH($A$4,IF($A$4=$A$15:$A$22),IF($B$4=$B$15:$B$22),IF($F$15:$F$22=0,$A$15:$A$22)))),0)"
'We get the index number of the desired value in the array that
we got in *IF* parts of the array formula.
Res = Hoja1.Evaluate(F)
'Since The position in the array of the element we want is the same as the row number inside the range the value we searched for is in, we can get that range/cell easily.
Set R = R.Cells(Res, 1) 'Or the column I want.
Debug.Print R.Address
The result is $A$18.
As we expected it matches, since inside the $A$15$:$A$22 the value we looked for is in the 4th row inside that source range.
We can get other columns for that match as well.

How can CountIf accept a structured reference column as criteria VBA-wise?

I'm trying to use structured references to the current columns the same as CountIf does for my UDF function. While
=COUNTIF(Data[Team];Overview[Team])
works, my new function
=CONCATENATEIF(Data[Team];Overview[Team];Data[Data])
doesn't work, since the Overview[Team] criteria Range can't be cast to a single value which is [#This Row].
I tried to change the parameter "criteria" As String as well as different methods. Calling
=CONCATENATEIF(Data[Team];Overview[#Team];Data[Data])
with "#" works as intended. But CountIf can handle [#Team], [Team] and normal ranges like [A1:A4]. So how they do it?
Public Function CONCATENATEIF(check_range As Range, criteria As Range, data_range As Range) As Variant
Dim mydic As Object
Dim L As Long
Set mydic = CreateObject("Scripting.Dictionary")
For L = 1 To check_range.Count
If check_range(L) = criteria Then
mydic(L) = data_range(L)
End If
Next
CONCATENATEIF= Join(mydic.items, ", ")
End Function
What cast does criteria need to work like CountIf's criteria? How can i transform the structured Reference [Team] to [#Team] vba-wise, so it selects the same row, where the Formular is used later.
The table for the problem (sadly can't embed images yet)
COUNTIF works due to inferred reference¹.
If you put a bunch of values in column A and then use =INDEX(A:A, , ) (Index(<column_A>, <all_rows>, <all_columns>)) in an unused column to the right of the data then the result will be from the common row in column A. Since you haven't provided a specific row reference where a single cell reference is expected, the associated (or inferred) row is used. This is why COUNTIF works; it is using an inferred reference from the Overview[Team] column to reference a single cell for criteria; e.g. the cell in Overview[Team] that is on the same row as the formula (also known as Overview[#Team]).
The VBA code is not using an inferred reference. It is referencing the whole column of Overview[Team] where it needs a single cell for criteria (e.g. Overview[#Team]).
You could try to artificially parse the column of criteria down to a single cell with something like Application.Caller.Row or you could just use Overview[#Team] as the criteria like it was intended.
¹ I hope I got that term right. I use it so little that I have a hard time remembering the correct term sometimes.

Put unique distinct values into a list from multiple columns of an array

I'm new to Excel and the journey has been good so far, but I haven't been able to resolve this particular issue by myself. I'm dealing with a table as under:
Essentially, I'm looking to refer to the array of tags in columns from B3:E6, and do the following:
Create a "Unique Tags" column: Create a unique list of "tags" in column H by removing duplicates.
Create a "Maximum Marks" column: Look for each of the unique tags in the array in each row, and return the marks from the marks column in the same row. If the tag appears in multiple rows. the sum of the corresponding marks in these multiple rows should be returned in the maximum marks column in column I. For example, 'EASY' appears in E3 as well as E5. Thus in the 'Unique Tags' List 'EASY' should correspond to Maximum Marks = 4 (2+2).
I could do this manually using formulas such as SUMIF, but I'm looking for a way to automate it since I might have to do this operation for a similar dataset with additional rows & columns. I'm open to VBA solutions as well but would prefer some sort of formula.
I hope I've explained it well enough! Thanks and looking forward to your inputs.
One way to do this is create a function that returns the array of your unique cells and then multiplies them all by matches in your Marks column.
Create the unique cells with this array function. Note this function uses the Dictionary object. In the VB Editor, go to Tools > References, and make sure Microsoft Scripting Runtime is selected.
Public Function UniqueValues(aRange As Range)
Dim DictValues As New Dictionary
Dim cll As Variant
Dim aryResults() As String
For Each cll In aRange
If Not DictValues.Exists(cll.Value) Then DictValues.Add cll.Value, "":
Next
UniqueValues = DictValues.Keys
Set DictValues = Nothing
End Function
Enter in cell H3 and press CTRL SHIFT RETURN (as it's an array function)
=TRANSPOSE(uniquevalues(B3:E6))
and drag down to H15 or beyond
We have to use TRANSPOSE as the array comes out in a row from the function.
Next we need to find the matching cells and multiply. Here in C15 enter the formula below
=INDEX(SUM((($B$3:$E$6=H3)*1)*$F$3:$F$6),1)
Drag this down to H15.

Excel: Count same number values in noncontiguous range

I'm looking for the most elegant way to count the same number values in a noncontiguous range (I'll refer to it as just 'range'). This is the range:
=$C$2:$C$31,$E$2:$E$31,$G$2:$G$31,$I$2:$I$31,$K$2:$K$31,$M$2:$M$31,$O$2:$O$31,$Q$2:$Q$31,$S$2:$S$7
These are the parameters:
The range contains non-adjacent columns.
The columns differ in height.
The cells in the range are either empty or contain integers.
I'm checking for how many cells equal '1', how many equal '2' etc. in the range. (Not in one go, but in seperate formulas).
I've used a named range to reference the range. I'd really like to use this named range in the formula, in one way or another.
I hope I've given you enough info... Thanks in advance!
I agree with Kartik that a VBA solution is required. However the solution offered is a little inefficient in that it loops over every cell in the ranged passed into the function. It also limits the key parameter to a range reference, and can only count up to 32767 matches. Here's an alternative addresses these shortcomings
Function CountIf_N(rng As Range, key As Variant) As Variant
Dim r As Range
Dim count As Long
count = 0
For Each r In rng.Areas
count = count + WorksheetFunction.CountIfs(r, key)
Next
CountIf_N = count
End Function
Note: assumes Excel 07 or later. If using with an ealier version replace CountIfs with CountIf
One approach is to use excel built in function Countif, but it won't work with non-contigous range. The other way (the easy way) will be to use VBA to create your own custom function, and then use it in excel.
I've presented that technique here.
Goto visual basic editor in excel by pressing Alt+F11, in the project window insert a new module and paste the below code:
Function countif_n(rng As Range, key As Range) As Integer
Dim count As Integer
count = 0
For Each cell In rng
If cell.Value = key.Value Then
count = count + 1
End If
Next cell
countif_n = count
End Function
Here rng is your non-contigous range, and key represent the "range"(cell) which contains the value you want to count. For eg., to check for 1 enter 1 in any cell lets suppose "F2", and your non-contigous range is "testrange"
Then use the above function by entering the following in any blank cell:
=countif_n(testrange, F2)
Although COUNTIF can't handle non-contiguous ranges some functions can, for example RANK and COUNT so for a range called Range this formula will give the number of instances of a number in Z2 within Range
=IFERROR(COUNT(Range)-SUM(RANK(Z2,Range,{1,0}))+2,0)
assumes Excel 2007 or later but can be amended to work in earlier versions
This doesn't quite work if there's stuff below S7 that can't be counted, but you may be able to modify. It also doesn't incorporate the named range.
=SUM(IF(MOD(COLUMN(A2:S31),2)=0,IF(A2:S31=2,1,0)))
This example counts the number of 2's.
This needs to be array-entered with ctrl-shift-enter. It's based on the fact that you're counting in every other column, at least in your example. Also, although you mention the columns are different heights, it looks like all except S are the same height. So maybe there's a way to work around that.

How to merge rows in a column into one cell in excel?

E.g
A1:I
A2:am
A3:a
A4:boy
I want to merge them all to a single cell "Iamaboy"
This example shows 4 cells merge into 1 cell however I have many cells (more than 100), I can't type them one by one using A1 & A2 & A3 & A4 what can I do?
If you prefer to do this without VBA, you can try the following:
Have your data in cells A1:A999 (or such)
Set cell B1 to "=A1"
Set cell B2 to "=B1&A2"
Copy cell B2 all the way down to B999 (e.g. by copying B2, selecting cells B3:B99 and pasting)
Cell B999 will now contain the concatenated text string you are looking for.
I present to you my ConcatenateRange VBA function (thanks Jean for the naming advice!) . It will take a range of cells (any dimension, any direction, etc.) and merge them together into a single string. As an optional third parameter, you can add a seperator (like a space, or commas sererated).
In this case, you'd write this to use it:
=ConcatenateRange(A1:A4)
Function ConcatenateRange(ByVal cell_range As range, _
Optional ByVal separator As String) As String
Dim newString As String
Dim cell As Variant
For Each cell in cell_range
If Len(cell) <> 0 Then
newString = newString & (separator & cell)
End if
Next
If Len(newString) <> 0 Then
newString = Right$(newString, (Len(newString) - Len(separator)))
End If
ConcatenateRange = newString
End Function
Inside CONCATENATE you can use TRANSPOSE if you expand it (F9) then remove the surrounding {}brackets like this recommends
=CONCATENATE(TRANSPOSE(B2:B19))
Becomes
=CONCATENATE("Oh ","combining ", "a " ...)
You may need to add your own separator on the end, say create a column C and transpose that column.
=B1&" "
=B2&" "
=B3&" "
In simple cases you can use next method which doesn`t require you to create a function or to copy code to several cells:
In any cell write next code
=Transpose(A1:A9)
Where A1:A9 are cells you would like to merge.
Without leaving the cell press F9
After that, the cell will contain the string:
={A1,A2,A3,A4,A5,A6,A7,A8,A9}
Source: http://www.get-digital-help.com/2011/02/09/concatenate-a-cell-range-without-vba-in-excel/
Update: One part can be ambiguous. Without leaving the cell means having your cell in editor mode. Alternatevly you can press F9 while are in cell editor panel (normaly it can be found above the spreadsheet)
Use VBA's already existing Join function. VBA functions aren't exposed in Excel, so I wrap Join in a user-defined function that exposes its functionality. The simplest form is:
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)," ")
entered as an array formula (using Ctrl-Shift-Enter).
Now, JoinXL accepts only one-dimensional arrays as input. In Excel, ranges return two-dimensional arrays. In the above example, TRANSPOSE converts the 4×1 two-dimensional array into a 4-element one-dimensional array (this is the documented behaviour of TRANSPOSE when it is fed with a single-column two-dimensional array).
For a horizontal range, you would have to do a double TRANSPOSE:
=JoinXL(TRANSPOSE(TRANSPOSE(A1:D1)))
The inner TRANSPOSE converts the 1×4 two-dimensional array into a 4×1 two-dimensional array, which the outer TRANSPOSE then converts into the expected 4-element one-dimensional array.
This usage of TRANSPOSE is a well-known way of converting 2D arrays into 1D arrays in Excel, but it looks terrible. A more elegant solution would be to hide this away in the JoinXL VBA function.
For those who have Excel 2016 (and I suppose next versions), there is now directly the CONCAT function, which will replace the CONCATENATE function.
So the correct way to do it in Excel 2016 is :
=CONCAT(A1:A4)
which will produce :
Iamaboy
For users of olders versions of Excel, the other answers are relevant.
For Excel 2011 on Mac it's different. I did it as a three step process.
Create a column of values in column A.
In column B, to the right of the first cell, create a rule that uses the concatenate function on the column value and ",". For example, assuming A1 is the first row, the formula for B1 is =B1. For the next row to row N, the formula is =Concatenate(",",A2). You end up with:
QA
,Sekuli
,Testing
,Applitools
,Visual Testing
,Test Automation
,Selenium
In column C create a formula that concatenates all previous values. Because it is additive you will get all at the end. The formula for cell C1 is =B1. For all other rows to N, the formula is =Concatenate(C1,B2). And you get:
QA,Sekuli
QA,Sekuli,Testing
QA,Sekuli,Testing,Applitools
QA,Sekuli,Testing,Applitools,Visual Testing
QA,Sekuli,Testing,Applitools,Visual Testing,Test Automation
QA,Sekuli,Testing,Applitools,Visual Testing,Test Automation,Selenium
The last cell of the list will be what you want. This is compatible with Excel on Windows or Mac.
I use the CONCATENATE method to take the values of a column and wrap quotes around them with columns in between in order to quickly populate the WHERE IN () clause of a SQL statement.
I always just type =CONCATENATE("'",B2,"'",",") and then select that and drag it down, which creates =CONCATENATE("'",B3,"'",","), =CONCATENATE("'",B4,"'",","), etc. then highlight that whole column, copy paste to a plain text editor and paste back if needed, thus stripping the row separation. It works, but again, just as a one time deal, this is not a good solution for someone who needs this all the time.
I know this is really a really old question, but I was trying to do the same thing and I stumbled upon a new formula in excel called "TEXTJOIN".
For the question, the following formula solves the problem
=TEXTJOIN("",TRUE,(a1:a4))
The signature of "TEXTJOIN" is explained as TEXTJOIN(delimiter,ignore_empty,text1,[text2],[text3],...)
I needed a general purpose Concatenate With Separator (since I don't have TEXTJOIN) so I wrote this:
Public Function ConcatWS(separator As String, ParamArray cell_range()) As String
'---concatenate with seperator
For n = LBound(cell_range) To UBound(cell_range)
For Each cell In cell_range(n)
If Len(cell) <> 0 Then
ConcatWS = ConcatWS & IIf(ConcatWS <> "", separator, "") & cell
End If
Next
Next n
End Function
Which allows us to go crazy with flexibility in including cell ranges:
=ConcatWS(" ", Fields, E1:G2, L6:M9, O6)
NOTE: "Fields" is a Named Range and the separator may be blank

Resources