【问题标题】:How to get the first and last numbers in a sequence如何获取序列中的第一个和最后一个数字
【发布时间】:2020-07-16 18:50:47
【问题描述】:

我正在使用 VBA 尝试获取分隔数字序列中的第一个和最后一个数字。

数据仅显示为逗号分隔的数字序列。它们始终按数字升序排列。

所以一个例子可能是:

1,2,3,5,7,8,9

只需要序列的第一个和最后一个,序列定义为比前一个数字大一的数字,在这种情况下有两个序列: 1,2,3 和 7,8,9。 5 不在序列中,因为它前后被不止一个分隔(缺少 4 和 6)。

在这两种情况下,我需要每个序列的第一个和最后一个数字,即1 & 3 用于第一个序列,7 & 9 用于第二个序列。

我想知道 regex 是否适合于此,或者将它们放入某个集合以便能够更轻松地前后导航是否会更好?

不确定从哪里开始——有什么想法吗?

非常感谢。

【问题讨论】:

    标签: vba sequence


    【解决方案1】:

    最简单的方法可能是使用Split:

    NumberSequence = "1,2,3,5,7,8,9"
    ValueList = Split(NumberSequence, ",")
    

    然后拉取ValueList数组中第一个和最后一个索引的值。

    您的问题描述中的复杂之处在于识别数字序列中何时“缺失”数字,但Split 也将有助于解决此问题。将数字读入数组后,很容易循环遍历它们并确定“间隙”在哪里。

    这说明了方法:

    Sub SampleProject()
        
        GetMinAndMax "1,2,3,5,7,8,9"
    
    End Sub
    
    Sub GetMinAndMax(NumberSequence As String)
        
        ValueList = Split(NumberSequence, ",")
        
        MinValue = CInt(ValueList(0))
        
        For i = 1 To UBound(ValueList)
            If CInt(ValueList(i)) > CInt(ValueList(i - 1)) + 1 Then
                MaxValue = CInt(ValueList(i - 1))
                
                'Ignore single numbers ("5" in your example)
                If Not MinValue = MaxValue Then MsgBox MinValue & " - " & MaxValue
                
                MinValue = CInt(ValueList(i))
                
            ElseIf i = UBound(ValueList) Then
                MaxValue = CInt(ValueList(UBound(ValueList)))
                MsgBox MinValue & " - " & MaxValue
                
            End If
        Next
    
    End Sub
    

    【讨论】:

    • 虽然 split 会将所有内容放入一个数组中,但它不会过滤掉 5,因为这不是连续的,因为列表中没有 4 和 6。
    • 根据原题的问题描述,序列1,2,3和7,8,9需要分别处理。所以答案是将Split 分别应用于它们中的每一个。不存在“过滤掉”不属于您正在研究的序列的数字的问题。
    • 我重读了您的问题并意识到我错过了什么。作为流程的一部分,您需要代码来识别序列中的“中断”。使用Split 仍然是实现目标的最简单方法,但需要用一些逻辑来详细说明以识别中断。我在上面扩展了我的答案,以展示一种方法。
    【解决方案2】:

    请尝试下一个功能:

    Function SequenceMargins(s As String) As Variant
      Dim arr As Variant, i As Long, strSeq As String, firstEl As Long
      Dim arrFin As Variant, k As Long
      
      arr = Split(s, ",")
      ReDim arrFin(UBound(arr))
    
      For i = 0 To UBound(arr) - 1
        If CLng(arr(i)) = CLng(arr(i + 1)) - 1 And firstEl = 0 Then firstEl = CLng(arr(i))
        If firstEl <> 0 And CLng(arr(i)) > firstEl And CLng(arr(i)) <> CLng(arr(i + 1)) - 1 Then
            arrFin(k) = firstEl & "&" & arr(i): k = k + 1
            firstEl = 0
        End If
        If i = UBound(arr) - 1 And firstEl <> 0 Then
            arrFin(k) = firstEl & "&" & arr(UBound(arr)): k = k + 1
        End If
      Next i
      ReDim Preserve arrFin(k - 1)
      SequenceMargins = arrFin
    End Function
    

    可以这样调用:

    Sub testSequenceMargins()
      Dim s As String, arr As Variant
      s = "1,2,3,5,7,8,9,10,13,14,15,20,34,35,36"
      arr = SequenceMargins(s)
      Debug.Print UBound(arr), arr(0), arr(UBound(arr))
    End Sub
    

    【讨论】:

      【解决方案3】:

      此方法接受一个包含数字序列的数组,并返回一个集合,其中包含找到的每个序列的第一个和最后一个数字:

      Option Explicit
      
      Private Sub Form_Load()
         Dim c As Collection
         Set c = FirstAndLast(Array(1, 2, 3, 5, 7, 8, 9))
      End Sub
      
      Public Function FirstAndLast(ByVal Sequence As Variant) As Collection
         Dim i As Integer
         
         Set FirstAndLast = New Collection
         
         For i = LBound(Sequence) To UBound(Sequence) - 1
            If Sequence(i) + 1 = Sequence(i + 1) Then
               If FirstAndLast.Count Mod 2 = 0 Then FirstAndLast.Add Sequence(i)
               If i = UBound(Sequence) - 1 Then FirstAndLast.Add Sequence(i + 1)
            Else
               If FirstAndLast.Count Mod 2 = 1 Then FirstAndLast.Add Sequence(i)
            End If
         Next
      End Function
      

      【讨论】:

        猜你喜欢
        • 2016-06-05
        • 2012-02-02
        • 1970-01-01
        • 2021-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-23
        相关资源
        最近更新 更多