Retrieve ranges instead of Strings from array formula result in Excel - 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.

Related

XLookup function won't detect an exact match until I place the cell in edit mode and then exit

I am using the XLookup function in Excel 365. I use a VBA script to create an ID number for a row of data and then I place that ID number into a cell.
If I use the XLookup function with Exact Match to locate that ID number and give me some value in a column next to it, I get an N/A.
However, if I manually locate that exact ID number and place the cell in edit mode and then exit the cell without taking any other step to alter the data in any way, the XLookup function returns the correct result.
If I leave the cell in it's initial state but use the Match Mode "-1" to get an exact match or next smaller, it returns the correct result. I don't want to leave it in that mode, because my final function will be two nested Xlookup functions that check one table for the ID and if not there, looks at an alternate table, so I don't want the first function to return a "next smaller" result.
When I generate the ID number in the VBA script, I generate it as the type "Variant" because the ID number is a decimal, such as 210.1. I then simply set that cell's Value to the variant ID number.
Here is a how I place the data into the cell:
Dim myID As Variant
Dim myCell As Variant
Dim myRange As Range
Set myRange = Range(someNamedRange)
myID = GenerateNextID 'This generates a variant in the form of something like 201.1
For Each myCell In myRange
myCell.Value = myID
myID = myID + 0.1
Next myCell
Excel seems to recognize it as a number; at least I seem to be able to do math functions on the ID number and get correct results.
I tried putting the lookup ID number in quotes to see if looking it up as text instead of a number produced a different result, but it didn't change my result.
Why am I getting this result? Is there a way to resolve it without using a different Match Mode?
You have a floating point precision error. The value in E11 is 211.39999999999998 rather than 211.4. Excel corrects the error when you re-enter the cell's value.
Write the value using a line like:
myID = Round(myID + 0.1, 1)

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

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)))

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.

Applescript get all values of Excel column

I am trying to store all the values of an excel column in an array.
set rangeDate to {value of range "A14:A100"}
repeat with date in rangeDate
if (date as string is equal to "01/01/2001") then
log "It works"
end if
end repeat
In my Excel I do have an exact date of 01/01/2001 formatted in the specified columns. When I remove the range and it is just cell A14 (where the date is) it works. But when I include the range A14:A100 it doesn't work.
I am new to applescript, I guess that it doesn't store the values as array values and instead a string object? Any help would be appreciated
You have 4 issues :
1) value of range should not be between {}, but between ()
2) 'Date' is a reserved word in Applescript, so you should not use it as the variable in the loop. I replaced it with 'myDate'.
3) instead of converting your date to string to compare with "01/01/2001", it is quicker to keep comparing 2 dates, and then, compare with the date "01/01/2001"
4) I think it is a bug (at least with my Excel version), but the rangeDate variable is not a list of dates as expected, but for me a list of list : {{01/02/01},{02/02/01},………} Therefore, each member of 'rangeDate' is not a date, but a list made on one item which is a date ! I am not sure, but it could also be that range definition could be a list of ranges... So I am using item 1 of sub list.
Anyway, script bellow is working :
tell application "Microsoft Excel"
activate
tell active sheet of document 1
set rangeDate to (value of range "A14:A100")
repeat with mydate in rangeDate
set TheDate to item 1 of mydate
if TheDate = (date "lundi 1 janvier 2001 00:00:00") then
log "It works"
end if
end repeat
end tell
end tell
Quickly getting the values of a range of cells is great news! But even better is that you can fill in the values of a range by defining the value of that range. This is SO MUCH FASTER than doing it one cell at a time.
When I tried getting the value of a column (a range of cells), I received a list of lists. Each item in the list had only one value - that is the value of the cell.
To speed up complex operations, once you've got the list of values, take the process out of the "tell Excel" block and let AppleScript do the calculations. Then turn the result back into a list of lists and define the value of the range in Excel.
I had a problem reading ranges with some cells containing #VALUE! (failed formulas). I didn't find a solution on the Internet, so I thought it would be a good idea to share my solution here. Comments & improvement are surely welcome. I'm inclined to think there is a more straightforward solution to the problem than this. :)
Getting all values with value of range can lead to a problem messing up the output of the script. AppleScript doesn't consider a cell's content "#VALUE!" (= missing values) a value since it is, well, missing. Therefore the script doesn't include the cell's content in the list of values. This obviously messes up the cell order in the values list, since it has less items than the actual range has cells. In this situation it is quite impossible to return each value to its original cell in the workbook. Adding ”of ranges” to the code includes all cells with missing values solving the problem.
N.B. The values will be displayed as a one-dimensional array. Handling multi-column ranges requires more work. Nonetheless the missing values are included.
set celVals to (value of ranges of range "A1:A4")
E.g. {2.2.2022, 1.1.2011, missing value, 3.3.2033}
In order to return the values back to the workbook it is required to build back the list of lists. A missing value will be written to its cell as an empty string. Of course the original (failed) formula can be written instead, if needed.
N.B. again. This code applies to one column situation only. A little more is needed to put back a multi-column range. I'm sure you'll manage. :D
set returningCelVals to {}
repeat with i from 1 to count of celVals
set end of returningCelVals to {item i of celVals}
end repeat
set value of range ("A1:A4") to returningCelVals
EDIT: I knew there is a better solution. Here it is:
set celVals to string value of range "A1:A4"
String value gives a two-dimensional array of values and error messages of the range. String value gives also e.g. cell's currency symbols, so it is perhaps not suitable to all situations.

