【问题标题】:Simplest way to form union of two Lists of objects (Containing Int and string values)形成两个对象列表联合的最简单方法(包含 Int 和字符串值)
【发布时间】:2014-03-27 13:58:36
【问题描述】:

我在这里看到了一个类似的问题,有一个很好的解决方案: Simplest way to form a union of two lists

但这里的问题是,当每个列表中只有一个参数(int 值)时,它可以工作。我有这个要求来组合 5 个不同的列表,其中包含同一类的对象,没有数据冗余,最终列表应该按 int 值的升序排序。

例子:

Class Company   //data Class
{
int companyNo;
string Name;
}

Class CompanyList : List<Company>
{
  .................
  public CompanyList GetList(int userID)
  {
    .....
  }
}

class company 有一个公共方法返回与搜索条件相对应的公司列表,让我们使用 userID。

CompanyList list1 = CompanyList .GetList(userID1);
CompanyList list2 = CompanyList .GetList(userID2);
CompanyList list3 = CompanyList .GetList(userID3);
CompanyList list4 = CompanyList .GetList(userID4);
CompanyList list5 = CompanyList .GetList(userID5);

The solution I implemented is (worked well):

CompanyList _finalList = list1;
*foreach (CompanyList _list in {_list2 ,_list3 ,_list4 ,_list5}) //loop thorugh all other list
{
   foreach (Company item in _list)
   {
      for (int i = 0; i <= _finalList.Count - 1; i++) 
      {
         if (_finalList.Item(i).CompanyNo== item.CompanyNo)
         //***EXIT TAKE NEXT LIST - GO TO *
      }
      if (i == _finalList.Count - 1)  //else check end of first list
      {
        //company no. not yet encountered(new)
        int pos = 0;
        foreach (Company companyInfo in _finalList) //search for position for new company no.
        {
          if (companyInfo.CompanyNo> item.CompanyNo) 
          {
             break;
          } 
          else 
          {
             pos = pos + 1; //increment position
           }
       }
      _finalList.Insert(pos, item); 'Add new item
    }
}

}

**代码从 VB.Net 转换为 C#。在这里我找不到此行的等效代码段,因此将其替换为概念。

我不是专业的 C# 程序员,只是想知道是否有更好或更简单的方法来做到这一点?

数据示例:

Input:
list1[0] = {0,"TCS"};
list1[1] = {1,"Infosys"};
list2[0] = {8,"IBM"};
list3[1] = {1,"Infosys"};
list4[0] = {0,"TCS"};
list5[0] = {9,"Accenture"};
list5[1] = {6,"HCL"};

Output:
finalList[0] = {0,"TCS"};
finalList[1] = {1,"Infosys"};
finalList[2] = {6,"HCL"};
finalList[3] = {8,"IBM"};
finalList[4] = {9,"Accenture"};

问候 sj

