有没有办法在没有VBA的Excel中连接两个数组?

时间:2017-09-12 18:29:15

标签: arrays excel excel-formula concatenation

我正在尝试创建一个返回两个不同长度数组的串联的公式。我需要这个串联作为另一个公式的一部分,如果可能的话我想避免“帮助”行。

请参阅下面的示例数据。

enter image description here

目标是让输出为{10;11;12;13;20;21;22}。当然,这可以很容易地硬编码到公式中,但这些值是动态的,因此不是一种选择。

我尝试了以下内容:

{A1:A4;B1:B3}

但这显然不是有效的Excel语法。

有解决方案吗?

9 个答案:

答案 0 :(得分:4)

Excel无法以您描述的方式直接连接数组(即简单地将它们背靠背组合)。但是,如果不使用辅助函数,则存在一个(复杂的)解决方案。

基本上您需要做的是将{10;11;12;13}转换为{10;11;12;13;0;0;0}并将{20;21;22}转换为{0;0;0;0;20;21;22}。获得该结果后,可以将两个长度为7的数组一起添加,以获得所需的结果。

那么如何在数组的开头或结尾添加零?

答案是巧妙地使用矩阵乘法(MMULT Excel内置函数)。

我不会解释为什么这是结果的所有数学因为我认为它太过于偏离编程,但最终下面的矩阵乘法方程给出了你想要的结果:

[1 0 0 0]   [10]   [10]
[0 1 0 0] * [11] = [11]
[0 0 1 0]   [12]   [12]
[0 0 0 1]   [13]   [13]
[0 0 0 0]          [ 0]
[0 0 0 0]          [ 0]
[0 0 0 0]          [ 0]

或者在Excel中,您可以输入以获得结果:(我添加了换行符以提高可读性。)

= MMULT({1,0,0,0;
         0,1,0,0;
         0,0,1,0;
         0,0,0,1;
         0,0,0,0;
         0,0,0,0;
         0,0,0,0},A1:A4)

如果您在单元格中突出显示此公式并按F9键,您应该会注意到它会为您提供所需的{10;11;12;13;0;0;0}结果。

同样,以下公式将为您提供所需的{0;0;0;0;20;21;22}

结果
= MMULT({0,0,0;
         0,0,0;
         0,0,0;
         0,0,0;
         1,0,0;
         0,1,0;
         0,0,1},B1:B3)

将这两个值相加在一起将得到所需的最终结果{10;11;12;13;20;21;22}

注意

此时,此可能足以满足您的需求。但是,对于大型数组,将这些1和0的矩阵硬编码到公式中可能过于繁琐。如果是这种情况,请继续阅读,告诉您如何自动生成这些1和0的矩阵,而不是硬编码。

我们如何自动生成上面显示的1&0和0的大型矩阵?

再一次没有解释很多"为什么"因为我认为讨论会变得太长而且偏离主题,这里是一个生成上面1和0的第一个矩阵的公式:

= (ROW(INDIRECT(ADDRESS(1,1)&":"&ADDRESS(ROWS(A1:A4)+ROWS(B1:B3),1)))
  =COLUMN(INDIRECT(ADDRESS(1,1)&":"&ADDRESS(1,ROWS(A1:A4)))))+0

1&0和2的第二矩阵的公式略有不同:

= (ROW(INDIRECT(ADDRESS(1,1)&":"&ADDRESS(ROWS(A1:A4)+ROWS(B1:B3),1)))
  =(COLUMN(INDIRECT(ADDRESS(1,1)&":"&ADDRESS(1,ROWS(B1:B3))))+ROWS(A1:A4)))+0

最终公式

连接两个(垂直)数组的最终公式如下:(添加了几个换行符以提高可读性)

