当我引用的值更改列时,如何查找一列中的数据?

时间:2016-05-24 21:44:47

标签: excel-vba excel-formula vba excel

我希望在两个文档之间进行INDEX-MATCH之类的查找,但我的MATCH's index array不会停留在一列中。
在Vague-English中:我想要一个已知列中的值与另一列中可能存在的值相匹配。

请参阅下图。让我们调用H列doc1上粗体垂直线左侧的所有内容,右侧为doc2。

Doc2有一列"找到这个",这将是INDEX's array。它与" ID1"来自doc1(请注意,"查找此"将的值与列ID1的顺序相同,但这样更容易以不这样的方式)。

" [结果]" doc2中的列将是来自doc1"想要这个"的值。匹配"找到这个"的行中的列...但是,有时来自"发现这个"不在" ID1"列,而是在" ID2"," ID3"等。
所以,我试图从Col J生成Col K.这就像按下Ctrl+F并在Col J中搜索一个值,然后从该行中的Col D获取值并将其复制到Col ķ。

我从另一个文档中的相同颜色的列中创建了相同的值,以便更容易想象它们来自何处。

Example documents

另请注意,在doc1的F列中,来自doc2" s的相同值"查找此"可以在其他一些文字后找到。 另请注意,列标题仅作为示例,ID列实际上没有编号。

我只是硬编码正确的列进行搜索,但我不能控制doc1,我担心未来的版本可能会有新的" ID"列,其他被删除。

我希望这是一个公式形式的解决方案,但VB会这样做。

4 个答案:

答案 0 :(得分:2)

要根据列J的给定值生成列K,您可以使用以下内容:

=INDEX(doc1!$D$2:$D$14,SUMPRODUCT((doc1!$B$2:$H$14=J2)*ROW(doc1!$B$2:$H$14))-1)

将该公式复制到您需要的位置。

它基本上只返回找到匹配列J的行。然后,我们在您的D范围的索引中找到该行,以获得您在K中的值。

概念证明:

Proof of concept

UPDATE:

如果您正在使用非唯一实体n列J.这个值可以在多个行和列中找到。请考虑使用以下命令返回找到J值的最后一行:

=INDEX(doc1!$D$2:$D$14,AGGREGATE(14,6,(doc1!$B$2:$H$14=J2)*ROW(doc1!$B$2:$H$14),1)-1)

更新2:

要返回第J行的第一行,你可以使用:

=INDEX($D$2:$D$14,AGGREGATE(15,6,1/($B$2:$H$14=J2)*ROW($B$2:$H$14)-1,1))

感谢Scott Craner对最小公式的暗示。

要确定您的范围B2:H14中是否有来自J列的UNIQUE数据,您可以输入此数组公式。要输入数组公式,您需要同时按CTRL + SHFT + ENTER而不只是按ENTER。当您在公式栏中看到公式周围的{}时,您就会知道自己已经做到了。你不能手动使用{}。

=IF(MAX(COUNTIF($B$2:$H$14,J2:J14))>1,"DUPLICATES","UNIQUE")

更新3

AGGREGATE - 对我来说是一个相对较新的功能,但可以追溯到Excel 2010. Aggregate是19个功能集合为1.如果它们都以相同的方式工作但它们不会这样做会很好。我认为它是编号为14及以上的函数,如果您愿意,它们将以相同的方式执行数组公式或CSE公式。好的是你在输入或编辑它时不需要使用CSE。 SUMPRODUCT是执行数组公式计算的常规公式的另一个示例。

我相信这个解释的内容是AGGREGATE括号内发生的事情。如果单击该链接,您将了解前两个参数是什么。第一个定义了您正在使用的函数,第二个告诉AGGREGATE如何处理错误,隐藏行和其他一些嵌套函数。这是相对容易的部分。我相信你想知道的是这发生了什么:

(doc1!$B$2:$H$14=J2)*ROW(doc1!$B$2:$H$14)

为了说明的目的,我们可以将这个公式缩小到可以做同样事情的规模。我将避免在A1开始,因为它可以使计数生活变得更容易,因为它是第1行和第1列。因此,通过将示例范围置于其外部,您可以看到更多特殊注意事项。

我想知道的是C列中每个项目列表中的哪一行出现在B列

  |    B     |    C
3 | DOG      |  PLATYPUS
4 | CAT      |  DOG
5 | PLATYPUS |

我们的迷你示例的完整公式将是:

{=($B$3:$B$5=C2)*ROW($B$3:$B$5)}

我们将把以下内容看作一个数组

