2016-07-23 39 views
1

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

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

是否有不使用工作表或循环任何其他的选择吗?

+0

有一种方法,但它不适用于bidimentional数组。你需要他们是双向的吗? –

+0

@EduardoPoço你是说二维数组?是的,我需要它像上面的例子(MyArray) –

+0

嗯,无论如何...我想使用字符串和Split(),它返回一个数组,但这个数组不会是2D。怎么样使用数组数组?而不是使用MyArray(i,j)访问,它将是MyArray(i)(j)。 –

回答

3

VBA,因为它不支持这样的事情。只有一个范围才可能。

理论上....

你可以做这样的东西用Evaluate在verly有限的方式。为了展示一些例子改变二维数组只有一个行/列或具体数值,使用有效的IF语句来允许做很多

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 

,但你需要手动编码,或者一个漂亮的复杂的算法“杀死”和“不循环”的想法一样好。

此外,这将花费更多的资源,并且总是比直接做得慢。你还需要一个额外的函数来将数组放入评估中。

还有一种情况可用于我的眼睛:通过INDEX提取一行/列。请记住,输出仍然是一个2d阵列,可以通过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也可以在一行中完成。

EDIT 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 

而且对于这个例子,虽然我不知道是怎么回事评估其中要更改的值,它应该是更好的直接更改它,因为它们被评估,或者如果它们总是相同的,只需编写一个子工作的所有值的工作。任何“解决方法”都会减慢整个过程。

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

+0

看起来像Worksheet.Evaluate比Application.Evaluate快两倍https://fastexcel.wordpress.com/2011/11/02/evaluate-functions-and-formulas-fun-how-to-make -excels-evaluate-method-twice-as-fast/ – Slai

+0

感谢您的精心解答。对此,我真的非常感激。现在,我会接受这个答案。我将来可能会有问题。 (+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答案有一个部分解决方案,我已经充实到避免使用锯齿状数组的解决方案。

事实证明,你可以在一个语句分配一个二维数组,但数组必须可以从1变和,并在Excel中它仅作品

您可以分配单独的元素,并且可以使用CopyMemory技巧分配整个“列”(但是只有第一列)。如果你需要分配整个“行”或“列”,那么用锯齿形阵列可能会更好。

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

我知道文档是新的和所有的,但有没有一个政策,它只是连接出一个答案?我的直觉说遵循与外部链接相同的规则;保持一个链接到源,但引用了相关的通道 – Etheur

+0

我无法找到任何方式在引用的链接来执行任务。你可以发布你的代码吗?谢谢。 –

+0

@Etheur是的,我只是在聊天中澄清。应该在评论中发布。更新了答案 – ThunderFrame

1

我不完全理解你的问题,所以我将把重点放在问题的标题

一个人如何在 在VBA分配相同的值到一个数组中的一些元素一次?

我不使用VBA,所以我不确定,但在Ruby之前我使用了VbScript很多,这里是一个例子如何在VbScript中做到这一点。我从来没有穿过VBA脚本,不能在VBA中运行(反过来我也是这样),但是谁知道..

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' 

编辑:德克备注:该解决方案是一个一维数组与它的其他数组,但我的解决方案,显示它往往更加灵活,它不需要被初始化例如..

+0

首先关闭:另一个1d阵列中的1d阵列不是2d阵列! –

+0

你是对的,我会在编辑 – peter

+0

解释谢谢你的答案。我很感激。 (+1) –

1

要避免每次你需要在一个定义的模式分配值时间写for循环,因为我理解这个问题,你将不得不实行分像这样:

SetArray(MyArray, patternStr, value) 

patternStr是将被解析并告诉Sub在哪里放置该值的字符串。有些possibilites是:

“R1” - 值变为所有列在第1行

“C” - 值变为所有行的第2列

“全” - 值变为所有元素

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

如果不清楚,我会编辑。希望能帮助到你!

+1

这不符合你的想法。您需要将字符串多次拆分以获取所有“地址”,然后循环它们。你至少应该显示执行这项工作的代码,而不是**“你必须用一些VBA代码来实现这个功能”**。对不起,但在我看来,这不是一个答案。这就像提供公式问题的“单元格颜色”一样。 –

+0

这是因为OP知道for-loop答案,只是不想多次使用它。如果不是这种情况,我会发布一些代码。 –

+0

感谢您的回答。我很感激。 (+1) –

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

+0

数组是VBA中的保留字,不能用作变量名称 – ThunderFrame

+0

感谢ThunderFrame,发布前我无法尝试。 – Slai

+0

在Excel 2016中,MyArray ***是一个基于1的2D数组(不参差不齐),所以'myArray(1)(2)= 11'会导致错误,但是myArray(1,2)= 11'将工作,但因为它没有锯齿状,'myArray(1)= [{13,13,13}]'失败,'下标超出范围' – ThunderFrame

相关问题