如何使用Linq

时间:2018-04-03 19:04:26

标签: vb.net linq range

我有一系列字母数字邮政编码,看起来像这样:

PostalCodes(0) = "AA000-DD130"
PostalCodes(1) = "DD131-DD150"
PostalCodes(2) = "DD151-EE180"
PostalCodes(3) = "EE300-EE600"
PostalCodes(4) = "EE450-EE700"
PostalCodes(5) = "EE800"
PostalCodes(6) = "EE810"
PostalCodes(7) = "EE811"
PostalCodes(8) = "EE812"
PostalCodes(9) = "EE813"
PostalCodes(10) = "EE814"
PostalCodes(11) = "EE815"

我希望它能够优化到这样的东西:

PostalCodes(0) = "AA000-EE180"
PostalCodes(1) = "EE300-EE700"
PostalCodes(2) = "EE800"
PostalCodes(3) = "EE810-EE815"

正如你所看到的,范围可以重叠或者可能存在间隙,没问题,我只想尽可能地优化(减少)邮政编码。 我已经有一个使用For循环的代码,但我想知道是否有办法使用Linq更快地完成这项任务并提高性能?

我正在使用vb.net。

提前致谢。

2 个答案:

答案 0 :(得分:1)

这几乎是这样的:

Dim PostalCodes(11) As String
PostalCodes(0) = "AA000-DD130"
PostalCodes(1) = "DD131-DD150"
PostalCodes(2) = "DD151-EE180"
PostalCodes(3) = "EE300-EE600"
PostalCodes(4) = "EE450-EE700"
PostalCodes(5) = "EE800"
PostalCodes(6) = "EE810"
PostalCodes(7) = "EE811"
PostalCodes(8) = "EE812"
PostalCodes(9) = "EE813"
PostalCodes(10) = "EE814"
PostalCodes(11) = "EE815"

Dim splits = _ 
    PostalCodes _
        .Select(Function (x) If(x.Contains("-"), x.Split("-"c), { x, x })) _
        .Select(Function (ps) ps.Select(Function (p) New With _
        { _
            .Prefix = p.Substring(0, 2), _
            .Value = Integer.Parse(p.Substring(2)) _
        }).ToArray()) _
        .ToArray()

Dim results = _
    splits _
        .Skip(1) _
        .Aggregate( _
            splits.Take(1).ToList(), _
            Function (a, x)
                Dim l = a.Last()
                If x(0).Prefix = l(1).Prefix AndAlso x(0).Value <= l(1).Value + 1 Then
                    a.RemoveAt(a.Count - 1)
                    a.Add( _
                    { _
                        New With _
                        { _
                            .Prefix = l(0).Prefix, _
                            .Value = l(0).Value _
                        }, _
                        New With _
                        { _
                            .Prefix = l(1).Prefix, _
                            .Value = x(1).Value _
                        } _                     
                    })
                Else
                    a.Add(x)
                End If
                Return a
            End Function) _
        .Select(Function (xs) String.Format("{0}{1:000}-{2}{3:000}", xs(0).Prefix, xs(0).Value, xs(1).Prefix, xs(1).Value)) _
        .ToArray()

它给出了:

AA000-DD180 
EE300-EE700 
EE800-EE800 
EE810-EE815 

还有一小部分工作要摆脱双"EE800-EE800"

答案 1 :(得分:0)

使用扩展方法将范围转换为带有StartPrefix,Start,EndPrefix,End values的元组:

<Extension()>
Public Function RangeToTuple(ByVal strRange As String) As (StartPrefix As String, Start As Integer, EndPrefix As String, [End] As Integer
    If strRange.Contains("-") Then
        Dim twoRanges = strRange.Split("-")
        Dim intRanges = twoRanges.Select(Function(rs) rs.Substring(2).ToInteger()).ToList()
        Return (twoRanges(0).Substring(0, 2), intRanges(0), twoRanges(1).Substring(0, 2), intRanges(1))
    Else
        Dim intRange = strRange.Substring(2).ToInteger()
        Return (strRange.Substring(0, 2), intRange, strRange.Substring(0, 2), intRange)
    End If
End Function

使用简单的扩展将String转换为Integer:

<Extension()>
Public Function ToInteger(s As String) As Integer
    Return Convert.ToInt32(s)
End Function

使用自定义扩展方法,该方法是APL扫描运算符的变体,其作用类似于Aggregate但返回中间值,并使用ValueTuple返回原始值和中间计算:< / p>

<Extension()>
Public Iterator Function ScanPair(Of T, TKey)(src As IEnumerable(Of T), seedKey As TKey, combine As Func(Of (Key As TKey, Value As T), T, TKey)) As IEnumerable(Of (Key As TKey, Value As T))
    Using srce = src.GetEnumerator()
        If srce.MoveNext() Then
            Dim prevkv = (seedKey, srce.Current)

            While srce.MoveNext()
                Yield prevkv
                prevkv = (combine(prevkv, srce.Current), srce.Current)
            End While
            Yield prevkv
        End If
    End Using
End Function

一种基于谓词分组的扩展方法:

<Extension()>
Public Function GroupByWhile(Of T, TRes)(src As IEnumerable(Of T), test As Func(Of T, T, Boolean), result As Func(Of T, TRes)) As IEnumerable(Of IGrouping(Of Integer, TRes))
    Return src.ScanPair(1, Function(kvp, cur) If(test(kvp.Value, cur), kvp.Key, kvp.Key + 1)) _
              .GroupBy(Function(kvp) kvp.Key, Function(kvp) result(kvp.Value))
End Function

<Extension()>
Public Function GroupByWhile(Of T)(src As IEnumerable(Of T), test As Func(Of T, T, Boolean)) As IEnumerable(Of IGrouping(Of Integer, T))
    Return src.GroupByWhile(test, Function(e) e)
End Function

然后你可以使用LINQ来处理元组:

Dim combinedRanges = input.Select(Function(rs) rs.RangeToTuple()) _
                          .GroupByWhile(Function(prev, cur) prev.EndPrefix = cur.StartPrefix And cur.Start <= prev.End+1 And prev.End <= cur.End) _
                          .Select(Function(kvpg) (kvpg.First().StartPrefix, kvpg.First().Start, kvpg.Last().EndPrefix, kvpg.Last().End)) _
                          .Select(Function(tp) If(tp.Start = tp.End, $"{tp.StartPrefix}{tp.Start}", _
                                                                     $"{tp.StartPrefix}{tp.Start}-{tp.EndPrefix}{tp.End}"))

注意:写完这个答案之后,我意识到我可以将我的序列分组结构概括为对谓词进行分组,并使其更易于访问。

每条评论转换为VB。