= MMULT(
    (ROW(INDIRECT(ADDRESS(1,1)&":"&ADDRESS(ROWS(A1:A4)+ROWS(B1:B3),1)))
    =COLUMN(INDIRECT(ADDRESS(1,1)&":"&ADDRESS(1,ROWS(A1:A4)))))+0,
    A1:A4)
 +MMULT(
    (ROW(INDIRECT(ADDRESS(1,1)&":"&ADDRESS(ROWS(A1:A4)+ROWS(B1:B3),1)))
    =(COLUMN(INDIRECT(ADDRESS(1,1)&":"&ADDRESS(1,ROWS(B1:B3))))+ROWS(A1:A4)))+0,
    B1:B3)

最终说明/思考

使用此公式的优点是它允许在不使用VBA的情况下连接数组。缺点是这种用于连接数组的方法仅适用于数字,而不适用于文本。 (这是因为MMULT需要数字。)

答案 1 :(得分:3)

如果您拥有动态数组版本的 Excel、Excel 365 或 2019 年之后的版本,则答案要短得多。如果一列名为 Foo,另一列名为 Bar,则您的公式为:

=SMALL((Foo,Bar),SEQUENCE(ROWS(Foo)+ROWS(Bar)))

此处,SEQUENCE 返回一个数组,其序列从 1 到 Foo 和 Bar 的总行数。 SMALL 为序列中的每个值返回适当的值。然后 Excel 将结果“溢出”到公式下方所需的行数中。

答案 2 :(得分:1)

对于数字数组:

=SMALL((A1:A4,B1:B4),ROW(INDIRECT("1:"&COUNT(A1:A4)+COUNT(B1:B4))))

=SMALL((A1:A4,B1:B4),ROW(INDEX(A:A,1):INDEX(A:A,COUNT(A1:A4)+COUNT(B1:B4))))

答案 3 :(得分:1)

感谢此线程的先前贡献者,我了解到附加数组数据的解决方案在于使用相等长度的数组。

此解决方案适用于文本和数字,但我使用了文本。

在下面的示例中,我加入了 3 个命名范围(分别为 Named Range1、Range2 和 Range3)。我还确保每个范围内的项目顺序保持不变,尽管这可能没有必要。

为了便于阅读,我使用了 Excel 365 的 LET 函数。该范围只是溢出到输入公式的空白列中。

=LET(
TotRows,ROWS(Range1)+ROWS(Range2)+ROWS(Range3),
A,INDEX(Range1,SEQUENCE(TotRows)),
B,INDEX(Range2,SEQUENCE(TotRows,,-ROWS(Range1)+1,1)),
C,INDEX(Range3,SEQUENCE(TotRows,,-ROWS(Range1)-ROWS(Range2)+1,1)),
X,IFERROR(IFERROR(A,B),C),
X)

更多的范围可以很容易地添加为 D、E、F 等......然后在最后包装更多的 IFERROR() 函数。我返回 X,但你当然可以将它包装到 UNIQUE() 中以提取一个唯一的列表,或者你可以 SORT() 它等等。

