【问题标题】:Can LINQ be used to find gaps in a sorted list?可以使用 LINQ 查找排序列表中的空白吗?
【发布时间】:2010-11-24 18:39:33
【问题描述】:

我是否有可能以一种允许我确定“9”的方式使用 LINQ 是排序列表中的第一个缺失值,而不使用 for 循环并将每个值与其相邻的值进行比较?

var listStringVals = new [] { "7", "13", "8", "12", "10", "11", "14" };
// sort list to "7","8","10","11","12","13","14"
var sortedList = listStringVals.OrderBy(c => int.Parse(c)).ToList();
// need some magic here to get the first gap in the sorted list

【问题讨论】:

  • 只是好奇;为什么他们是string 而不是int 之类的?
  • 循环最终将成为用 C# 编写此代码的最简单、最清晰的方法。
  • @BeemerGuy - 它们是字符串,因为它们代表存储在数据库中的 ID 字符串的一部分。我只是在玩 LINQ,试图更好地了解它何时可以简化某些任务。

标签: c# .net linq sorting .net-3.5


【解决方案1】:

var strings = new string[] { "7", "13", "8", "12", "10", "11", "14" };

然后

var list = Array.ConvertAll(strings, s => Int32.Parse(s)).OrderBy(i => i);
// or
var list = strings.Select(s => int.Parse(s)).OrderBy(i => i);
// or
var list = strings.OrderBy(s => int.Parse(s));

(注意this问题)

然后

var result = Enumerable.Range(list.Min(), list.Count).Except(list).First(); // 9
// or
int min = list.Min(), max = list.Max();
var result = Enumerable.Range(min, max - min + 1).Except(list).First();

【讨论】:

  • 实际上,而不是list.Count,它应该与最后一项比较,哪个是最高值?如果只有 5 个项目,但最后一个项目是 1000,则不会返回所有间隙,仅返回 5 以内的间隙
  • @hunter;你是对的,这就是我在回答中所涵盖的内容,实际上 =)
  • @hunter - 我最初的问题只是找到第一个差距,但在其他情况下找到所有差距可能很有用。
  • @Nate... 这不像你想的那样工作。如果您的列表是{ 7, 8, 10, 1000 },结果将是11,而不是11 to 999
  • @Nate -- 只需将 list.Count 更改为 list.Max() - list.Min() + 1
【解决方案2】:

这是一种让您入门的方法(我在这里使用了int 值):

List<int> listStringVals = (new int[] { 7, 13, 8, 12, 10, 11, 14 }).ToList();
List<int> SortedList = listStringVals.OrderBy(c => c).ToList();
List<int> Gaps = Enumerable.Range(SortedList.First(), 
                                  SortedList.Last() - SortedList.First() + 1)
                           .Except(SortedList).ToList();

【讨论】:

  • 太好了,感谢您展示了一种消除所有差距的好方法。这篇文章产生了一些很棒的答案,很遗憾我只能标记一个作为答案。谢谢大家!
  • +1 用于在开始时编写正确的Range 计算!
【解决方案3】:
var listStringVals = new string[] {"7", "13", "8", "12", "10", "11", "14"};
var sortedInts = listStringVals.Select(c => int.Parse(c)).OrderBy(x => x);
var noGaps = Enumerable.Range(sortedInts.First(), 
                              sortedInts.Last() - sortedInts.First() + 1);
var missing = noGaps.Except(sortedInts).Select(x => x.ToString()).First();

编辑:通过BeemerGuy's answer 生成固定范围。仍然离开我的,因为它不会忽略 string 表示 ints 的列表的丑陋 :)

【讨论】:

    【解决方案4】:

    (abatishchev 击败了我,但他的答案还是更好。但是,由于同一问题的替代解决方案可能很有趣,所以我仍然发布此内容。)

    Hackity 黑客攻击。但是工作,如果你真的想要这样做。性能会很糟糕,因为这种技术在找到答案时不会停止——它总是会遍历每个数字!但它会起作用:

    public static int FindFirstMissing(IEnumerable<int> sequence)
    {
        bool found = false;
    
        int agg = sequence.Aggregate((aggregate, next) => {
            if (found)
                return aggregate;
    
            if (next - aggregate != 1) {
                found = true;
                return aggregate + 1;
            }
    
            return next;
        });
    
        if (!found)
            throw new ArgumentException("sequence", "Contains no missing numbers.");
    
        return agg;
    }
    

    【讨论】:

    • 真的很酷!我认为Aggregate() 是该系列中最强大的工具。但同时有点复杂。
    • 对于这样的情况效率不高。 ;)
    • +1 表示一种新方法,我很长时间没有审查对这个问题的回答,但这个很有趣。感谢所有花时间发布的人,我很感激时间和观点。
    【解决方案5】:
    string firstGap = sortedList
        .Zip(sortedList.Skip(1), (f, s) => Tuple.Create(f, s))
        .First(tup => (int.Parse(tup.Item1) + 1) != int.Parse(tup.Item2)).Item1;
    

    应该给你第一个gap之前的第一个item,所以第一个缺失的元素是:

    string gap = (int.Parse(firstGap) + 1).ToString();
    

    【讨论】:

    • 非常有创意的解决问题的方法,我真的很喜欢阅读所有的答案!
    • @jball - 它在列表中的项目和以下项目之间创建一系列对,例如("7","8"), ("8","10)...("11","14")。第三行查找第一个元素不小于第二个元素的第一个元组.
    • 感谢您的解释(很抱歉删除了我的评论)-过去 20 分钟我一直在玩它,当我注意到您的评论时,我实际上只是摸索它做了什么。
    • 我最喜欢这个解决方案,因为它会找到实际的差距而不是列表中没有的数字。 var gaps = a.Zip(a.Skip(1), (l, r) =&gt; new[] { l + 1, r - 1 }).Where(p =&gt; p[0] &lt;= p[1])
    【解决方案6】:

    有点晚了,但我认为这是一种很酷的方式:

    List<int> listStringVals = (new int[] { 7, 13, 8, 12, 10, 11, 14 }).ToList();
                listStringVals.Sort();
                return listStringVals.Skip(1).Select((x, i) => x - listStringVals[i] == 1).Any(x => !x);
    

    【讨论】:

    • 我喜欢看到解决同一问题的不同方法。感谢您的发布,即使它有点晚了。花时间去探索所有不同的答案已经得到了回报。再次感谢所有花时间在此线程上发帖的人。
    【解决方案7】:

    为什么不直接使用All,因为集合中的所有成员都需要符合标准...

    例子

    someVar.All(v =&gt; someVar.Contains(v + 1) || v == someVar.Last())

    那么您不必订购,而且更好。

    如果需要,您可以在此步骤之后或什至在此期间进行排序,但我个人只会使用排序后的集合并让它为我完成这项工作。

    如果需要,您可以在检查后获取值,然后返回检查结果,或者如果您出于某种原因想要通过上面的多行修改以及存储值的列表来隐藏它。

    例如

    someVar.All((v) => { 
        bool result = someVar.Contains(v + 1) || v == someVar.Last();
        if(!result) someList.Add(v);
        return true;
    });
    

    检查列表(可以排序)的计数是否为非零值,以指示它是否满足。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多