【问题讨论】:

    标签: c# vb.net visual-studio-2010 for-loop foreach


    【解决方案1】:

    好的,所以你有一些东西的序列,在你的情况下,“东西”是Company,它不会覆盖object.Equalsobject.HashCode

    所以,像这样的新扩展可能会很有用

    public static IEnumerable<T> Union(
        this IEnumerable<T> source,
        IEqualityComparer<T> comparer,
        params IEnumerable<T>[] others)
    {
        if (comparer == null)
        {
            comparer = EqualityComparer<T>.Default;
        }
    
        var result = source.Distinct(comparer); 
        foreach(var o in source)
        {
            if (o == null)
            {
                continue;
            }
    
            result = result.Union(o, comparer);
        }
    
        return result;
    }
    

    要使这个以及其他采用IEqualityComparer 的函数易于使用,您可以将此类添加到您的代码中,

    public class EqualityComparerImproved<T> : EqaulityComparer<T>
    {
        private readonly Func<T, T> equalityComparison;
    
        private readonly Func<T, int> hashGenerator;
    
        private EqualityComparerImproved(
                 Func<T, T> equalityComparison,
                 Func<T, int> hashGenerator)
        {
            this.equalityComparison = equalityComparison;
            this.hashGenerator = hashGenerator;
        }
    
        public static EqualityComparerImproved<T> Create
                 Func<T, T> equalityComparison,
                 Func<T, int> hashGenerator)
        {
             if (equalityComparison == null)
             {
                 throw new ArgumentNullException("equalityComparison");
             }
    
             if (hashGenerator == null)
             {
                 throw new ArgumentNullException("hashGenerator");
             }
    
             return new EqualityComparerImproved<T>(
                 equalityComparison,
                 hashGenerator);
        }
    
        public override bool Equals(T x, T y)
        {
            return this.equalityComparison(x, y);
        }
    
        public override int GetHashCode(T obj)
        {
            return this.hashGenerator(obj);
        }
    }
    

    一旦这两个(公认的冗长)代码到位,您就可以做

    var output = list1.Union(
        EqualityComparerImproved<Company>.Create(
            (x, y) => x.companyNo == y.companyNo && x.Name == y.Name,
            (obj) =>
                {
                    unchecked // Overflow is fine, just wrap
                    {
                        int hash = 17;
                        hash = hash * 23 + obj.companyNo;
                        hash = hash * 23 + obj.Name.GetHashCode();
                        return hash;
                    }
                },
        list2,
        list3,
        list4,
        list5);
    

    或者如果companyNo 是唯一键,

    var output = list1.Union(
        EqualityComparerImproved<Company>.Create(
            (x, y) => x.companyNo == y.companyNo,
            (obj) => obj.companyNo),
        list2,
        list3,
        list4,
        list5);
    

    足够了。

    【讨论】:

    • +1 不错!我真的很喜欢通用的 IEqualityComparer!它确实使答案有点长,但非常完整。 :)
    • 感谢 Jodrell.. 经过一些额外的努力,它工作了(因为实际上数据类要复杂得多)。 +1!!
    【解决方案2】:

    类似于 Habib 的解决方案,但更简洁和完整。

    int[] userIDs = new[] { userID1, userID2, userID3, userID4, userID5 };
    
    IEnumerable<Company> distinctCompanies =
        from companyList in userIDs.Select(CompanyList.GetList)
        from company in companyList
        group company by company.companyNo into companiesWithSameNo
        select companiesWithSameNo.First();
    
    CompanyList finalList = new CompanyList();
    finalList.AddRange(distinctCompanies);
    

    您可能在 CompanyList 中有一个构造函数,它也直接接受 IEnumerable&lt;Company&gt;,因此您可以直接在此处传递 distinctCompanies

    【讨论】:

      【解决方案3】:

      您可以使用 GroupBy 或 Union 来删除重复项... Union 可以使 linq 更简洁(我认为),但两者都可以工作...缺点是在这种情况下您还需要一个自定义 IEqualityComparer 因为等于 on您的公司对象将返回 false(因为它们是不同的实例)...另一种方法是让您的 Company 类实现 IEqualityComparer 并将我实现该接口的代码复制到您的 Company 类中。

      // Union gives you a unique list if it knows how to compare the objects properly
      var companyEqualityComparer = new CompanyEqualityComparer();
      foreach (var companyList in new List<List<Company>>(){list2, list3, list4, list5})
      {
          combinedList = combinedList.Union(companyList, companyEqualityComparer);
      }
      // Order your output list
      var finalList = combinedList.OrderBy(c => c.companyNo).ToList();
      

      定义您的 CompanyEqualityComparer...

      // CompanyEqualityComparer which is needed since your companies are different instances
      public class CompanyEqualityComparer : IEqualityComparer<Company>
      {
          public bool Equals(Company x, Company y)
          {
              return x.companyNo.Equals(y.companyNo);
          }
          public int GetHashCode(Company obj)
          {
              return obj.companyNo.GetHashCode();
          }
      }
      

      【讨论】:

      • 我在我的答案中包含了一个通用的EqualityComparerImproved&lt;T&gt; 类,它有一个Create 方法,该方法需要适当的代表返回一个IEqualityComparer&lt;T&gt;
      【解决方案4】:

      我认为你需要类似的东西:

      List<Company> inputList = //Get your input List
      List<Company> outputList = inputList.GroupBy(r => r.companyNo)
                                          .Select(grp => new Company
                                              {
                                                  companyNo = grp.Key,
                                                  Name = grp.First().Name,
                                              })
                                          .OrderBy(r=> r.companyNo)
                                          .ToList();
      

      【讨论】:

        猜你喜欢
        • 2012-11-10
        • 2013-11-12
        • 2011-10-29
        • 1970-01-01
        • 1970-01-01
        • 2011-09-19
        • 2020-06-15
        • 1970-01-01
        • 2013-08-18
        相关资源
        最近更新 更多