当您稍微修改它以组合溢出范围时,事情会变得更加有趣和动态(即,命名范围被对溢出范围的引用替换(请参阅附加的屏幕截图以获取使用的示例和公式)

我上面的输入和结果如下:Screenshots of Data and Output with Formula

祝你好运。

答案 4 :(得分:0)

对于它的价值,这里有一个解决方案,它连接两个任意两个垂直数组(没有数据必须是数字的限制)。

以下是数组公式:(例如,合并A1:A4C7:C9

= INDEX(CHOOSE({1,2},A1:A4,C7:C9),
  N(IF({1},ROW(INDEX($A:$A,1):INDEX($A:$A,ROWS(A1:A4)+ROWS(C7:C9)))-IF(
  ROW(INDEX($A:$A,1):INDEX($A:$A,ROWS(A1:A4)+ROWS(C7:C9)))<=ROWS(A1:A4),0,ROWS(A1:A4)))),
  N(IF({1},2-(ROW(INDEX($A:$A,1):INDEX($A:$A,ROWS(A1:A4)+ROWS(C7:C9)))<=ROWS(A1:A4)))))

以下是组合两个水平数组的数组公式(例如A1:D1C3:E3

= INDEX(CHOOSE({1;2},A1:D1,C3:E3),
  N(IF({1},2-(COLUMN(INDEX($1:$1,1):INDEX($1:$1,COLUMNS(A1:D1)+COLUMNS(C3:E3)))
  <=COLUMNS(A1:D1)))),N(IF({1},COLUMN(INDEX($1:$1,1):INDEX($1:$1,COLUMNS(A1:D1)+
  COLUMNS(C3:E3)))-IF(COLUMN(INDEX($1:$1,1):INDEX($1:$1,COLUMNS(A1:D1)+COLUMNS(C3:E3)))
  <=COLUMNS(A1:D1),0,COLUMNS(A1:D1)))))

答案 5 :(得分:0)

TLDR和自我指导-Here's the example workbook

是的,有一种方法可以在2016年办公室之前连接数组。我知道上面的ImaginaryHuman已经回答了这个问题,但是我有另一种方法,它返回一个数组,并且读取起来更容易一些(IMHO)。我将介绍公式的演变,以便您可以找到适合您的用例的公式。我用粗体突出显示了用例,以便您可以快速找到您的用例。我知道这很冗长,但是我是那种喜欢知道解决方案如何工作的人,所以我将尽力给您同样的礼貌。

该公式依赖于嵌套的IF语句和INDEX / CHOOSE结构。它适用于范围,命名范围甚至表列。我所有的示例都显示了四个范围,因此显示了三个IF语句,但是如果您关心这么多嵌套的IF语句,则可以将其扩展到(我认为)64个范围。

对于这些示例,数据范围是A3:B6A9:B11A14:B19A22:B32。所得的数组公式位于E3:E26范围内,并以Ctrl+Shift+Enter结尾以使其成为数组公式。您的数据可以随心所欲-您不受这些范围的束缚-只需适当地替换范围即可。

如果您的数据在连续范围中:

=IF(ROW()-ROW(E3)<ROWS(A3:A6),INDEX(A3:B6,ROW()-ROW(E3)+1,COLUMN()-COLUMN(E3)+1),
IF(ROW()-ROW(E3)<ROWS(A3:A6)+ROWS(A9:A11),INDEX(A9:B11,ROW()-ROW(E3)-ROWS(A9:A11),COLUMN()-COLUMN(E3)+1),
IF(ROW()-ROW(E3)<ROWS(A3:A6)+ROWS(A9:A11)+ROWS(A14:A19),INDEX(A14:B19,ROW()-ROW(E3)-ROWS(A3:A6)-ROWS(A9:A11)+1,COLUMN()-COLUMN(E3)+1),
INDEX(A22:B32,ROW()-ROW(E3)-ROWS(A3:A6)-ROWS(A9:A11)-ROWS(A14:A19)+1,COLUMN()-COLUMN(E3)+1))))

工作原理:

  1. IF语句通过从单元格E3的输出范围的顶部减去当前行,并将其与第一个输入中的单元格数进行比较,确保我们处于第一个范围A3:B6的范围。
  2. 给定从单元格A3:B6计算出的行和列偏移量,INDEX语句从E3的第一个输入范围中选择一个项目。
  3. 如果该行不在第一个范围内,则继续执行下一个IF语句,该语句重复该过程,但将数组的当前行与前两个范围的长度进行比较。对于任何其他嵌套的IF语句,将重复此过程。

如果您的数据不在连续范围内,则您需要一列来显示数据的原始范围,或者两者兼有:

=IF(ROW()-ROW(E3)<ROWS(A3:A6),INDEX(CHOOSE({1,2,3},{1},A3:A6,B3:B6),ROW()-ROW(E3)+1,COLUMN()-COLUMN(E3)+1),
IF(ROW()-ROW(E3)<ROWS(A3:A6)+ROWS(A9:A11),INDEX(CHOOSE({1,2,3},{2},A9:A11,B9:B11),ROW()-ROW(E3)-ROWS(A3:A6)+1,COLUMN()-COLUMN(E3)+1),
IF(ROW()-ROW(E3)<ROWS(A3:A6)+ROWS(A9:A11)+ROWS(A14:A19),INDEX(CHOOSE({1,2,3},{3},A14:A19,B14:B19),ROW()-ROW(E3)-ROWS(A3:A6)-ROWS(A9:A11)+1,COLUMN()-COLUMN(E3)+1),
INDEX(CHOOSE({1,2,3},{4},A22:A32,B22:B32),ROW()-ROW(E3)-ROWS(A3:A6)-ROWS(A9:A11)-ROWS(A14:A19)+1,COLUMN()-COLUMN(E3)+1))))

工作原理:

  1. IFINDEX语句的所有原理均与上面相同。
  2. 添加了CHOOSE语句,该语句使您可以选择不连续的数据列或具有每个范围所需标识符的静态数组。在这种情况下,我选择了数字(1,2,3,4)。
  3. CHOOSE语句可以具有任意多的列-只需将第一个参数更改为{1,2,3,4}的四列,然后将第四列添加为最后一个参数。对任何后续列(即{1,2,3,4,5}进行相同操作,然后将第五列添加为最后一个参数。

如果您使用的是水平数据而不是垂直数据,则可以使用TRANSPOSE使上一个示例生效。只需将TRANSPOSE函数嵌套在CHOOSE函数中,如下所示:

CHOOSE({1,2,3},{1},TRANSPOSE(A3:C3),TRANSPOSE(A4:C4)

您可以使用命名的范围或表显着清理公式。此示例基于前一个示例,该示例允许数据不在连续范围内,并提供了一个标识符列,显示数据来自何处:

=IF(ROW()-ROW(E3)<ROWS(Table1),INDEX(CHOOSE({1,2,3},{1},Table1[Column1],Table1[Column2]),ROW()-ROW(E3)+1,COLUMN()-COLUMN(E3)+1),
IF(ROW()-ROW(E3)<ROWS(Table1)+ROWS(Table2),INDEX(CHOOSE({1,2,3},{2},Table2[Column1],Table2[Column2]),ROW()-ROW(E3)-ROWS(Table1)+1,COLUMN()-COLUMN(E3)+1),
IF(ROW()-ROW(E3)<ROWS(Table1)+ROWS(Table2)+ROWS(Table3),INDEX(CHOOSE({1,2,3},{3},Table3[Column1],Table3[Column2]),ROW()-ROW(E3)-ROWS(Table1)-ROWS(Table2)+1,COLUMN()-COLUMN(E3)+1),
INDEX(CHOOSE({1,2,3},{4},Table4[Column1],Table4[Column2]),ROW()-ROW(E3)-ROWS(Table1)-ROWS(Table2)-ROWS(Table3)+1,COLUMN()-COLUMN(E3)+1))))

如果这还不够,您可以通过创建一些命名值来做更多的整理工作以提高可读性。可以做的第一件事是定义我们从哪一行开始从每个表中获取数据。在此示例中,我将它们命名为Table2_ULTable3_ULTable4_UL。他们在名称管理器中的代码公式如下:

  1. Table2_UL=ROWS(Table1)
  2. Table3_UL=Table2_UL+ROWS(Table2)
  3. Table4_UL=Table3_UL+ROWS(Table3)

如您所见,每个组件都基于最后一个组件,因此其输出是动态的。现在,我们有了一个更具可读性的公式:

=IF(ROW()-ROW(E3)<Table2_UL,INDEX(CHOOSE({1,2,3},{1},Table1[Column1],Table1[Column2]),ROW()-ROW(E3)+1,COLUMN()-COLUMN(E3)+1),
IF(ROW()-ROW(E3)<Table3_UL,INDEX(CHOOSE({1,2,3},{2},Table2[Column1],Table2[Column2]),ROW()-ROW(E3)-Table2_UL+1,COLUMN()-COLUMN(E3)+1),
IF(ROW()-ROW(E3)<Table4_UL,INDEX(CHOOSE({1,2,3},{3},Table3[Column1],Table3[Column2]),ROW()-ROW(E3)-Table3_UL+1,COLUMN()-COLUMN(E3)+1),
INDEX(CHOOSE({1,2,3},{4},Table4[Column1],Table4[Column2]),ROW()-ROW(E3)-Table4_UL+1,COLUMN()-COLUMN(E3)+1))))

但这对我来说还不够。我想摆脱对ROW()COLUMN()的所有讨厌的引用。为此,我们可以在名称管理器中再定义两个值来为我们跟踪当前行和列:

  1. Output_CC=COLUMN()-COLUMN(Sheet1!E3)+1
  2. Output_CR=ROW()-ROW(Sheet1!E3)+1

最后,我们得到了一些易于理解的内容:

=IF(Output_CR-1<Table2_UL,INDEX(CHOOSE({1,2,3},{1},Table1[Column1],Table1[Column2]),Output_CR,Output_CC),
IF(Output_CR-1<Table3_UL,INDEX(CHOOSE({1,2,3},{2},Table2[Column1],Table2[Column2]),Output_CR-Table2_UL,Output_CC),
IF(Output_CR-1<Table4_UL,INDEX(CHOOSE({1,2,3},{3},Table3[Column1],Table3[Column2]),Output_CR-Table3_UL,Output_CC),
INDEX(CHOOSE({1,2,3},{4},Table4[Column1],Table4[Column2]),Output_CR-Table4_UL,Output_CC))))

如果我们真的想一路走下去,我们也可以将CHOOSE语句转换为命名值。只需对名称管理器中的每个输入表执行以下操作,确保为每个输入表赋予唯一的名称:

Table1_IN=CHOOSE({1,2,3},{1},Table1[Column1],Table1[Column2])

现在我们可以很容易地阅读公式:

=IF(Output_CR-1<Table2_UL,INDEX(Table1_IN,Output_CR,Output_CC),
IF(Output_CR-1<Table3_UL,INDEX(Table2_IN,Output_CR-Table2_UL,Output_CC),
IF(Output_CR-1<Table4_UL,INDEX(Table3_IN,Output_CR-Table3_UL,Output_CC),
INDEX(Table4_IN,Output_CR-Table4_UL,Output_CC))))

但是,这还不够,因为您无法打开过滤器并对数组A-Z进行排序。您收到错误“您无法更改数组的一部分”。不过,有一种解决方法!它需要一个帮助器列并复制您的输出。可以将其复制到普通的旧范围或表中。要允许您对数据进行排序和过滤,请在数组输出的左侧(在这种情况下,从D3开始)创建一个帮助器列。如果您的数据不需要排序(像所有文本列一样),请创建静态编号(1、2、3、4等)。在此示例中,列G包含要排名的数字。如果确实需要排名,请在D3中输入以下公式并将其向下拖动:

=RANK.EQ(G3,G$3:G$26,0)+COUNTIF(G$3:G3,G3)-1

如果需要提升排名,请将最终参数更改为1。现在,如果对数据进行了排名,则您将出现无序的排名;如果不是,则您将获得一个不可排序的数组,并在其旁边加上一个静态数字。现在,我们将数据复制到范围或表中。在I的{​​{1}}列中,创建与数据集一样长的静态编号(即1、2、3、4)。现在,在单元格I3的右侧,输入一个指向源数组中数据的J3

VLOOKUP

向下拖动公式,然后向右拖动。现在,您可以对数据进行排序和过滤,就像在正常范围内一样。

答案 6 :(得分:0)

要在MMULT中获得数字数组的1和0矩阵(如@ ImaginaryHuman072889所述),请使用以下代码:

{= --(((ROW()<=TRANSPOSE(ROW()))*(ROW()<10))*(TRANSPOSE(ROW($1:$11))<=ROW($1:$11)))}

这是一个很好的解决方案,但是在使用矩阵功能时不需要使用ADDRESS或INDIRECT函数,这将使用较少的代码。两个简单的步骤:

  1. 调整(ROW()<10)运算符或值以在矩阵中所需行的上方和/或下方创建0。
  2. 调整行范围“ 11”以对应于两个数组合并后将存在的总行数。

答案 7 :(得分:0)

对于两个垂直的单列列表/数组,请尝试以下操作:

https://drive.google.com/file/d/1x1SX4VGM7rQPMkJMVAtvDzpmYG2XHZWx/view?usp=sharing

(在Excel中打开,而不在Google表格中打开)

我已经使用LET函数,因为它被认为是最佳实践,并且更容易了解发生了什么。

使用的其他动态数组函数有:SORT,UNIQUE,FILTER,SEQUENCE,CHOOSE和INDEX

希望这会有所帮助,

ST

公式也如下:

注意 List1 List2 是命名范围; K3#和M3#是溢出数组(请参阅最后两个公式)

所有唯一:

=SORT(UNIQUE(LET(
RightList,FILTER(List2, NOT(ISBLANK(List2))),
LeftList,FILTER(List1, NOT(ISBLANK(List1))),
BothListCount,SEQUENCE(ROWS(LeftList)+ROWS(RightList),1,1),
INDEX(CHOOSE({1,2},LeftList,RightList),
N(IF({1},BothListCount IF(BothListCount<=ROWS(LeftList),0,ROWS(LeftList)))),
N(IF({1},2-(BothListCount<=ROWS(LeftList)))))
)))

在两个列表中:

=SORT(UNIQUE(FILTER(List1,COUNTIF(List2,List1)>0)))

不在两个列表中

=SORT(UNIQUE(LET(
RightList, M3#,
LeftList,K3#,
BothListCount,SEQUENCE(ROWS(LeftList)+ROWS(RightList),1,1),
INDEX(CHOOSE({1,2},LeftList,RightList),
N(IF({1},BothListCount-IF(BothListCount<=ROWS(LeftList),0,ROWS(LeftList)))),
N(IF({1},2-(BothListCount<=ROWS(LeftList)))))
)))

在列表1中,但不在列表2中-在上式中也称为K3#

=SORT(UNIQUE(FILTER(List1,(COUNTIF(List2,List1)=0)*NOT(ISBLANK(List1)))))

在列表2中,但不在列表1中-在上式中也称为M3#

=SORT(UNIQUE(FILTER(List2,(COUNTIF(List1,List2)=0)*NOT(ISBLANK(List2)))))

答案 8 :(得分:0)

用任何分隔符连接两个向量,比如“#”。
可以使用 Office 365 TextJoin() 一步完成。假设结果是单元格 A1 中的一个字符串:
10#11#12#13#20#21#22

然后使用以下公式创建单个字符串数组。选择连续7个单元格显示数组后记得点击CTRL-SHFT-Enter得到大括号:

{=TRIM(MID(
  SUBSTITUTE(A1,"#",REPT(" ",99)),       
 (ROW(OFFSET($AB$1,,,LEN(A1)-LEN(SUBSTITUTE(A1,"#",""))+1))-1)*99+
((ROW(OFFSET($AB$1,,,LEN(A1)-LEN(SUBSTITUTE(A1,"#",""))+1)))=1),
  99))}

还可以通过将数组转换为数字来求和,如公式所示:

{=SUM(1*TRIM(MID(SUBSTITUTE(A1,"#",REPT(" ",99)),(ROW(OFFSET($AB$1,,,LEN(A1)-LEN(SUBSTITUTE(A1,"#",""))+1))-1)*99+((ROW(OFFSET($AB$1,,,LEN(A1)-LEN(SUBSTITUTE(A1,"#",""))+1)))=1),99)))}= 109

enter image description here