如何使用两个标准(数组公式或VBA)基于数据构build数组,

我有以下示例数据:

Excel中的示例数据表

第一列是名称列表,第二列是这些名称所属的年份。

我想要做的是构build一年中所有唯一(不同)名称的列表。 因此,例如在2016年,我希望它在最终结果列中创build一个列表,而在2017年,我希望它用当年的唯一名称build立一个列表。

最好我希望它是一个(dynamic)命名范围,以便计算只需执行一次,以便我可以使用=INDEX(examplenamedrange, 1)公式来调用我想要使用的名称。

如果这在dynamic命名范围内是不可能的,那么将数组存储在VBA中也是可能的。

我已经看到了一些在networking中查看列表中的唯一值的Excel公式,但没有一个我可以find一个额外的标准(在这种情况下:年)。

任何人都可以在正确的道路上设置我吗?

只是在那里添加另一个类似,但不同的方法。 您可以使用返回数组的UDF。 因此,将代码粘贴到代码模块中,然后在工作表上使用以下公式

 =GetNamesInYear(names,dates,2016) 

其中names是您的名称范围, dates是您的date范围, 2016是您正在search的年份,无论是在公式中编写的数字还是对值为2016的单元格的引用都没有问题。

要返回完整数组,您需要使用Ctrl + Shift + Enterinput公式。 要查看所有结果,而不仅仅是第一个结果,请突出显示该单元格及其下方的5(例如),按F2编辑,然后再按Ctrl + Shift + Enter

或者,您可以使用任何可以处理string数组的工作表函数来访问数组。 例如:

 =INDEX(GetNamesInYear(names,dates,2016),2) 

返回数组中的第二个项目


这是代码

 Function GetNamesInYear(names As Range, years As Range, year As Integer) As Variant Dim namesArr As Variant namesArr = names.Value2 Dim yearsArr As Variant yearsArr = years.Value2 Dim results As Long results = 0 Dim resultArr As Variant Dim i As Long ReDim resultArr(0 To 0) For i = 1 To UBound(namesArr, 1) If Not InArray(resultArr, namesArr(i, 1)) And (yearsArr(i, 1) = year) Then ReDim Preserve resultArr(0 To results) resultArr(results) = namesArr(i, 1) results = results + 1 End If Next i GetNamesInYear = Application.WorksheetFunction.Transpose(resultArr) End Function Private Function InArray(arr As Variant, value As Variant) As Boolean Dim i As Integer For i = 0 To UBound(arr) If arr(i) = value Then InArray = True Exit Function End If Next i InArray = False End Function 

结果如下所示:

在这里输入图像说明

更新
姓名和dateinput现在按照来自OP的意见拆分出来(单独的范围)

这是一个简短的VBA子实现你所要求的。

要设置子,请按Alt + F11打开VBA编辑器,然后Insert > Module并粘贴以下代码。 我已经评论它,以显示每个部分正在做什么。 你也可以设置这个在年份单元格更改时运行,但是我将把它作为一个练习留给你! 要运行它,请在VBA编辑器中按F5或单击运行button。

 Sub uniqueInYear() Dim sh As Worksheet Set sh = ActiveSheet Dim vcell As Range Dim namesString As String namesString = "" Dim namesList() As String ' Compile string with all names comma separated for given year For Each vcell In Range("A2:A" & sh.UsedRange.Rows.Count) ' check if name already captured for given year If InStr(namesString, vcell.Value) = 0 And vcell.Offset(0, 1).Value = sh.Range("E1").Value Then namesString = namesString & "," & vcell.Value End If Next vcell ' If empty then quit If namesString = "" Then Exit Sub End If ' Remove leading comma namesString = Right(namesString, Len(namesString) - 1) ' Put names into array namesList = Split(namesString, ",") ' Write names to result column after clearing it sh.Range("E2:E" & sh.UsedRange.Rows.Count + 1).Value = "" Dim nameVar As Variant For Each nameVar In namesList sh.Range("E" & sh.UsedRange.Rows.Count + 1).End(xlUp).Offset(1, 0).Value = nameVar Next nameVar ' Named range - delete if it exists then create a-fresh On Error Resume Next sh.Parent.Names("UniqueNames").Delete On Error GoTo 0 sh.Parent.Names.Add name:="UniqueNames", _ RefersTo:=sh.Range("E2", sh.Range("E" & sh.UsedRange.Rows.Count + 1).End(xlUp)) End Sub 

结果:

在这里输入图像说明

你可以试试这个:

 Sub Names() Dim x, Years, Counted, ColumnCount, j, lColumn Dim Names(), FoundNames() Years = Range("B1").Value Counted = 0 ColumnCount = 2 ReDim Names(Range("A" & Rows.count).End(xlUp).row) ReDim FoundNames(LBound(Names) To UBound(Names)) lColumn = Cells(1, Cells(1, Columns.count).End(xlToLeft).Column).Column For Each c In Range(Range("A1"), Range("A" & Rows.count).End(xlUp)) If Years <> Range("B" & c.row).Value Then For i = LBound(Names) To UBound(Names) If Names(i) <> "" Then j = j + 1 FoundNames(j - 1) = Names(i) End If Next i ReDim Preserve FoundNames(LBound(Names) To j - 1) Cells(1, lColumn + ColumnCount).Value = Years For i = LBound(FoundNames) To UBound(FoundNames) Cells(i + 2, lColumn + ColumnCount).Value = FoundNames(i) Next ColumnCount = ColumnCount + 1 Years = Range("B" & c.row).Value Counted = 0 ReDim Names(Range("A" & Rows.count).End(xlUp).row) ReDim FoundNames(LBound(Names) To UBound(Names)) End If If InStr(Join(Names, ","), c.Value) < 1 Then Names(Counted) = c.Value Counted = Counted + 1 End If Next c j = 0 For i = LBound(Names) To UBound(Names) If Names(i) <> "" Then j = j + 1 FoundNames(j - 1) = Names(i) End If Next i ReDim Preserve FoundNames(LBound(Names) To j - 1) Cells(1, lColumn + ColumnCount).Value = Years For i = LBound(FoundNames) To UBound(FoundNames) Cells(i + 2, lColumn + ColumnCount).Value = FoundNames(i) Next End Sub 

结果如下所示:

在这里输入图像说明

数组公式可以在这里工作:

=INDEX($A$1:$A$15, N(IF({1}, MODE.MULT(IF(($B$1:$B$15=2016)*(ROW($A$1:$A$15)=MATCH($A$1:$A$15, $A$1:$A$15, 0)), (ROW($A$1:$A$15)) * {1,1})))))

将你命名的范围定义为dynaRange_2016,看看它在两个图像中的用法

在这里输入图像说明

在这里输入图像说明

您可以改为每年的范围命名,然后再定义另一个名称作为唯一范围。 这更通用:

定义命名的range_2017 as =INDEX(Sheet5!$A:$A, MATCH(2017,Sheet5!$B:$B, 0)):INDEX(Sheet5!$A:$A, MATCH(2017,Sheet5!$B:$B, 1))

然后定义另一个命名范围uniques_2017 =INDEX(Sheet5!range_2017, N(IF({1}, MODE.MULT(IF(ROW(Sheet5!range_2017)-MATCH(2017, Sheet5!$B:$B, 0)+1=MATCH(Sheet5!range_2017, Sheet5!range_2017, 0), (ROW(Sheet5!range_2017)-MATCH(2017, Sheet5!$B:$B, 0)+1) * {1,1})))))

在你的工作表中,例如你可以调用INDEX(uniques_2017,3)。 对于你所期望发生的所有年份也要这样做。