使用 LINQ-to-Objects 有条件地合并两个集合

发布于 2025-01-02 15:05:12 字数 570 浏览 4 评论 0原文

我需要帮助找到使用 LINQ-to-Objects 合并两个内存中集合的最有效方法。就我而言,这不是简单的连接或连接,因为需要根据属性值排除某些项目。

让我们使用以下示例(不太现实,但它说明了我的需要):

public abstract class Person
{
    public String Name { get; set; }
}

public class Employee : Person
{
    public DateTime? TerminationDate { get; set; }
}

public class Customer : Person
{
    ...
}

注意,员工也可能是客户。

我有一个员工列表和一个客户列表,并希望生成遵循以下规则的所有人员的列表:

  1. 所有不属于员工的客户
  2. 所有不属于客户且未终止的员工 (TerminationDate == null)
  3. 所有人员既是客户又是员工并且没有终止 (TerminationDate == null)

实现此目的最有效的方法是什么?

I need help finding the most efficient way to merge two in-memory collections with LINQ-to-Objects. In my case, it's not a simple join or concat because certain items need to be excluded based on a property value.

Let's use the following example (not very realistic, but it illustrates what I need):

public abstract class Person
{
    public String Name { get; set; }
}

public class Employee : Person
{
    public DateTime? TerminationDate { get; set; }
}

public class Customer : Person
{
    ...
}

Note, an Employee may also be a Customer.

I have a list of Employees and a list of Customers and want to generate a list of all Persons following these rules:

  1. All Customers that are not also Employees
  2. All Employees that are not also Customer AND are not terminated (TerminationDate == null)
  3. All Persons that are both Customers and Employees AND are not terminated (TerminationDate == null)

What's the most efficient way to accomplish this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

岁月打碎记忆 2025-01-09 15:05:12

很难确定,但假设乔恩对姓名匹配的猜测是正确的,那么这在概念上有意义吗?

请注意,它当前执行多次迭代,因此我通常会在某些地方添加 ToArray 等,但我不想在尝试验证概念位时将其添加到此代码中。

var allPeople = ...;

var customers = allPeople.OfType<Customer>();
var employees = allPeople.OfType<Employee>();

var employeeNames = new HashSet(employees.Select(p => p.Name));
var customerNames = new HashSet(employees.Select(p => p.Name));

// list item #1
var customersNotEmployees = customers
    .Where(c => employeeNames.Contains(c.Name) == false);

// will be used by #2 and #3
var employeesNotTerminated = employees
    .Where(e => e.TerminationDate == null);

// list item #2
var employeesNotCustomersAndNotTerminated = employeesNotTerminated
    .Where(e => customerNames.Contains(e.Name) == false);

// list item #3
var employeesAndCustomersAndNotTerminated = employeesNotTerminated
    .Where(e => customerNames.Contains(e.Name) == true);

Hard to tell for sure, but assuming Jon's guess of name-matching is right, then does this make conceptual sense?

Note that it currently does multiple iteration, so I'd normally add ToArray or the like in some places, but I didn't want to add that into this code while trying to validate the conceptual bit.

var allPeople = ...;

var customers = allPeople.OfType<Customer>();
var employees = allPeople.OfType<Employee>();

var employeeNames = new HashSet(employees.Select(p => p.Name));
var customerNames = new HashSet(employees.Select(p => p.Name));

// list item #1
var customersNotEmployees = customers
    .Where(c => employeeNames.Contains(c.Name) == false);

// will be used by #2 and #3
var employeesNotTerminated = employees
    .Where(e => e.TerminationDate == null);

// list item #2
var employeesNotCustomersAndNotTerminated = employeesNotTerminated
    .Where(e => customerNames.Contains(e.Name) == false);

// list item #3
var employeesAndCustomersAndNotTerminated = employeesNotTerminated
    .Where(e => customerNames.Contains(e.Name) == true);
肤浅与狂妄 2025-01-09 15:05:12

虽然不一定不正确,但詹姆斯的示例比我希望的要复杂一些,并且不完整,因为我需要一个列表作为最终结果。

也不确定这是否是最好的解决方案,但这是我迄今为止想到的:

public abstract class Person
{
    public String Name { get; set; }

    public override Boolean Equals(Object other)
    {
        var otherPerson = other as Person;

        if (otherPerson == null)
            return false;

        return Name.Equals(otherPerson.Name, StringComparison.OrdinalIgnoreCase);
    }
}

public class Employee : Person
{
    public DateTime? TerminationDate { get; set; }
}

public class Customer : Person
{
    ...
}


public class People
{
    private IEnumerable<Customer> Customers;
    private IEnumerable<Employee> Employees;

    public IEnumerable<Person> GetCurrentPeople()
    {
        // Find all customers that are not employees
        var onlyCustomers = Customers.Except(Employees);

        // We only want employees that have not been terminated
        var currentEmployees = Employees.Where(e => e.TerminationDate == null);

        // Find all customers that are also employees
        var customerAndEmployees = currentEmployees.Intersect(Customers);

        // Now deal with employees that are not customers
        var onlyEmployees = currentEmployees.Except(Customers);

        // Join the lists together
        var mergedRules = onlyCustomers.Concat(onlyEmployees).Concat(customerAndEmployees);

        return mergedRules;
    }
}

While not necessarily incorrect, James' example is a bit more complicated that I was hoping for and is incomplete as I need a single list as the end result.

Not sure this is the best solution, either, but here's what I came up with so far:

public abstract class Person
{
    public String Name { get; set; }

    public override Boolean Equals(Object other)
    {
        var otherPerson = other as Person;

        if (otherPerson == null)
            return false;

        return Name.Equals(otherPerson.Name, StringComparison.OrdinalIgnoreCase);
    }
}

public class Employee : Person
{
    public DateTime? TerminationDate { get; set; }
}

public class Customer : Person
{
    ...
}


public class People
{
    private IEnumerable<Customer> Customers;
    private IEnumerable<Employee> Employees;

    public IEnumerable<Person> GetCurrentPeople()
    {
        // Find all customers that are not employees
        var onlyCustomers = Customers.Except(Employees);

        // We only want employees that have not been terminated
        var currentEmployees = Employees.Where(e => e.TerminationDate == null);

        // Find all customers that are also employees
        var customerAndEmployees = currentEmployees.Intersect(Customers);

        // Now deal with employees that are not customers
        var onlyEmployees = currentEmployees.Except(Customers);

        // Join the lists together
        var mergedRules = onlyCustomers.Concat(onlyEmployees).Concat(customerAndEmployees);

        return mergedRules;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文