有条件地将两个集合与LINQ-to-Objects合并

时间:2012-02-02 15:55:42

标签: .net linq collections linq-to-objects

我需要帮助找到将两个内存中集合与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)
  4. 实现这一目标的最有效方法是什么?

2 个答案:

答案 0 :(得分:0)

很难确定,但假设Jon猜测名称匹配是正确的,那么这是否具有概念意义?

请注意,它目前进行多次迭代,所以我通常在某些地方添加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);

答案 1 :(得分:0)

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

不确定这是最好的解决方案,但这是我到目前为止所提出的:

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;
    }
}