【问题标题】:How can I upsert an enumerable from another enumerable c#如何从另一个可枚举的 c# 中插入可枚举
【发布时间】:2013-04-19 01:41:45
【问题描述】:

我浏览了这个网站上的问题,但没有找到与我的特定问题相匹配的问题。

假设我有以下几点:

Product[] store1 = { new Product { Name = "apple", Code = 9, Code1="1" }, 
                   new Product { Name = "orange", Code = 4 } };

Product[] store2 = { new Product { Name = "apple", Code = 9, Code2="2" }, 
                   new Product { Name = "lemon", Code = 12 } };

与:

public class Product : IEquatable<Product>
{
    public string Name { get; set; }
    public int Code { get; set; }
    public string Code1 { get; set; }
    public string Code2 { get; set; }

    public bool Equals(Product other)
    {

        //Check whether the compared object is null. 
        if (Object.ReferenceEquals(other, null)) return false;

        //Check whether the compared object references the same data. 
        if (Object.ReferenceEquals(this, other)) return true;

        //Check whether the products' properties are equal. 
        return Code.Equals(other.Code) && Name.Equals(other.Name);
    }

    // If Equals() returns true for a pair of objects  
    // then GetHashCode() must return the same value for these objects. 

    public override int GetHashCode()
    {

        //Get hash code for the Name field if it is not null. 
        int hashProductName = Name == null ? 0 : Name.GetHashCode();

        //Get hash code for the Code field. 
        int hashProductCode = Code.GetHashCode();

        //Calculate the hash code for the product. 
        return hashProductName ^ hashProductCode;
    }
}

如何返回单个 Enumerable,其中 store1 中的数据在匹配时被 store2 中的数据覆盖,并且在不匹配时仅从 store2 插入到 store1 中。基本上,我正在寻找与 TSQL Merge 语句等效的 C#。

在一天结束的时候运行这个:

foreach (var product in union)
      Console.WriteLine(product.Name + " " + product.Code + " " + product.Code1 + " " + product.Code2);

我想回来:

苹果 9 1 2

橙色 4

柠檬 12

但是当我运行这个时:

IEnumerable<Product> union = store1.Union(store2);

我明白了:

苹果 9 1

橙色 4

柠檬 12

当我运行这个时:

IEnumerable<Product> union = store1.Concat(store2);

我明白了:

苹果 9 1

橙色 4

苹果 9 2

柠檬 12

提前感谢您的帮助。

【问题讨论】:

  • 你要覆盖数据的键值是什么?
  • 这只是取自 MSDN 上的 article 的一个例子,但如果我在现实生活中使用这个例子,我喜欢匹配名称和代码,然后覆盖 code1 和 code2,如果他们store1 中为空白。
  • @user1031517 当你有{ Name = "apple", Code = 9, Code1 = "1" }{ Name = "apple", Code = 9, Code1 = "2", Code2 = "2" } 时会发生什么,在这种情况下应该考虑哪个?这里Code1 属性在两个项目中重叠。当我们在两个系列中都有许多这样的重叠产品时会发生什么,那么所有这些都应该被考虑?像{ Name = "apple", Code = 9, Code1 = "1" }{ Name = "apple", Code = 9, Code2 = "2" } 这样的案例是否会出现在同一个集合中,例如,在store1 中?

标签: c# linq merge ienumerable


【解决方案1】:
        //
        // Summary:
        //     Produces the set union of two sequences by using the default equality comparer.
        //
        // Parameters:
        //   first:
        //     An System.Collections.Generic.IEnumerable<T> whose distinct elements form
        //     the first set for the union.
        //
        //   second:
        //     An System.Collections.Generic.IEnumerable<T> whose distinct elements form
        //     the second set for the union.
        //
        // Type parameters:
        //   TSource:
        //     The type of the elements of the input sequences.
        //
        // Returns:
        //     An System.Collections.Generic.IEnumerable<T> that contains the elements from
        //     both input sequences, excluding duplicates.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     first or second is null.
public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second);

因为功能

排除重复项。

所以你必须为 Product[] 编写联合函数

public static class ProductExtension
{
    public static IEnumerable<T> Union<T>(this IEnumerable<T> store1, IEnumerable<T> store2)
    {
        List<T> List = new List<T>();
        foreach (var item in store2)
        {
            if (store1.Any(n=>n.Equals(item)))
            {
                var obj = store1.First(n => n.Equals(item));
                foreach (System.Reflection.PropertyInfo pi in obj.GetType().GetProperties())
                {
                    object v1 = pi.GetValue(obj, null);
                    object v2 = pi.GetValue(item, null);
                    var value = v1;
                    if (v2 != null && (v1 == null || v1.ToString() == string.Empty) && v1 != v2)
                    {
                        value = v2;
                    }
                    pi.SetValue(obj, value, null);
                }
                List.Add(obj);
            }
            else {
                List.Add(item);
            }            
        }

        foreach (var item in store1) { 
            if(!store2.Any(n=>n.Equals(item))){
                List.Add(item);
            }
        }
        return List.AsEnumerable();
    }
}

【讨论】:

    【解决方案2】:

    我会在Product 类中为它编写自己的功能,而不是依赖反射。类似于AsComboCombineWith 方法:

    public IEnumerable<Product> AsCombo(Product p)
    {
        //if not equal, return both
        if (!Equals(p))
        {
            yield return this;
            yield return p;
            yield break;
        }
    
        //if equal return the one desired by checking all properties
        yield return new Product //always better to return new instance for linq queries
        { 
            Name = Name, 
            Code = Code, 
            Code1 = Code1 ?? p.Code1, //I give preference to 'this'
            Code2 = Code2 ?? p.Code2  //I give preference to 'this'
        };
    }
    

    现在您的所有标准 Linq 查询都应该可以工作了。

    var combo = store1.Concat(store2)
                      .GroupBy(x => x)
                      .Where(x => x.Count() == 1)
                      .Select(x => x.Key) //get non duplicated products
                      .Concat(store1.Distinct() //concat them with combined form of duplicated products
                                    .Join(store2.Distinct(), x => x, x => x, (x, y) => x.AsCombo(y))
                                    .SelectMany(x => x))
                      .ToList();
    

    使用下面的查询会更容易一些,但这依赖于Union 的实现(假设Union 保留第一个或外部序列的重复项,并丢弃后续/内部序列的重复项)。不推荐。

    var combo = store1.Join(store2, x => x, x => x, (x, y) => x.AsCombo(y))
                      .SelectMany(x => x)
                      .Union(store1)
                      .Union(store2)
                      .ToList();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-14
      • 2010-09-11
      • 1970-01-01
      • 2017-04-01
      • 2021-01-04
      相关资源
      最近更新 更多