0

ベースコードを持つ人のリストと場所の配列があります。リスト内の同じ場所でベースコードが異なる人を排除し、場所が異なる人を保持する必要があります。

IEqualityComparer を使用して、linq でグループ化しようとしましたが、成功しませんでした。どうすればよいか教えてください。これは私のクラス構造です

public class Person
{
    public string Name { get; set; }

    public List<Location> Locations { get; set; }
}
public class Location
{
    public string Name { get; set; }
    public string BaseCode { get; set; }
}

データ例

Person 1
Name : John

Locations :
      [0]  Name : India , BaseCode : "AA12"
      [1] Name : USA ,BaseCode : "AA14"
Person 2
Name : John

Locations :
      [0]  Name : India, BaseCode : "AA13"
      [1] Name : USA ,BaseCode : "AA14"
Person 3
Name : John

Locations :
      [0]  Name : India, BaseCode : "AA16"
      [1] Name : UK , BaseCode : "AA17"

リストから Person 2 を除外し、Person 1 と Person 3 を保持したいのですが、アドバイスをお願いします

4

4 に答える 4

1

免責事項:BaseCodeこのソリューションは、異なる/同じ場所で同じことを具体的に処理しません。あなたの要件では、これについて何も言及していません。


IEqualityComparer<T>ルート

ここで重要な部分は、 と の両方の実装IEqualityComparer<T>です。PersonLocation

class Program
{
    static void Main(string[] args)
    {
        var p1 = new Person {Name ="John", BaseCode="AA12", Locations = new List<Location>
        {
            new Location { Name = "India" },
            new Location { Name = "USA" }
        }};

        var p2 = new Person {Name ="John", BaseCode="AA13", Locations = new List<Location>
        {
            new Location { Name = "India" },
            new Location { Name = "USA" }
        }};

        var p3 = new Person {Name ="John", BaseCode="AA14", Locations = new List<Location>
        {
            new Location { Name = "India" },
            new Location { Name = "UK" }
        }};

        var persons = new List<Person> { p1, p2, p3 };

        // Will not return p2.
        var distinctPersons = persons.Distinct(new PersonComparer()).ToList();

        Console.ReadLine();
    }
}

public class PersonComparer : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        if (x == null || y == null)
            return false;

        bool samePerson = x.Name == y.Name;

        bool sameLocations = !x.Locations
            .Except(y.Locations, new LocationComparer())
            .Any();

        return samePerson && sameLocations;
    }

    public int GetHashCode(Person obj)
    {
        return obj.Name.GetHashCode();
    }
}

public class LocationComparer : IEqualityComparer<Location>
{
    public bool Equals(Location x, Location y)
    {
        if (x == null || y == null)
            return false;

        return x.Name == y.Name;
    }

    public int GetHashCode(Location obj)
    {
        return obj.Name.GetHashCode();
    }
}

は、 を提供PersonComparerする linqExcept拡張機能を使用してLocationComparer、2 つの場所のリスト間の違いのリストを生成します。

次にPersonComparer、linqDistinctメソッドにフィードします。


IEquatable<T>ルート

BaseCode「一致」するために異なるカウントで作業する必要がある場合GetHashCode、値を区別する機会が与えられないため、このルートは機能しないと思います.

別の解決策はIEquatable<T>、クラス自体に実装し、さらにオーバーライドして、GetHashCodeこの実装を尊重することです。DistinctExcept

public class Person : IEquatable<Person>
{
    public string Name { get; set; }
    public string BaseCode { get; set; }
    public List<Location> Locations { get; set; }

    public bool Equals(Person other)
    {
        if (other == null)
            return false;

        bool samePerson = Name == other.Name;

        // This is simpler because of IEquatable<Location>
        bool sameLocations = !Locations.Except(other.Locations).Any();

        return samePerson && sameLocations;
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }
}

public class Location : IEquatable<Location>
{
    public string Name { get; set; }

    public bool Equals(Location other)
    {
        if (other == null)
            return false;

        return Name == other.Name;
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }
}

これにより、呼び出しがより簡単になります。

var distinctPersons = persons.Distinct().ToList();
于 2012-07-10T13:34:46.173 に答える
0

IEquatable インターフェイスを利用して、次のように Equal および GetHashCode メソッドをオーバーライドできます。

質問の変更後に編集:

public class Location : IEquatable<Location>
{    
       public string Name { get; set; }     
       public string BaseCode { get; set; 

        public bool Equals(Location other)
        {
            if (Object.ReferenceEquals(other, null)) return false;

            if (Object.ReferenceEquals(this, other)) return true;
            return BaseCode.Equals(other.BaseCode);
        }

        public override int GetHashCode()
        {
            return BaseCode.GetHashCode();
        }


} 

そのため、List of Person で Distinct を使用できるようになり、個別の名前と BaseCode のみが返されます。

 var distinctListPerson = PersonList.Distinct().ToList();

このインターフェイスについては、MSDNから読むことができます

于 2012-07-10T13:28:52.387 に答える
0

Adamのソリューションは、それを処理するより「適切な」方法です。しかし、LINQ でそれを行いたい場合は、次のようなことも行う必要があります (コードは、場所が順序付けられることを想定し、文字列を識別子として受け取ることに注意してください)。

persons
    .GroupBy(x => x.Name)
    .SelectMany(x => x)
        .GroupBy(y => string.Concat(y.Locations.Select(z => z.Name)))
    .SelectMany(x => x
        .GroupBy(y => string.Concat(y.Locations.Select(z => z.BaseCode)))
    .Select(x => x.First());
于 2012-07-10T13:34:45.390 に答える
0

次のようなものを書きたくなります。私はそれがy.Locations.Equals()動作することを確認していませんが、同じ仕事をするものに置き換えるのは簡単なはずです.

    List<Person> personList = new List<Person>();
    List<Person> deduplicatedPersonList = new List<Person>();
    personList.ForEach(x =>
    {
        Person existingPerson = personList.Find(y =>
        {
            if (y.Locations.Equals(x.Locations))
                return false;
            return true;
        });
        if (existingPerson == null)
            deduplicatedPersonList.Add(x);
    });
于 2012-07-10T13:36:58.727 に答える