使用Linq覆盖/加入两个集合

时间:2009-06-10 13:21:09

标签: c# linq collections

我有以下情况:

列表1有20个TItem类型的项目,列表2有5个相同类型的项目。列表1已包含列表2中的项目,但处于不同的状态。我想用列表2中的项目覆盖列表1中的5个项目。

我认为联接可能有效,但我想覆盖列表1中的项目,而不是将它们连接在一起并且有重复项。

有一个唯一的键可用于查找列表1中要覆盖哪些项,键的类型为int

4 个答案:

答案 0 :(得分:2)

你可以使用内置的Linq .Except()但是它想要一个IEqualityComparer,所以使用。a fluid version的.Except()代替。

假设一个对象带有一个整数键,如你所示:

public class Item
{
    public int Key { get; set; }
    public int Value { get; set; }
    public override string ToString()
    {
        return String.Format("{{{0}:{1}}}", Key, Value);
    }
}

原始的对象列表可以与更改的对象合并,如下所示:

IEnumerable<Item> original = new[] { 1, 2, 3, 4, 5 }.Select(x => new Item
    {
        Key = x,
        Value = x
    });
IEnumerable<Item> changed = new[] { 2, 3, 5 }.Select(x => new Item
    {
        Key = x,
        Value = x * x
    });

IEnumerable<Item> result = original.Except(changed, x => x.Key).Concat(changed);
result.ForEach(Console.WriteLine);

输出:

{1:1}
{4:4}
{2:4}
{3:9}
{5:25}

答案 1 :(得分:1)

LINQ不用于对底层数据源执行实际修改;它严格来说是一种查询语言。当然,您可以在List2List1进行外部联接,如果不是空,则选择List2的实体,如果是List1的实体,则选择IEnumerable<>这会给你一个ToList()的结果;它实际上不会修改集合。您可以对结果执行List1并将其分配给List1,但这会更改引用;我不知道这是否会影响你申请的其余部分。

从字面上理解您的问题,因为您希望将List2中的项目替换为for中的项目(如果存在),那么您必须在List1中手动执行此操作循环遍历List2,检查List1中是否存在相应的条目,并使用List2中的{{1}}条目替换{{1}}条目。

答案 2 :(得分:1)

正如亚当所说,LINQ是关于查询的。但是,您可以使用Enumerable.Union以正确的方式创建集合。你需要创建一个合适的IEqualityComparer - 拥有UnionBy会很高兴。 (也许是MoreLINQ的另一个?)

基本上:

var list3 = list2.Union(list1, keyComparer);

其中keyComparer是比较两个键的实现。 MiscUtil包含ProjectionEqualityComparer,这会使这更容易。

或者,您可以在连接后使用MoreLINQ中的DistinctBy

var list3 = list2.Concat(list1).DistinctBy(item => item.Key);

答案 3 :(得分:0)

这是GroupJoin的解决方案。

        List<string> source = new List<string>() { "1", "22", "333" };
        List<string> modifications = new List<string>() { "4", "555"};
          //alternate implementation
        //List<string> result = source.GroupJoin(
        //    modifications,
        //    s => s.Length,
        //    m => m.Length,
        //    (s, g) => g.Any() ? g.First() : s
        //).ToList();

        List<string> result =
        (
            from s in source
            join m in modifications
            on s.Length equals m.Length into g
            select g.Any() ? g.First() : s
        ).ToList();

        foreach (string s in result)
            Console.WriteLine(s);

嗯,我正在使用的可重用扩展方法怎么样:

    public static IEnumerable<T> UnionBy<T, U>
    (
        this IEnumerable<T> source,
        IEnumerable<T> otherSource,
        Func<T, U> selector
    )
    {
        return source.GroupJoin(
            otherSource,
            selector,
            selector,
            (s, g) => g.Any() ? g.First() : s
                );
    }

由以下人员召集:

List<string> result = source
  .UnionBy(modifications, s => s.Length)
  .ToList();