忽略或避免 UDF 中的循环引用?

Posted

技术标签:

【中文标题】忽略或避免 UDF 中的循环引用?【英文标题】:Ignore, or avoid circular reference in UDF? 【发布时间】:2021-05-06 13:02:52 【问题描述】:

我正在尝试制作一个计划者。我有一张桌子,我想变成这样:

ID Parent ID Start Date Duration End Date
1 01/01/2021 10 11/01/2021
2 1 06/05/2021 2 08/05/2021
3 2 08/05/2021 1 09/05/2021
4 3 09/05/2021 5 14/05/2021
5 2,3,4 14/05/2021 4 18/05/2021

开始日期将基于父 ID 与任务 ID 的匹配,因此任务 2 在任务 1 之后,任务 2 在任务 3 之后等等。我还希望能够针对可能发生在同一时间,因此任务 5 可以在任务 2、3 或 4 之后开始,以最后一个结束。

我为计算开始日期编写了这个 UDF。

Option Explicit

Function LastPredecessor(PreList As String, TaskList As Range, TaskDueDate As Range)

Dim ID
Dim Dates
Dim nPres, i As Integer

On Error Resume Next

Pres = Split(PreList, ",")
nPres = UBound(Pres)
ReDim Dates(0 To nPres, 0)

For i = 0 To nPres
    Dates(i, 0) = IDX_Match(CInt(Pres(i)), TaskList, TaskDueDate)
Next

LastPredecessor = WorksheetFunction.Max(Dates)

End Function

Function IDX_Match(LookupVal, MatchRange As Range, LookupRange As Range, Optional MatchType As Integer = 0)
    
    IDX_Match = WorksheetFunction.Index(LookupRange.Value2, WorksheetFunction.Match(LookupVal, MatchRange.Value2, MatchType))
    
End Function

该函数在表格中的开始日期这样调用

=LastPredecessor([@Parent ID],[ID],[End Date])

如果结束日期独立于开始日期,效果很好,但是一旦我尝试更新结束日期,并添加开始日期 + 持续时间作为结束日期的计算,它就会产生一个循环错误。

我确定我做错了一些非常简单的事情,但很高兴知道我需要做些什么来解决这个问题。

编辑:

感谢@Toddleson,以下是稍作修改的版本

Function LastPredecessor(PreList As String, EndDates As Range, IDColumn As Range) As Date
    Dim Preds() As String, PredDates() As Long
    
    Preds = Split(PreList, ",")
    ReDim PredDates(UBound(Preds))
    For i = LBound(Preds) To UBound(Preds)
        PredDates(i) = IDColumn(WorksheetFunction.Match(CInt(Preds(i)), IDColumn, 0)).Offset(0, EndDates.Column - IDColumn.Column).Value2
    Next i
    LastPredecessor = Application.WorksheetFunction.Max(PredDates)
End Function

【问题讨论】:

你能澄清一下函数的目的和成功的结果是什么样的吗? 嗨@Toddleson 表中显示的是想象中的成功输出。目的是生成一个甘特表和图表,其中一个任务在完成另一个或多个任务之前无法开始,因此在上面的示例中,任务 5 的开始日期应该是任务 2、3 和 4 之间的最大结束日期。对于单个父任务,我可以使用普通的 excel 函数来执行此操作,但对于存在这些相互竞争的前置任务的情况,我需要 UDF。 【参考方案1】:

好的,我重写了函数。它应该完全按照你向我描述的那样做。

Function LastPredecessorV2(PreList As String, EndDates As Range, IDColumn As Range) As Date
    Dim Preds() As String, PredDates() As Long
    
    Preds = Split(Replace(PreList, " ", ""), ",")
    ReDim PredDates(UBound(Preds))
    For i = LBound(Preds) To UBound(Preds)
        PredDates(i) = IDColumn.Find( _
                        What:=CInt(Preds(i)), _
                        LookAt:=xlWhole _
                    ).Offset(0, EndDates.Column - IDColumn.Column).Value2 'Corrected
    Next i
    LastPredecessorV2 = CDate(Application.WorksheetFunction.Max(PredDates))
End Function

下面是参数输入的样子。这些范围可以是整个列,它仍然有效。

所有结束日期都使用公式(开始日期 + 持续时间(以天为单位))。第一个任务开始日期是该列中唯一的独立值,所有其他的都使用公式。

编辑: 我应该提到,您可以在范围之间插入列,并且该功能仍然可以工作。

【讨论】:

感谢您的帮助。我稍微修改了一下,因为我认为它提供了更大的灵活性(我认为建议只适用于 col A),但是这个定义让我找到了一个可行的解决方案。谢谢 好点,如果您移动桌子,.Column - 1 将停止工作。 -1 应该是 - IDColumn.Column,就像您在修改后的版本中一样。

以上是关于忽略或避免 UDF 中的循环引用?的主要内容,如果未能解决你的问题,请参考以下文章

避免 grails 中的循环引用继承

如何避免 activesupport 中的循环参数引用警告

当一个表中的两列指向另一个表中的主键时,避免循环引用

EF6:如何避免循环引用?

json数据避免$ref 循环引用

Json.NET如何避免循环引用