将Excel VBA数组传递给父级函数?

我不知道这个问题是否有意义。

示例给定:为单个单元格给出以下值(我们将其称为A1 ): Sub-value #1|Here's another sub-value #2|Yet again, last but not least, sub-value #3我已经知道有人会告诉我,这是一个数据库应该使用的地方(相信我,我的专业是数据库pipe理,我知道,但我需要我的数据在这种方式)。 我的分隔符是| 。 现在说我想创build一个函数,将采取每个子值的LEN()并返回所有子值的AVERAGE() 。 如果我想创build一个函数来做到这一点,我可以使用split() ,取每个值,做一个LEN()并返回AVERAGE()

对于给出的例子,我们利用单元格B1 。 我已经在过去创build了类似的函数,可以通过下面的方法工作(虽然不是这个确切的方法),但是每次都需要分割和连接数组/单元格的值: =ARRAY_AVERAGE(ARRAY_LEN(A1,"|","|"),"|","|")

  • ARRAY_LEN(cell,delimiter[,Optional new_delimiter])
  • ARRAY_AVERAGE(cell,delimiter[,Optional new_delimiter])

但是,我想知道是否可能有一个更dynamic的方法来做到这一点。 基本上,我想用一些自定义的VBA函数split()一个数组,将它传递给父函数,然后用一个将数组合并到一起的函数来包装数组。

这里是单元格函数的运行方式: =ARRAY_AVERAGE(ARRAY_LEN(ARRAY_SPLIT(A1,"|")))

  • ARRAY_SPLIT(cell,delimiter)将拆分数组。
  • ARRAY_LEN(array)将返回ARRAY_LEN(array)中每个子值的长度。
  • ARRAY_AVERAGE(array)将返回ARRAY_AVERAGE(array)中每个子值的平均值。 由于此函数返回多个值的单个值,因此将采用假想的ARRAY_JOIN(array,delimiter) ,它将再次合并数组。

这需要在单元格中添加一个或两个额外的函数,但是也会降低单元格将要转换的迭代次数以及单个单元格值和VBAarrays的迭代次数。

你怎么看? 可能? 可行? 或多或less的代码效率?

以下是在单个单元格上工作的2个示例VBA UDF:input公式为

= AVERAGE(len_text(SPLIT_TEXT(A1, “|”)))

请注意,在这种情况下,您实际上并不需要len_text函数,您可以使用Excel的LEN(),但是您必须inputAVERAGE(..)作为数组公式。

 Option Explicit Public Function Split_Text(theText As Variant, Delimiter As Variant) As Variant Dim var As Variant var = Split(theText, Delimiter) Split_Text = Application.WorksheetFunction.Transpose(var) End Function Public Function Len_Text(something As Variant) As Variant Dim j As Long Dim k As Long Dim var() As Variant If IsObject(something) Then something = something.Value2 End If ReDim var(LBound(something) To UBound(something), LBound(something, 2) To UBound(something, 2)) For j = LBound(something) To UBound(something) For k = LBound(something, 2) To UBound(something, 2) var(j, k) = Len(something(j, k)) Next k Next j Len_Text = var End Function 

现在,这是一个非常粗略的例子,但它应该给你一个如何开始的想法,以及如何定制这种方法来满足你的需求。 假设在一个名为example.txt的文本文件中有以下数据

 Name|Age|DoB|Data1|Data2|Data3 David|25|1987-04-08|100|200|300 John|42|1960-06-21|400|500|600 Sarah|15|1997-02-01|700|800|900 

该文件位于文件夹C:\ Downloads下 。 要使用ADO在VBA中查询,您需要参考Microsoft ActiveX Data Objects 2.X库 ,其中X是您安装的最新版本。 我还参考Microsoft Scripting Library在运行时创build我的Schema.ini文件,以确保正确读取我的数据。 如果没有Schema.ini文件,那么运行数据的风险不会像驱动程序那样被读取。 作为文本的数字可以无情地阅读为空,date通常也返回null。 Schema.ini文件为文本驱动程序提供了数据的精确定义以及如何处理它。 您不必像我所做的那样明确地定义每一列,但至less应该设置Format,ColNameHeader和DateTimeFormat值。

