我正在寻找一个公式,该公式获取表格的名称和数量并将其逐项列出,以便数量与名称出现的行数相匹配。
以此表作为输入为例:
姓名 | 数量 |
---|---|
芯片 | 1 |
苏打 | 2 |
比萨 | 1 |
曲奇饼 | 3 |
并在下一张表中输出此内容:
逐项 |
---|
芯片 |
苏打 |
苏打 |
比萨 |
曲奇饼 |
曲奇饼 |
曲奇饼 |
答案1
这应该适用于所有版本的 Excel,但 2019 可能会有使用新函数的更简单的方法;这假设您的初始数组位于 A1:B4。
首先,您需要在 A 中添加一个辅助列来对 B 列执行 COUNTIF。将其放在 Sheet2 的 A1 中,然后将其向下拖:
=COUNTIF(B$1:B1,B1)
在 B1 中您需要输入第一个值;要么输入要么输入=Sheet1!A1
。
然后在 B2 中你可以使用这个:
=IF(VLOOKUP(B1, Sheet1!$A$1:$B$11,2,0)=A1,INDEX(Sheet1!$A$1:$A$11,MATCH(B1,Sheet1!$A$1:$A$11,0)+1),B1)
该方法通过对上面的行进行 Vlookup 并返回数量来实现。如果 A 列中的计数小于数量,则重复 B1 的值(A2 将增加 1)。如果计数等于最大值,则使用 INDEX/MATCH 查找列表中的下一个名称。
请注意,名称和数量范围在此处写为,Sheet1!$A1:$B11
但您可以根据需要扩展它。如果您有数量为 0 的物品,或者如果您有任何物品在列表中出现多次,它就会中断。如果这些是必需的,那么可能可以编写一个略有不同的版本
答案2
如果您愿意,这里有一些代码可以为您完成此操作。
按“ALT + F11”打开代码编辑器。
右键单击 VBAProject(在打开的工作簿弹出的代码编辑器中)
选择插入/新模块
将会弹出一个新的模块面板。
将下面的代码粘贴到其中:
Sub CreateList()
Dim StartingCell
StartingCell = "A1"
Dim ActSheet As Worksheet
Dim SelRange As range
Set ActSheet = ActiveSheet
Set SelRange = Selection
Dim cel As range
Dim selectedRange As range
Set selectedRange = Application.Selection
Dim offs As Integer
Dim rowloc
Dim cellname
Dim loopcount
Dim totalcount
Dim rowdata()
Dim actrow
offs = 1
rowloc = 0
totalcount = 0
For Each cel In selectedRange.Cells
rowloc = rowloc + 1
If rowloc = 2 Then
rowloc = 0
If cel.Value > 0 Then
totalcount = totalcount + cel.Value
End If
End If
Next cel
actrow = 0
ReDim rowdata(totalcount)
For Each cel In selectedRange.Cells
rowloc = rowloc + 1
If rowloc = 2 Then
rowloc = 0
loopcount = cel.Value
If loopcount > 0 Then
For i = 1 To loopcount
rowdata(actrow) = cellname
actrow = actrow + 1
Next i
End If
Else
cellname = cel.Value
End If
Next cel
Dim NewSheet As Worksheet
Set NewSheet = ThisWorkbook.Sheets().Add()
NewSheet.Move After:=Sheets(ThisWorkbook.Sheets().Count)
NewSheet.range(StartingCell).Select
For i = 0 To totalcount - 1
ActiveCell.Value = rowdata(i)
ActiveCell.Offset(1).Select
Next i
ActSheet.Select
SelRange.Select
MsgBox "New sheet created"
End Sub
返回你的工作簿。
突出显示您所需要的内容。
按“alt + F8”调出宏列表。
选择创建列表
点击运行按钮
创建新工作表时将显示一个弹出窗口。
它必须有两列宽(根据您的设计),第一列是文本值,下一列是数量。从技术上讲,您可以拥有任意多的行,只需突出显示所有行,然后就会创建一个新工作表(每次运行宏时)。
这应该可以正常工作,因为它们将被忽略。
您可以更改下面显示的脚本行(第 3 行)中的单元格来更改列表的开始创建位置(只需将 A1 更改为您想要的单元格)。
StartingCell = "A1"
一旦创建,可以根据需要进行复制和粘贴,而不必依赖初始列表。
VBA 不是我的第一个脚本语言,因此它可能更简洁,但它是有效的代码。
答案3
以下公式可以解决问题。它使用FILTERXML
函数将文本转换为范围:
=FILTERXML("<Group><Element>"& SUBSTITUTE(LEFT(TEXTJOIN("",TRUE,REPT((A2:A5&"|"),B2:B5)), LEN(TEXTJOIN("",TRUE,REPT((A2:A5&"|"),B2:B5)))-1), "|", "</Element><Element>") &"</Element></Group>","/*/*")
MS 表示 Excel 2019 具有 SPILL 功能。我不记得是这样,但我想一定是因为在他们向我们推出之前它已经转移到了 Excel 2021,所以我从来没有注意到我们拥有的仍然是 Excel 2019。LET
显然不是在 2019 年,所以我没有将TEXTJOIN
用途放入公式范围的名称中,而是使用了“根据需要重复多次”的方法。
REPT
用于重复 A 列单元格中的文本,重复次数与 B 列单元格中的值相同。|
放置在每个单元格之间,这样内容中的奇怪巧合不会导致以后出现错误的结果,但主要是因为有一个已知字符可以分割字符串。 该内部数组用于TEXTJOIN
生成具有所有必需值且可拆分的字符串。LEFT
然后用于删除末尾多余的“|”。
接下来,`FILTERXML 设置一个起始字符串,该字符串中的“|”字符被适当的 XML 标签对替换,还有一个结束字符串,外加一个简单的 XML 路径。
这显示了充分使用该想法的所有部分。但有人可能会更狡猾,用 XML 标记对替换“|”字符并跳过该SUBSTITUTE
步骤:
=FILTERXML("<Group><Element>"& LEFT(TEXTJOIN("",TRUE,REPT((A2:A5&"</Element><Element>"),B2:B5)), LEN(TEXTJOIN("",TRUE,REPT((A2:A5&"</Element><Element>"),B2:B5)))-19) &"</Element></Group>","/*/*")
的缺点是FILTERXML
,这不是一个可以INDEX
获取任意大小的范围并对其进行处理的函数。它有一个内部限制,我相信 处理的数组大约有 6,000 多个字符FILTERXML
。(TEXTJOIN
也有这种限制。)因此,人们不想不必要地向字符串中添加字符。事实上,虽然我为了清晰起见使用了和,但人们经常使用简单的和来代替。
但它仍然归结为无法直接处理大范围。因此,如果您有大约 250 行(示例数据集中有 700 行“有效”重复填充 700 行,这意味着它没有使用所有行(见下文)但没有因长度而失败,但每行中较长的条目和更多的重复会影响事情……所以就像他们过去常说的那样,它对某些“有效”值有效)它可以做得很好。此外,内部(对于公式计算过程,但通过使用 F9 逐步查看可见)数组将精确地截断它可以处理的长度,从而导致一些有趣的事情,因为它的最后一个条目可能会以奇怪的方式结束。例如,应该看起来像的数组{"horsey"}
可能会像这样结束: {"hors
伴随失败。
但如果列表足够短并且需要的重复次数足够少(平均每次 3 次,而不是 57 次),那么它可能会快速而简单。
但是,像 give 这样的 VBA 解决方案Netspud2K
是此类问题的黄金标准。即使它没有完全优化,它也会很快,并且不受所需结果长度的限制。