从一个列表中排除存在于另一个列表中的项目

3

我有一个列表,例如 List<string> ListProviderKeys,其中有一些值。 我还有另一个来自下面类的列表,例如 List<ChangesSummary> SecondList;

public class ChangesSummary
{
    public string TableName { get; set; }
    public string ProviderKey { get; set; }
    public string ProviderAdrsKey { get; set; }
    public string ProviderSpecialtyKey { get; set; }
    public string FieldName{ get; set; }
}

假设第一个列表中包含的值与我们放入第二个列表中的ProviderKey字段中的值相同。 现在我想要的是将第二个列表缩减为仅包含其ProviderKey不在第一个列表中的值。 我该怎么做?我知道有Except运算符,但不确定如何在这种情况下使用它!

1
有一个运算符Exclude,尝试使用它。 - Konstantin Zadiran
要使用Except,两个列表的值必须相同,因此您需要将其中一个的类型转换为另一个以使其按照您的条件应用Except。 - Zinov
4个回答

1
我能想到的最好方法是:
A)创建字典并使用其快速查找功能。
B)在此字典上使用LINQ的.Where方法和.ContainsKey(),它内部使用Hashtable并执行快速查找。这应该将搜索复杂度降至几乎O(1),而不是O(N)或更差(当我们使用带有.Any().Contains()的LINQ .Where()时,会导致嵌套循环)。
来自MSDN页面

Dictionary泛型类提供了从一组键到一组值的映射。每次添加到字典中都包括一个值及其关联的键。通过使用其键检索值非常快,接近于O(1),因为Dictionary类实现为哈希表。

所以我们可以做的是:
Dictionary<string, string> dict = ListProviderKeys.ToDictionary(s => s);

var newList = SecondList.Where(e => !dict.ContainsKey(e.ProviderKey)).ToList();

这是一个非常简单、短小却完整的例子,旨在说明它并测试其性能:
class Person
{
    public int Id { get; set; }        
}

class Program
{
    static void Main(string[] args)
    {
        List<int> ints = new List<int>();
        List<Person> People = new List<Person>(1000);

        for (int i = 0; i < 7500; i++)
        {
            ints.Add(i);
            ints.Add(15000 - i - 1);
        }

        for (int i = 0; i < 45000; i++)
            People.Add(new Person() { Id = i });

        Stopwatch s = new Stopwatch();

        s.Start();

        // code A (feel free to uncomment it)         
        //Dictionary<int, int> dict = ints.ToDictionary(p => p);

        //List<Person> newList = People.Where(p => !dict.ContainsKey(p.Id)).ToList();

        // code B
        List<Person> newList = People.Where(p => !ints.Contains(p.Id)).ToList();

        s.Stop();

        Console.WriteLine(s.ElapsedMilliseconds);
        Console.WriteLine("Number of elements " + newList.Count);

        Console.ReadKey();
    }

发布模式下的结果如下:
代码A和代码B输出了30,000个元素,但是:
使用代码B需要超过2000毫秒,而使用代码A只需要5毫秒。

这个意思是有道理的,但是在我运行的例子中它崩溃了。 - Bohn
List<string> firstList = new List<string>(); firstList.Add("fdsfd"); firstList.Add("sdfvf"); firstList.Add("P2"); firstList.Add("P4"); - Bohn
@Bohn 我提供了一个简短的例子。 - Fabjan
是的,现在可以了,谢谢。我自己的示例变量名称有复制粘贴问题 :) - Bohn
我应该稍微更新一下,使字符串比较不区分大小写。 - Bohn

0
 public class Programm
    {
        public static void Main()
        {
             List<ChangesSummary>  summaries = new List<ChangesSummary>();

             summaries.Add(new ChangesSummary()
             {
                 FieldName = "1",
                 ProviderKey = "Test1",
             });

             summaries.Add(new ChangesSummary()
             {
                 FieldName = "2",
                 ProviderKey = "Test2",
             });

             summaries.Add(new ChangesSummary()
             {
                 FieldName = "3",
                 ProviderKey = "Test3",
             });

            List<string> listProviderKeys = new List<string>();

            listProviderKeys.Add("Test1");
            listProviderKeys.Add("Test3");

            var res = summaries.Where(x => !listProviderKeys.Contains(x.ProviderKey));


            res.ToList().ForEach(x => Console.WriteLine(x.ProviderKey));

            Console.ReadLine();
        }
    }

    public class ChangesSummary
    {
        public string TableName { get; set; }
        public string ProviderKey { get; set; }
        public string ProviderAdrsKey { get; set; }
        public string ProviderSpecialtyKey { get; set; }
        public string FieldName { get; set; }
    }

你运行了这个例子吗?你的输入数据是什么? - Zinov
抱歉,应该可以工作的,我在使用这些代码时,在自己的示例中复制粘贴出现了错误。 - Bohn

-1

我认为在这种情况下,简单的Where语句更容易且更易读。

var first = new List<string> { "a" };
var second = new List<ChangesSummary>()
{
    new ChangesSummary() { ProviderKey = "a" },
    new ChangesSummary() { ProviderKey = "b" }
};

var result = second.Where(item => !first.Contains(item.ProviderKey));

// result
//    .ToList()
//    .ForEach(item => Console.WriteLine(item.ProviderKey));

抱歉,应该可以工作的,我在使用这些代码时,在自己的示例中复制粘贴出现了错误。 - Bohn

-2

我相信这个会起作用:

List<ChangesSummary> ExceptionList = SecondList.
Where(x => !ListProviderKeys.Any(key => x.ProviderKey == key)).ToList();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接