简体   繁体   English

形成两个对象列表的并集的最简单方法(包含 Int 和字符串值)

[英]Simplest way to form union of two Lists of objects (Containing Int and string values)

I saw a similar question here with a very good solutions: Simplest way to form a union of two lists我在这里看到了一个类似的问题,有一个很好的解决方案: Simplest way to form a union of两个列表

But the problem here is, it works when there is only one parameter in each list (int value).但这里的问题是,当每个列表中只有一个参数(int 值)时,它才起作用。 I had this rquirement to combine 5 different lists containing objects of the same class without data redundancy and the final list should be sorted out in ascending order of int value.我有这个要求来组合 5 个不同的列表,其中包含相同类的对象而没有数据冗余,最终列表应该按 int 值的升序排序。

Example:例子:

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

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

Class company has a pulic method returning list of companies corresponding to a search criteria, let us userID.类 company 有一个 pulic 方法返回与搜索条件相对应的公司列表,让我们使用 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
    }
}

}

**the code is converted from VB.Net to C#. **代码从VB.Net转换为C#。 Here I could not find the quivalent code piece for this line so replaced it with the concept.在这里,我找不到该行的等效代码段,因此将其替换为概念。

I am not an expert C# programmer and just wondering if there is any better or simpler way to do this?我不是 C# 专家,只是想知道是否有更好或更简单的方法来做到这一点?

Data example:数据示例:

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

Regards Sj问候 Sj

You can use either GroupBy or Union to remove duplicates... Union makes for a little cleaner linq (I think) but either can work... the downside is that you also need a custom IEqualityComparer in this case since equals on your company objects will return false (since they are different instances)... An alternative is to have your Company class implement IEqualityComparer and just copy the code I have implementing that interface into your Company class.您可以使用 GroupBy 或 Union 来删除重复项... Union 使 linq 更简洁(我认为)但两者都可以工作...缺点是在这种情况下您还需要一个自定义的 IEqualityComparer 因为您的公司对象上的 equals将返回 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();

Define your CompanyEqualityComparer...定义您的 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();
    }
}

Similar to Habib's solution, but a bit more concise and complete.类似于 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);

You might have a constructor in CompanyList that directly accepts an IEnumerable<Company> , too, so you could directly pass distinctCompanies there instead.您可能在 CompanyList 中也有一个直接接受IEnumerable<Company>的构造函数,因此您可以直接在那里传递distinctCompanies

Okay, so you have a number of sequences of something, in your case "something" would be Company , which doesn't overide object.Equals or object.HashCode .好的,所以你有一些东西的序列,在你的情况下,“东西”是Company ,它不会覆盖object.Equalsobject.HashCode

So, a new extension like this, might prove useful因此,像这样的新扩展可能会很有用

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

To make this, and other functions that take an IEqualityComparer simple to use, you could add this class to your code,为了使这个和其他使用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);
    }
}

Once these two, admittedly lengthy, bits of code were in place you could do一旦这两个,诚然冗长的代码就位,你就可以做

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

or if companyNo is a unique key,或者如果companyNo是唯一键,

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

would suffice.就足够了。

I think you need something like:我认为你需要这样的东西:

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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM