Excel递归 - 在序列中找到最后一个

时间:2016-04-07 16:07:30

标签: excel vba recursion

使用Excel 2010&有一个零件清单,其中一些可以更改其零件号。如果某个部件已更改,则它将位于一个工作表(A列)中,以及其新值(B列)。更改时还有一个日期时间戳。

OldValue to NewValue

在我的所有部件号列表中,我需要

  1. 检查部件是否已更改,
  2. 跟踪它的每一个变化,直到我找到最后一个。
  3. last one ”可能意味着

    • B列( newVal )中的值不在A列( oldval )或
    • 如果它返回自身(更正),在这种情况下,最新的日期时间戳是决定因素。

    通过添加将其重定向到上一个(正确)版本的行来输入对列表的更正。一个例子:

    • oldVal A更改为newVal B 2016-04-08 11:39:04.765

    • oldVal B更改为newVal C 2016-04-08 12:21:39.801

    • 我们发现B - > C更改不正确
    • oldVal C更改为newVal B 2016-04-08 13:44.07.913

    我知道vLookup不会这样做。我认为SUMPRODUCT可以解决第一部分,但我不确定如何进行递归(或者如果可能的话)的时间部分。

    任何想法,想法,解决方案将不胜感激。

    修改

    其他信息 -

    所需的输出 Desired Output

    我想在 NewPartNo 列中显示的是该部件号链中的最后一个值。如果部件号来自A - > B - > C - > D,我希望在A列的 PartNo NewPartNo 列中看到D.

    你有一个1960-1,其newVal为25-1960。问题是25-1960然后有一个新的OBB1960。有多少级递归错位的val?

    这就是问题 - 在某些地方,部分可能(最初)被命名为A列中的任何一个。至少有多少级别 - 我不知道。可能是5-10,也许?我完全不知道。

    编辑2

    @ TMDean的解决方案大部分都有效,除了我有下面的数据。

    Recursion

    如果从A001开始,它将映射到B001。 B001的最新(最近的日期)映射是C001。 C001的映射(自身)与另一个映射具有相同的日期时间戳,因此A001应映射到C001。

    在单步执行该功能时,它会找到第一个(A001 - > B001),但在尝试查找第二个(B001 - > C001)时会抛出错误。

    在上文中,查找的正确结果应为A001 - > C001。

3 个答案:

答案 0 :(得分:3)

以下是两个用于检索递归 newVal 的UDF以及它的关联日期。

Option Explicit

Function newestVal(lu As Range, rng As Range)
    Dim val As Variant
    Static app As New Application

    'truncate the rng down to the used range
    Set rng = Intersect(rng, rng.Parent.UsedRange)
    val = lu.Value2

    With rng
        If IsError(app.Match(lu.Value2, .Columns(1), 0)) Then
            val = app.Index(.Columns(2), app.Match(val, .Columns(2), 0), 1)
        Else
            Do While Not IsError(app.Match(val, .Columns(1), 0))
                val = app.Index(.Columns(2), app.Match(val, .Columns(1), 0), 1)
            Loop
        End If
    End With

    newestVal = val
End Function

Function latestValDate(lu As Range, rng As Range)
    Dim d As Long, val As Variant, dbl As Double
    Static app As New Application

    'truncate the rng down to the used range
    Set rng = Intersect(rng, rng.Parent.UsedRange)
    val = lu.Value2

    With rng
        If IsError(app.Match(lu.Value2, .Columns(1), 0)) Then
            val = app.Index(.Columns(2), app.Match(val, .Columns(2), 0), 1)
        Else
            Do While Not IsError(app.Match(val, .Columns(1), 0))
                val = app.Index(.Columns(2), app.Match(val, .Columns(1), 0), 1)
            Loop
        End If

        For d = app.Match(val, .Columns(2), 0) To rng.Rows.Count
            If .Cells(d, 2).Value2 = val Then
                If .Cells(d, 3).Value2 > dbl Then
                    dbl = .Cells(d, 3).Value2
                End If
            End If
        Next d
    End With

    latestValDate = dbl
End Function

在F2:G2中,

=newestVal(E2,A:C )
=latestValDate(E2,A:C )

recursive_newval

¹用户定义函数(又名UDF)被放入标准模块代码表中。点击 Alt + F11 ,当VBE打开时,立即使用下拉菜单插入►模块 Alt + 中号)。将功能代码粘贴到标题为 Book1 - Module1(Code)的新模块代码表中。点击 Alt + Q 返回工作表。

答案 1 :(得分:1)

这是所有的伪代码,但它应该让你正确的方向。给我几个小时,我可以为你做,但尝试弄清楚它可能会很有趣。

    If Selection Offset Column + 1 <> vbNullString then
    newvalue = Selection Offset Column + 1.value
    for i = 1 to 100
    If newvalue Offset Column + 1 = vbNullString then
        Display (i)a,b,c
        Exit For
    ElseIf newvalue Offset Column + 1 <> vbNullString then
    newvalue = newvalue Offset Column + 1.value
    Next i

答案 2 :(得分:1)

受Jeeped解决方案的启发,我想知道它是否可以更通用,并编写了一个递归版VLOOKUP的UDF实现。

此函数的工作方式与VLOOKUP类似,不同之处在于它将跟随查找值链,直到它到达表的末尾,如问题中所述。

Option Explicit

Function VLOOKUPR(LookupValue As Variant, _
                  TableArray As Range, _
                  ColIndexNum As Long, _
                  RangeLookup As Boolean)
    Dim LookupIndex

    Set TableArray = Intersect(TableArray, TableArray.Parent.UsedRange)
    VLOOKUPR = CVErr(xlErrNA)

    Do
        LookupIndex = Application.Match(LookupValue, TableArray.Columns(1), _
                                        IIf(RangeLookup, 1, 0))
        If IsError(LookupIndex) Then Exit Do

        VLOOKUPR = TableArray(LookupIndex, ColIndexNum)
        If LookupValue <> TableArray(LookupIndex, 1) Then Exit Do

        LookupValue = TableArray(LookupIndex, ColIndexNum)
        Set TableArray = Intersect(TableArray, TableArray.Offset(LookupIndex))
    Loop Until TableArray Is Nothing
End Function

由于我希望它是通用的,所以我没有要求它返回查找值而不是#N/A如果它存在于列B中。您可以在您使用的单元格公式中包含此逻辑致电VLOOKUPR=IFERROR(VLOOKUPR($E2,$A$2:$A$100,2,FALSE),VLOOKUP($E2,$B$2:$B$100,1,FALSE))