=INDEX($B$3:$B$5,AGGREGATE(14,6,($B$3:$B$5=C2)*ROW($B$3:$B$5),1)-2)

因此,如您所述,第一个括号将是一个布尔数组。每个TRUE的单元格都将为TRUE,直到强制进入数学计算。当发生这种情况时,True变为1,False变为0.I该公式作为CSE公式输入并放置在D2中,它将按如下方式分解:

FALSE  X  3
FALSE  X  4
TRUE   X  5

3,4和5来自ROW()返回它在数组运算时处理的行号的值。小技巧,我们可以有ROW(1:3)。只需要确保数组的大小匹配!这不是矩阵数学只是直接乘法。由于布尔现在正在经历数学运算,我们现在正在研究:

0 X 3 = 0
0 X 4 = 0
1 X 5 = 5

因此,{0,0,5}的数组将被传递回聚合以进行更多处理。这里需要注意的重要一点是,它只包含0和我们匹配的各个行号。因此,对于第一个聚合公式,选择公式14,即LARGE函数。我们还告诉它忽略错误,在这种情况下无关紧要。因此,在将数组提供给聚合函数之后,有一个,1)来完成聚合函数。 1告诉聚合函数,当数组从最小到最大排序时,我们想要第一个大数。如果该数字为2,那么它将是第二大数字,依此类推。因此返回找到某个内容的最后一行或唯一一行。所以在我们的小例子中它将是5。

但等待5被埋没在另一个名为Index的函数中。在我们的小例子中,INDEX公式将是:

=INDEX($B$3:$B$5,AGGREGATE(...)-2)

嗯,我们知道范围只有3行长,所以要求第5行,因为你的索引号超出范围,所以会因为错误而砸到你的头部。所以在原始公式中标题行校正为-1或小例子为-2,我们在小例子中看到的是:

=INDEX($B$3:$B$5,5-2)
=INDEX($B$3:$B$5,3)

这里有一个奇怪的信息,最后一个不会变成PLATYPUS ...它变成了对B5的单元格引用拉了PLATYPUS。但那个小小的细微差别是另一个故事。

现在在评论中斯科特基本上告诉我反转错误以获得第一行。这对于聚合来说是重要的一步,它让我在圈子里跑了一段时间。因此,我们的迷你示例中第一行选项的完整等式是

=INDEX($B$3:$B$5,AGGREGATE(15,6,1/($B$3:$B$5=C2)*ROW($B$3:$B$5),1)-2)

斯科特克拉纳实际上建议跳过一个数学步骤是什么:

=INDEX($B$3:$B$5,AGGREGATE(15,6,ROW($B$3:$B$5)/($B$3:$B$5=C2),1)-2)

然而,由于我在写完这一切之后才意识到这一点,所以解释将继续使用这两个方程中的第一个

因此,需要注意的重要一点是从功能14到功能15的更改SMALL。把它想象成一个最小的发现。而这次6与1 /一起发挥了巨大的作用。所以这次我们在中间的数组相当于:

1/FALSE X 3
1/FALSE X 4
1/TRUE  X 5

然后变为:

1/0 X 3
1/0 X 4
1/1 X 5

然后再次将你打得更好,因为你试图除以0:

#div/0 X 3
#div/0 X 4
   1/1 X 5

但是当你使用6作为第二个参数/参考时,当你告诉AGGREGATE忽略错误时,你很聪明并保护自己免受那个耳光的影响!因此,上面的内容变为:

{5}

由于我们正在执行SMALL,并且我们通过了1)作为AGGREGATE的结束部分,我们基本上已经说过给我最小行号或结果数组中的第一个最小数字升序。

其余部分与LARGE AGGREGATE方法相同。我最初陷入的陷阱是我没有使用1 /强制错误。结果,每当我尝试获取数组的SMALL时,我从所有错误结果中得到0。

SUMPRODUCT以非常类似的方式工作,但只有在中间的结果数组只返回1非零答案时才有效。原因是SUMPRODUCT函数的最后一步是生成数组的所有单个元素。因此,如果您只有1个非零,那么您将得到非零数字。如果你有两行匹配例如12和31,那么SUMPRODUCT方法将返回43,这不是你想要的任何行号,其中聚合大号会告诉你31和小聚会告诉你12。 / p>

答案 1 :(得分:2)

这样的事可能,从K2开始并复制下来:

=IFERROR(INDEX(D:D,MAX(IFERROR(MATCH(J2,B:B,0),-1),IFERROR(MATCH(J2,E:E,0),-1),IFERROR(MATCH(J2,G:G,0),-1),IFERROR(MATCH(J2,H:H,0),-1))),"")