Why is my VBA with relative references returning incorrect references?

I'm trying to develop an Excel application that asks our 4D database for information. To do that, I built a query builder and it works. Now I want to make it more generic so that when I call the query builder, I can pass it a range in which the tables and fields the query is based on are stored. Here is a line where I call the sub and pass it the parameters:
QueryDatabase Worksheets("TablesAndFields").Range("A2:R20"), Worksheets("TablesAndFields"), Worksheets("Import")
Here is the first line in the sub:
Sub QueryDatabase(QuerySpecs As Range, QuerySheet As Worksheet, TargetSheet As Worksheet)
One of the things I need to do is have the VBA figure out the row of the last fields in the various columns. Here is my current code:
LastRowReportTables = QuerySpecs.Offset(0, 3).End(xlDown).Row
LastRowQuery = QuerySpecs.Offset(0, 6).End(xlDown).Row
LastRowSort = QuerySpecs.Offset(0, 14).End(xlDown).Row
This returns the same value for all 3 of them (the second line of the range). It seems to do this regardless of which cells have values in them. For instance, in the case of the range specified above it will return 3. If the range is "A22:R40" it returns 23. What I really need is for it to return the row relative to it's position in the range, but I could fake that if necessary by subtracting the largest multiple of 20 less than the result. (I'm formatting my query builder to fit in 19 rows + a buffer row.) So far, I haven't even been able to get it to return different results for the LastRow variables.
In addition to the Offset method you see above, I've also tried putting it in a With QuerySpecs... End With block. I don't remember the exact result, but I couldn't get that to work either.
The next thing I will need to do is pull values out of the various cells kind of like this:
strStartCell = QuerySpecs.Offset(0, 18).Value
This throws a Run time error 13: Type mismatch. Does anybody have any advice on how to accomplish my goals? Thank-you!
I suggest reading the documentation of the resources you are trying to use.
This can be done by selecting the word which you want to search in the documentation, then press the [F1] key, that will take you to the Microsoft documentation page (i.e. if you select Row and press [F1] will take you to the page Range.Row Property (Excel).
Reading the documentation for Row and Offset will help you understand these Range.Properties and the returns you are getting from your code.
To get the last non-empty row in a range of one column, assuming all data in the range is continuous (i.e. there are no empty cells between rows with content), use these lines:
With WorksheetFunction
LastRowReportTables = .CountA(QuerySpecs.Columns(4))
LastRowQuery = .CountA(QuerySpecs.Columns(7))
LastRowSort = .CountA(QuerySpecs.Columns(15))
End With
Note: If the "QuerySpecs" range is a kind of database, all columns in the fields should have the same number of records, if so, it should be sufficient to obtain the last row for one field only.
To obtain the value of a cell within a range, use this line:
This returns the value of the field (column) 18,row 13 of the QuerySpecs range
strStartCell = QuerySpecs.Cells(13, 18).Value2
The Run-time error 13: Type mismatch is generated when trying to assign an array to a string variable.

Resources