如何在VBA中一次为数组的某些元素分配相同的值?

时间:2016-07-23 15:39:36

标签: arrays excel vba excel-vba assign

我很想知道如何在VBA中同时为数组中的某些元素,行或列分配相同的值?例如,我有一个3x3阵列( MyArray ),就像这样

1   2   3
4   5   6
7   8   9

我需要更改元素的值 MyArray(1,1) MyArray(2,2) MyArray(1,3) MyArray(3,2) 11或第2行13的值。现在,如何通过单个语句执行这些操作?我想的是:

Union(MyArray(1,1), MyArray(2,2), MyArray(1,3), MyArray(3,2)) = 11

MyArray(2,1 : 2,3) = 13

会起作用,但发生错误。我处理这个问题的方法是将数组的数据放在一个未使用的工作表上,更改那里的值,然后将工作表中的值读回到像this method这样的数组中。使用上面的例子,我然后使用以下代码:

Range("A1:C3") = MyArray: Range("A1,B2,C1,B3") = 11: MyArray = Range("A1:C3")

Range("A1:C3") = MyArray: Range("A2:C2") = 13: MyArray = Range("A1:C3")

没有使用工作表或循环还有其他选择吗?

6 个答案:

答案 0 :(得分:3)

VBA原样,不支持这样的东西。只有范围才有可能。

理论上....

您可以使用Evaluate以极其有限的方式执行此类操作。要显示一些仅更改一行/列或二维数组的特定值的示例:

Dim MyArray() As Variant

Public Function MyArrayOut() As Variant
  MyArrayOut = MyArray
End Function

Sub testing()
  'we are using a 5x4 (4 columns / 5 rows) array
  ReDim MyArray(0 To 4, 0 To 3) As Variant 'also 1 to 5 and 1 to 4 are possible.... doesn't matter

  Dim i As Long, j As Long 'input some numbers
  For i = 0 To 4
    For j = 0 To 3
      MyArray(i, j) = i + 5 * j + 1
    Next
  Next

  [A1:D5].Value2 = MyArray
  Stop ' check the sheet -> should be 1 to 20

  'lets add 11 to the second row
  MyArray = Evaluate("=IF({0;1;0;0;0},MyArrayOut()+11,MyArrayOut())")

  [A1:D5].Value2 = MyArray
  Stop ' check again. row 2 should be 13, 18, 23, 28

  'set only 1,1 ; 2,2 ; 4,2 and 5,4 to 0. just keep in mind that using math will fail if ANY value is not numeric (or empty)
  MyArray = Evaluate("=MyArrayOut()*{0,1,1,1;1,0,1,1;1,1,1,1;1,0,1,1;1,1,1,0}")

  [A1:D5].Value2 = MyArray
  Stop 'A1, B2, B4, D5 all should be 0

  'set column 3 to "abc"

  MyArray = Evaluate("=IF({0,0,1,0},""abc"",MyArrayOut())")

  [A1:D5].Value2 = MyArray
  Stop ' check again. column C should be just abc

End Sub

使用有效的IF - 语句可以做很多事情,但是你需要对它进行硬编码,或者是一个非常复杂的算法,这种算法“无法”杀死“不循环”的想法。

此外,这将需要更多的资源,并且总是比直接做得慢。此外,您还需要一个额外的函数来将数组置于evaluate中。

仍有一种情况可供我使用:通过INDEX提取一行/一列。请记住,输出仍然是一个二维数组,可以通过Application.Transpose - Trick来识别。它可用的原因只是事实,没有必要知道数组的大小。这样您就可以通过extrVar = Evaluate("=INDEX(MyArrayOut(),2,)")直接提取。虽然2可以通过不同的变量设置。这样您就不需要ReDim新变量(在这种情况下为extrVar)。它只需要变体。也就是说,使用Application.Index,您也可以在不使用Evaluate的情况下实现此目的。 (这也使用更少的资源并且更快)

我希望您了解 可以使用评估方式完成工作的方式。

尽管如此,VBA本身并没有提供任何方法来通过语法本身一步操作数组的多个内容。 (至少不是你可以用于这种情况的方式)