enter image description here

如果要保留Match变量列的位置,请考虑为要检查的每列创建通用范围名称,例如" Col1"," Col2", " COL3&#34 ;.创建一些超出您认为需要的范围名称,并将它们引用到=$B:$B=$E:$E等。将所有范围名称插入Max()语句中的Match函数,如上所述。

在表中添加或删除列时,请将范围名称定义调整为要检查的列。

例如,如果在Max()中设置了五个匹配的公式,并且表发生了更改,那么您只想检查三列,将三个范围名称指向同一列。即使同一列匹配多次,Max()也只返回一个结果和一个查找。

答案 2 :(得分:0)

如果我理解正确的话,我想出了一个vba解决方案:

Sub DisplayActiveRange()
    Dim sheetToSearch As Worksheet
        Set sheetToSearch = Sheet2
    Dim sheetToOutput As Worksheet
        Set sheetToOutput = Sheet1

    Dim search As Range
    Dim output As Range
    Dim searchCol As String
        searchCol = "J"
    Dim outputCol As String
        outputCol = "K"
    Dim valueCol As String
        valueCol = "D"
    Dim r As Range

    Dim currentRow As Integer
        currentRow = 1
    Dim maxRow As Integer
        maxRow = sheetToOutput.UsedRange.Rows.Count

    For currentRow = 1 To maxRow

        Set search = Range("J" & currentRow)

        For Each r In sheetToSearch.UsedRange

            If r.Value <> "" Then
                If r.Value = search.Value Then

                    Set output = sheetToOutput.Range(outputCol & currentRow)
                    output.Value = sheetToSearch.Range(valueCol & currentRow).Value

                    currentRow = currentRow + 1
                    Set search = sheetToOutput.Range(searchCol & currentRow)

                End If
            End If

        Next

    Next currentRow

End Sub

答案 3 :(得分:0)

可能有更好的方法,但这会给你你想要的。我们假设两个标题都在&#34; source&#34;和&#34;目的地&#34;床单。你需要适应&#34; Const&#34;根据工作表的命名方式声明。按Control&amp; G在Excel中打开VBA窗口并将此代码复制并粘贴到&#34;本工作簿&#34;根据&#34; VBA项目&#34;组,然后选择&#34;运行&#34;从菜单中:

        Option Explicit

    Private Const sourceSheet = "Source"
    Private Const destSheet = "Destination"

    Public Sub FindColumns()

        Dim rowCount As Long
        Dim foundValue As String

        Sheets(destSheet).Select

        rowCount = 1 'Assume a header row

        Do While Range("J" & rowCount + 1).value <> ""

            rowCount = rowCount + 1
            foundValue = FncFindText(Range("J" & rowCount).value)
            Sheets(destSheet).Select
            Range("K" & rowCount).value = foundValue

        Loop

    End Sub

    Private Function FncFindText(value As String) As String

        Dim rowLoop As Long
        Dim colLoop As Integer
        Dim found As Boolean
        Dim pos As Long

        Sheets(sourceSheet).Select
        rowLoop = 1
        colLoop = 0

        Do While Range(alphaCon(colLoop + 1) & rowLoop + 1).value <> "" And found = False

            rowLoop = rowLoop + 1

            Do While Range(alphaCon(colLoop + 1) & rowLoop).value <> "" And found = False

                colLoop = colLoop + 1

                pos = InStr(Range(alphaCon(colLoop) & rowLoop).value, value)

                If pos > 0 Then

                    FncFindText = Mid(Range(alphaCon(colLoop) & rowLoop).value, pos, Len(value))
                    found = True

                End If

            Loop

            colLoop = 0

        Loop

    End Function

    Private Function alphaCon(aNumber As Integer) As String

    Dim letterArray As String
    Dim iterations As Integer

    letterArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

        If aNumber <= 26 Then

            alphaCon = (Mid$(letterArray, aNumber, 1))

        Else

            If aNumber Mod 26 = 0 Then

                iterations = Int(aNumber / 26)
                alphaCon = (Mid$(letterArray, iterations - 1, 1)) & (Mid$(letterArray, 26, 1))

            Else

                'we deliberately round down using 'Int' as anything with decimal places is not a full iteration.
                iterations = Int(aNumber / 26)
                alphaCon = (Mid$(letterArray, iterations, 1)) & (Mid$(letterArray, (aNumber - (26 * iterations)), 1))

            End If

        End If

    End Function