使用示例Schema.ini文件:

 [example.txt] Format=Delimited(|) ColNameHeader=True DateTimeFormat=yyyy-mm-dd Col1=Name Char Col2=Age Integer Col3=DoB Date Col4=Data1 Integer Col5=Data2 Integer Col6=Data3 Integer 

你会注意到文件名被括在第一行的括号中。 这不是可选的,它也允许你为不同的文件定义不同的模式。 正如前面提到的,我在VBA中创build了我的Schema.ini文件,如下所示:

 Sub CreateSchema() Dim fso As New FileSystemObject Dim ts As TextStream Set ts = fso.CreateTextFile(FILE_DIR & "Schema.ini", True) ts.WriteLine "[example.txt]" ts.WriteLine "Format=Delimited(|)" ts.WriteLine "ColNameHeader=True" ts.WriteLine "DateTimeFormat=yyyy-mm-dd" ts.WriteLine "Col1=Name Char" ts.WriteLine "Col2=Age Integer" ts.WriteLine "Col3=DoB Date" ts.WriteLine "Col4=Data1 Integer" ts.WriteLine "Col5=Data2 Integer" ts.WriteLine "Col6=Data3 Integer" Set fso = Nothing Set ts = Nothing End Sub 

你会注意到我使用variablesFILE_DIR,这是我在模块顶部定义的常量。 您的Schema.ini文件–MUST-与您的数据文件位于同一位置。 查询的连接string也使用这个目录,所以我定义了这个常量来确保它们引用同一个地方。 这是我的模块的顶部与FILE_DIR常数连同string和SQL查询:

 Option Explicit Const FILE_DIR = "C:\Downloads\" Const TXT_CONN = "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=" & FILE_DIR & ";Extensions=asc,csv,tab,txt;" Const SQL = "SELECT Name, DoB, ((Data1 + Data2 + Data3)/3) AS [Avg_of_Data]" & _ "FROM example.txt " 

注意TXT_CONN中的部分叫做Dbq 。 这是您的数据文件存储的目录。 您将实际定义您在SQLstring的WHERE子句中使用的特定文件。 SQL常量包含您的查询string。 在这种情况下,我们只需selectName,DoB和平均三个数据值。 所有这一切,你准备好实际执行你的查询:

 Sub QueryText() Dim cn As New ADODB.Connection Dim rs As New ADODB.Recordset Dim i As Integer 'Define/open connection With cn .ConnectionString = TXT_CONN .Open 'Query text file With rs .Open SQL, cn .MoveFirst 'Loop through/print column names to Immediate Window For i = 0 To .Fields.Count - 1 Debug.Print .Fields(i).Name Next i 'Loop through recordset While Not (.EOF Or .BOF) 'Loop through/print each column value to Immediate Window For i = 0 To .Fields.Count - 1 Debug.Print .Fields(i) Next i .MoveNext Wend .Close 'Close recordset End With .Close 'Close connection to file End With Set rs = Nothing Set cn = Nothing End Sub 

我知道我说过,在我上面的评论中这样做非常简单,看起来像很多工作,但我向你保证不是。 你可以只使用QueryText()方法,并得到类似的结果。 然而,我已经包括了其他一切,试图给你一些想法,告诉你如何解决你可能遇到的问题,如果你没有得到预期的结果。

这是我最初学习的指南: http : //msdn.microsoft.com/en-us/library/ms974559.aspx

这里是一个指南,以实际的Excel文件做同样的事情: http : //support.microsoft.com/kb/257819

最后,这里是Schema.ini文件的更多信息: http : //msdn.microsoft.com/en-us/library/windows/desktop/ms709353( v=vs.85) .aspx

希望你能find一种方法来利用你的工作中的所有这些信息! 学习所有这一切的好处是可以使用ADO来查询实际的数据库,如Access,SQL Server和Oracle。 代码几乎与这里打印的内容相同。 只需换出连接string,sqlstring,并忽略有关Schema.ini文件的整个位。