修改
我几乎忘了:要将变量中的所有值设置为相同的值,像MyArray = Evaluate("=IF(MyArrayOut()="""",5,5)")这样的东西会将所有内容设置为5或MyArray = Evaluate("=IF(MyArrayOut()="""",""abc"",""abc"")")只包含“abc”。要将其清空,您可以使用MyArray = Evaluate("=IF(MyArrayOut()="""","""","""")"),但如果没有ReDim的简单Preserve也可以在一行中执行此操作。

编辑2

对于Range("A1:C3") = MyArray: Range("A1,B2,C1,B3") = 11: MyArray = Range("A1:C3")示例,您可以将其用作自己的函数的基础,如:

Public Sub multiSetSame(ByRef arr As Variant, ByVal val As Variant, ParamArray str() As Variant)
  Dim runner As Variant
  For Each runner In str
    arr(Split(runner, ",")(0), Split(runner, ",")(1)) = val
  Next
End Sub

Sub test()
  Dim MyArray(1 To 3, 1 To 4) As Long

  multiSetSame MyArray, 11, "1,1", "2,2", "1,3", "3,2"

  [A1:D3].Value2 = MyArray
End Sub

但是对于这个例子,虽然我不知道如何评估哪个值要改变,但是在评估它们时,或者如果它们总是相同的话,应该更好地直接改变它,只需编写一个子函数来完成所有值的工作。任何“解决方法”都会减慢整个过程。

如果您还有任何疑问,请询问;)

答案 1 :(得分:1)

VBA没有提供在单个语句中分配多维数组的任何方法,因此您需要声明数组然后分配每个值:

Dim MyArray(2,2)

MyArray(0,0) = 1
MyArray(0,1) = 2
MyArray(0,2) = 3
MyArray(1,0) = 4
MyArray(1,1) = 5
MyArray(1,2) = 6
MyArray(2,0) = 7
MyArray(2,1) = 8
MyArray(2,2) = 9

修改 Slai的早期版本的答案有一个部分解决方案,我已经充实了一个避免使用锯齿状阵列的解决方案。

事实证明,你可以在一个语句中分配一个二维数组,但是数组必须是Variant和1-based,而在Excel中工作。< / p>

您可以分配单个元素,并且可以使用CopyMemory技巧分配整个&#34;列&#34; (但第一列)。如果你需要分配整个&#34;行&#34;或者&#34;列&#34;,那么你可能会因为锯齿状阵列而变得更好。

Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
    lpvDest As Any, _
    lpvSource As Any, _
    ByVal cbCopy As Long)

Sub test()
    'assign a 2D array using the [] shorthand syntax for Eval
    myArray = [{1,2,3;4,5,6;7,8,9}]

    'Change an individual value
    myArray(1, 2) = 11

    'Change the entire first row (this ONLY works for the first row)
    'Using a 1-based array assigned with []/Eval
    colarray = [{14,15,16}]
    CopyMemory myArray(1, 1), colarray(1), (UBound(colarray, 1) - LBound(colarray, 1) + 1) * 16 ' changes a whole "column"

    'Change the entire first row (this ONLY works for the first row)
    'Using a 0-based array assigned with Array()
    colarray = Array(17, 18, 19)
    CopyMemory myArray(1, 1), colarray(LBound(colarray, 1)), (UBound(colarray, 1) - LBound(colarray, 1) + 1) * 16 ' changes a whole "column"

End Sub

答案 2 :(得分:1)

我不完全理解你的问题,所以我专注于问题的标题

  

如何为数组中的某些元素赋予相同的值   曾经在VBA?

我没有使用VBA,所以我不确定,但在Ruby之前我经常使用VbScript,这里有一个如何在VbScript中执行此操作的示例。我从来没有越过一个不在VBA中运行的VbScript代码(反之亦然),但谁知道..

MyArray = array(_
array(1, 2, 3),_
array(4, 5, 6),_
array(7, 8, 9)_
)

Wscript.echo MyArray(0)(0) '1'
Wscript.echo MyArray(2)(2) '9'
编辑:正如Dirk所说:这个解决方案是一个单维数组,其中包含其他数组,但正如我的解决方案显示它通常更通用,它不需要初始化,例如..

答案 3 :(得分:1)

为了避免每次需要以定义的模式分配值时编写for循环,正如我所理解的那样,你必须像这样实现一个Sub:

SetArray(MyArray, patternStr, value)

patternStr是将被解析的字符串,并告诉Sub将值放在何处。一些可能性是:

“r1” - 值将转到第1行的所有列

“c2” - 值将转到第2列中的所有行

“all” - 值转到所有元素

“3,2 1,1” - 值转到(3,2)和(1,1)...如果是3x3矩阵,也许你会想要省略模式中的逗号

您必须使用一些VBA代码实现此功能,至少一次,然后可以多次调用它并且非常容易理解调用代码。

您可能希望以与空格连接的字符串(如“r1 c2”)的方式实现解析器,执行所有单独的分发。

然后,您的示例变为:

SetArray(MyArray, "1,1 2,2 1,3 3,2", 11)

EDIT1

也可以将数组包装在自定义类实例中,以便调用变得更短:

Set aw = New MyArrayWrapper
MyArrayWrapper.array = MyArray

aw.SetArray("1,1 2,2 1,3 3,2", 11)
aw.SetArray("r1 c2", 12)

如果不清楚,我会编辑。希望它有所帮助!

答案 4 :(得分:1)

我从未见过使用矩形数组可以做到这一点的语言,但是对于数组数组来说它可以更容易一些:

MyArray = Array( [{1, 2, 3}], [{4, 5, 6}], [{7, 8, 9}] )
MyArray(0)(1) = 11 
MyArray(1) = [Column(A1:A3)*0+13]   ' changes a whole "row" to 13
MyArray(2) = MyArray(1)             ' copies one row to another

锯齿状数组的缺点(或优点)是每个数组可以具有不同的大小(元素数量),甚至可以是Nothing

答案 5 :(得分:-2)

使用for循环将相同的值分配给多个变量:

myArray[valueOne, valueTwe, valueThree];

for(var i = 0; i < myArray.length; i++) {           
    myArray[i] = true;          
}