【问题标题】:How to select the last set of concatenated sequence from multiple similar sequence如何从多个相似序列中选择最后一组连接序列
【发布时间】:2012-05-11 02:47:09
【问题描述】:

我有一个 int 数组,它是多个相似数组的串联数组,都从 1 开始。

1、2、3、4
1、2
1、2、3
1、2

int[] list = { 1, 2, 3, 4, 1, 2, 1, 2, 3, 1, 2 };

我想要实现的是获得结果的“最后一组”,即 {1, 2}。

尝试

int[] list = { 1, 2, 3, 4, 1, 2, 1, 2, 3, 1, 2 };

List<int> lastSet = new List<int>();
var totalSets = list.Count(x => x == 1);

int encounter = 0;
foreach (var i in list)
{
    if (i == 1)
        encounter += 1;

    if (encounter == totalSets)
        lastSet.Add(i);
}

lastSet.ToList().ForEach(x => Console.WriteLine(x));

有没有更好的方法来使用 LINQ 实现这一点,也许是SkipWhileGroupByAggregate

【问题讨论】:

    标签: c# .net linq collections


    【解决方案1】:

    如果您可以让您的列表成为实际的List&lt;int&gt;,或者如果您不介意通过.ToList() 创建列表的副本,您可以这样做:

    var list = new[]{ 1, 2, 3, 4, 1, 2, 1, 2, 3, 1, 2 }.ToList();
    var lastSet = list.Skip(list.LastIndexOf(1)).ToList();
    

    否则Aggregate可以工作,但是有点丑:

    var lastSet = list.Aggregate(new List<int>{1}, (seed, i) => {
        if(i == 1) {seed.Clear(); }
        seed.Add(i);
        return seed;
    })
    

    更新

    正如 dtb 指出的那样,您可以使用 Array.LastIndexOf 而不是创建列表:

    var list = new[]{ 1, 2, 3, 4, 1, 2, 1, 2, 3, 1, 2 };
    var lastSet = list.Skip(Array.LastIndexOf(list, 1)).ToList();
    

    【讨论】:

    • @dtb:呵呵,是的,当然。出于某种原因,我没有读到肯定是int[] 的部分,所以我只是假设他会使用IEnumerable&lt;int&gt;
    【解决方案2】:

    适用于任何IEnumerable(但比直接List 版本慢)

    var sub = list.Reverse<int>()
                  .TakeWhile(i => i != 1)
                  .Concat(new[]{1})
                  .Reverse<int>();
    

    如果你愿意,可以在结果上运行ToArray()

    【讨论】:

    • 打败我。我的想法完全正确。
    • 建议:不要使用Enumerable.Repeat,而是使用new[]{1}。它更短、更快,而且(我认为)更容易阅读。
    • @StriplingWarrior 谢谢,你是对的。我真的很喜欢像Enumerable.Return(1) 这样的框架扩展方法,因为它经常出现。我忘记了可以在没有类型信息的情况下实例化数组。
    【解决方案3】:

    使用下面的GroupAdjacent Extension Method,您可以将列表拆分为以 1 开头的序列,然后取最后一个序列:

    var result = list.GroupAdjacent((g, x) => x != 1)
                     .Last()
                     .ToList();
    

    public static IEnumerable<IEnumerable<T>> GroupAdjacent<T>(
        this IEnumerable<T> source, Func<IEnumerable<T>, T, bool> adjacent)
    {
        var g = new List<T>();
        foreach (var x in source)
        {
            if (g.Count != 0 && !adjacent(g, x))
            {
                yield return g;
                g = new List<T>();
            }
            g.Add(x);
        }
        yield return g;
    }
    

    【讨论】:

    • 不错的扩展,总是一个常见的用例,并且比 GroupBy 要高效得多,GroupBy 必须缓冲才能知道它是否正确(这可能是 - 而且是 - 懒惰)。
    【解决方案4】:

    LINQ 被高估了:

    int[] list = { 1, 2, 3, 4, 1, 2, 1, 2, 3, 1, 2 };
    
    int pos = Array.LastIndexOf(list, 1);
    
    int[] result = new int[list.Length - pos];
    Array.Copy(list, pos, result, 0, result.Length);
    
    // result == { 1, 2 }
    

    现在可读性提高了 100%:

    int[] list = { 1, 2, 3, 4, 1, 2, 1, 2, 3, 1, 2 };
    
    int[] result = list.Slice(list.LastIndexOf(1));
    
    // result == { 1, 2 }
    

    在哪里

    static int LastIndexOf<T>(this T[] array, T value)
    {
        return Array.LastIndexOf<T>(array, value);
    }
    
    static T[] Slice<T>(this T[] array, int offset)
    {
        return Slice(array, offset, array.Length - offset);
    }
    
    static T[] Slice<T>(this T[] array, int offset, int length)
    {
        T[] result = new T[length];
        Array.Copy(array, offset, result, 0, length);
        return result;
    }
    

    【讨论】:

    • LINQ 并不是解决所有问题的最佳解决方案,但在这种情况下,它仍然可以提供更具可读性的解决方案。
    • @StriplingWarrior:同意(另请参阅我对这个问题的其他答案)。但是,如果可读性是问题,那么没有什么是一些扩展方法无法解决的。 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-26
    • 2016-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-03
    相关资源
    最近更新 更多