【问题标题】:LINQ List Contains Another List (Contiguous)LINQ 列表包含另一个列表(连续)
【发布时间】:2012-05-31 13:40:45
【问题描述】:

我很难找到一种有效但简单的方法来检查一个列表是否包含另一个列表(保留顺序)。它类似于 string.Contains(string) 功能。

假设我有四个整数集合:

 A = [1, 2, 3, 4, 5]
 B = [2, 3]
 C = [5, 6, 7]
 D = [3, 2, 4]

A.Contains(B) 为真,而A.Contains(C)A.Contains(D) 为假。

如果有帮助的话,我宁愿不使用迭代器,但我无法想象一种有效的方法;以下代码效率极低。

 public static bool IsSequentiallyEqual<T>(this IEnumerable<T> lhs, IEnumerable<T> rhs)
 {
      return lhs.Zip(rhs, (a, b) => a.Equals(b)).All(isEqual => isEqual == true);
 }

 public static bool StartsWith<T>(this IEnumerable<T> haystack, IEnumerable<T> needle)
 {
      return haystack.Take(needle.Count()).IsSequentiallyEqual(needle);
 }

 public static bool Contains<T>(this IEnumerable<T> haystack, IEnumerable<T> needle)
 {
      var result = list.SkipWhile((ele, index) => haystack.Skip(index).StartsWith(needle));
      return result.Count() >= needle.Count();
 }

【问题讨论】:

  • 您将拥有多少件商品? (也就是说,效率是关键,还是你只是想要一些不是很低效的东西?)
  • 只需要效率是不够的,但会很好
  • 有没有办法通过 LINQ 干净地实现这些?
  • 干净利落的意思是“没有用所需的逻辑把源代码弄得乱七八糟?”

标签: linq contains


【解决方案1】:

使用散列的工作。请注意,可以进行一些检查以立即返回错误,但我只展示了该过程的实质。这是方便的扩展格式:

更新为处理订单

void Main()
{
    var first        = new List<int>() { 1, 2, 5 };
    var firstInOrder = new List<int>() { 1, 2, 3 };
    var second       = new List<int>() { 1, 2, 3, 4, 5 };
    var third        = new List<int>() { 1, 10, 20 };

    Console.WriteLine( first.FoundInOther( second ) );        // False
    Console.WriteLine( firstInOrder.FoundInOther( second ) ); // True
    Console.WriteLine( first.FoundInOther( third ) );         // False

}

public static class NumberExtensions
{

    public static bool FoundInOther( this IEnumerable<int> initial, IEnumerable<int> other )
    {
        int index = -1;
        var asDictionary = other.ToDictionary( itm => itm, itm => ++index );

        index = -1;
        return initial.All( oth => asDictionary.ContainsKey( oth ) && (asDictionary[oth] == ++index));
    }

}

【讨论】:

  • 尝试使用var fourth = new List&lt;int&gt;() { 5, 2 }; 你的方法返回true,而我希望它返回false(顺序很重要)。
  • @hehewaffles 完成参见示例。只需将索引放入 KVP 的容器中即可。
  • 它仍然只测试序列的开头。例如,{2, 3} 应在 {1, 2, 3, 4, 5} 中找到。
【解决方案2】:

此版本使用队列来存储可能的子序列。除了最初的Take() 之外,它只迭代一次haystack,一旦找到匹配项就会停止迭代。但是,它会改变 LINQ 语句中的变量。

public static bool Contains<T>(this IEnumerable<T> haystack, IEnumerable<T> needle)
{
    var needleList = needle.ToList();
    var queue = new Queue<T>(haystack.Take(needleList.Count - 1));
    return haystack.Skip(needleList.Count - 1)
                   .Any( hay =>   
                       {
                           queue.Enqueue(hay);
                           bool areEqual = queue.SequenceEqual(needleList);
                           queue.Dequeue();
                           return areEqual;
                       });  
}

【讨论】:

    【解决方案3】:
    public static bool Contains<T>(this IEnumerable<T> haystack, IEnumerable<T> needle)
    {
        var hayList = haystack.ToList();
        var needleList = needle.ToList();
        return Enumerable.Range(0, hayList.Count)
                         .Select(start => hayList.Skip(start).Take(needleList.Count))
                         .Any( subsequence => subsequence.SequenceEqual(needleList));
    }
    

    【讨论】:

    • 仍然是 O(N^2) 但我非常喜欢这个
    【解决方案4】:
    public static bool Contains<T>(this IEnumerable<T> first, IEnumerable<T> second)
     {
          return string.Join("~", first).Contains(string.Join("~", second));
     }
    

    少一点“笨拙”,至少避免为长长的列表做一些工作。

    public static bool Contains<T>(this IEnumerable<T> first, IEnumerable<T> second)
       {
           //trying to avoid multiple enumeration
            var firstList = first.ToList();
            var secondList = second.ToList();
    
            if (!secondList.Any(firstList.Contains)) return false;
            if (secondList.Count() > firstList.Count()) return false;
            if (Math.Max(firstList.Count(), secondList.Count()) > 99999)
                 throw new ShouldNotUseThisUglyMethodException("I'm too kludgy to be used. Let me die...");
            return string.Join("~", firstList).Contains(string.Join("~", secondList));
        }
    

    【讨论】:

    • 如果你不想比这更笨拙,请在列表中使用.ToArray(),然后使用与 String.Contains() 类似的算法;)
    猜你喜欢
    • 1970-01-01
    • 2011-04-20
    • 1970-01-01
    • 1970-01-01
    • 2013-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多