【问题标题】:Forced ranking macro excel vba强制排名宏excel vba
【发布时间】:2016-04-02 15:35:53
【问题描述】:

我的设置如上图所示。

宏的逻辑是,如果我在单元格B5 或在Range("B2:B26") 的空单元格中输入一个数字1,那么输出将采用以下格式:

B2 3
B3 4
B4 2
B5 1

现在它给了我输出,但有某些缺点,例如

如果我向同一个单元格提供输入8,那么它仍然会增加排名。我合并了一个匹配检查以查看该值是否存在,但它似乎不起作用任何帮助将不胜感激。

     Private Sub Worksheet_Change(ByVal Target As Range)

        Application.ScreenUpdating = False
        Application.EnableEvents = False

            Dim KeyCells As Range
            Dim i As Long, Cel As Range, sht1 As Worksheet, j As Long, found As Boolean
            Set sht1 = Sheet1

        Set KeyCells = sht1.Range("B2:C26")
        If Not Application.Intersect(KeyCells, Range(Target.Address)) Is Nothing Then

        If Target.Column = 2 Then

            For i = 2 To 26
                If sht1.Range("B" & i) <> Empty And sht1.Range("B" & i).Value >= Target.Value And i <> Target.Row Then
                        sht1.Range("B" & i).Value = sht1.Range("B" & i).Value + 1
                Else: End If
            Next i
            Else: End If


        If Target.Column = 3 Then

            For i = 2 To 26
                If sht1.Range("C" & i) <> Empty And sht1.Range("C" & i).Value >= Target.Value And i <> Target.Row Then
                        sht1.Range("C" & i).Value = sht1.Range("C" & i).Value + 1
                Else: End If
            Next i


        Else: End If


        Else: End If
        Call CreateDataLabels
        Target.Select
        Application.ScreenUpdating = True
        Application.EnableEvents = True
End Sub

【问题讨论】:

  • 我有点困惑。如果你在B5 中输入1 那么会发生什么?你是如何在B3 中得到4 的?
  • @SiddharthRout 如果你删除这部分 found = False For i = 2 To 26 If sht1.Range("B" &amp; i) &lt;&gt; Empty And sht1.Range("B" &amp; i).Value = Target.Value And i &lt;&gt; Target.Row Then found = True Else: End If Next i 你会得到它。
  • 能忘记代码,解释一下逻辑吗? :)
  • 是的,逻辑是我有 1-25 项技能,我想强制排名,这样我就不能两次选择相同的数字。如果我有 1、2、3、4……然后我添加一个新的 3,那么 3 变成 4,4 变成 5,依此类推。
  • 所以如果你在上面的场景中加上8(你已经有1,2,3)那么8应该保持原样还是应该变成4?

标签: vba excel ranking


【解决方案1】:

这是你正在尝试的吗?我没有对它进行广泛的测试

Option Explicit

Dim rng As Range

Private Sub Worksheet_Change(ByVal Target As Range)   
    Dim oldVal As Long, i as Long

    On Error GoTo Whoa

    Application.EnableEvents = False

    Set rng = Range("B2:B26")

    If Not Intersect(Target, rng) Is Nothing Then
        oldVal = Target.Value

        If NumExists(oldVal, Target.Row) = True Then
            For i = 2 To 26
                If i <> Target.Row And Range("B" & i).Value >= oldVal Then _
                Range("B" & i).Value = Range("B" & i) + 1
            Next i
        End If
    End If

Letscontinue:
    Application.EnableEvents = True
    Exit Sub
Whoa:
    MsgBox Err.Description
    Resume Letscontinue
End Sub

Function NumExists(n As Long, r As Long) As Boolean
    Dim i As Long

    For i = 2 To 26
        If Range("B" & i) = n And r <> i Then
            NumExists = True
            Exit Function
        End If
    Next i
End Function

【讨论】:

  • 让我测试一下谢谢你的努力:)
  • 我觉得这个If NumExists(oldVal, Target.Row) = True Then应该是If NumExists(oldVal, Target.Row) = False Then吧?
  • 因为如果数字不存在,那么它不应该通过进入循环来增加它:)
  • 是的,它不能很好地工作,因为它正在增加 Target.Value 并且它不应该触及目标单元格。请在答案中运行编辑后的代码,它将帮助您理解,我也在尝试对 C 列做同样的事情。
  • 但它会改变目标值。这就是你在cmets中提到的。如果用户在B5中输入1,由于数字已经存在,所以会变成4
【解决方案2】:

编辑以删除“帮助”值

已编辑为 C 列添加功能

作为 Siddharth Rout 的答案,并且 OP 不再要求任何其他内容,我建议以下作为替代选项,如果值得考虑,可以讨论

Option Explicit

Private Sub Worksheet_Change(ByVal target As Range)
    Dim oldVal As Long
    Dim wrkRng As Range

    Application.EnableEvents = False
    On Error GoTo EndThis

    If Continue(target, Range("B2:C26").Cells, oldVal, wrkRng) Then '<== here you set "B2:C26" as the "sensitive" range 
        With wrkRng
            .Offset(, 2).Value = .Value
            .FormulaR1C1 = "=IF(RC[2]<>"""",RC[2]+IF(and(RC[2]>=" & oldVal & ",ROW(RC)<>" & target.Row & "),1,0),"""")"
            .Value = .Value
            .Offset(, 2).ClearContents
        End With
    End If

EndThis:
    If Err Then MsgBox Err.Description
    Application.EnableEvents = True
    Exit Sub
End Sub

Function Continue(target As Range, rng As Range, oldVal As Long, wrkRng As Range) As Boolean
    If target.Cells.Count = 1 Then
        If Not IsEmpty(target) Then ' if cell has not been cancelled
            Set wrkRng = Intersect(target.EntireColumn, rng)
            If Not wrkRng Is Nothing Then
                oldVal = target.Value
                Continue = Application.WorksheetFunction.CountIf(wrkRng, oldVal) > 1
            End If
        End If
    End If
End Function

与 Siddharth Rout 的解决方案相比,它增强了以下方面:

  • 更多(完整?)测试好像继续rng 处理

    在之前的解决方案中

    • 如果您取消了 rng 中的一个单元格,它会在所有 rng 单元格中添加 1

    • 如果您将值粘贴到多个 rng 单元格中,则会引发错误

  • 不使用单元格迭代,用于oldVal 计数目的和排名更新

【讨论】:

  • 这很好用,也有优势,但是为什么我在 B 中输入 D 列时会得到一些数字?
  • 我的代码使用了一个“帮助”列 (Offset(, 2).Value = .Value),它与“B”列(即“D”列)相距两列。只是忘了删除“帮助”列值。请参阅编辑后的答案:现在“D”列中没有“帮助”值。如果您需要用相关数据填充“D”列,则使用不同的列偏移量更改每个出现的 Offset(, 2) 以达到“空闲”列
  • 好的,但我也想为 C 列实现与 B 列相同的逻辑
  • 这些已编辑的代码。我本可以把它改得比实际少一点,但我更愿意尽可能优化它
猜你喜欢
  • 1970-01-01
  • 2011-10-04
  • 1970-01-01
  • 2015-07-18
  • 2018-05-19
  • 2011-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多