5

実装するメソッドに応じて、コレクション内の同様のオブジェクトを見つけたい

たとえば、このサンプル クラス:

class myObj
{
    public int Data1 { get; set; }

    public int Data2 { get; set; }

    public int Data3 { get; set; }        
}

次に、クラスにSimilarメソッドを実装します。

public bool Similar(myObj obj)
{
    if (obj.Data1 == this.Data1 && obj.Data2 == this.Data2)
        return true;
    return false;
}

今、私はこのコレクションを持っています:

List<myObj> items = new List<myObj>();
// none similar
items.Add(new myObj() { Data1 = 1, Data2 = 2, Data3 = 4 });
items.Add(new myObj() { Data1 = 2, Data2 = 3, Data3 = 18 });
items.Add(new myObj() { Data1 = 3, Data2 = 4, Data3 = 75 });
items.Add(new myObj() { Data1 = 4, Data2 = 2, Data3 = 3 });
//similar
items.Add(new myObj() { Data1 = 5, Data2 = 26, Data3 = 97 });
items.Add(new myObj() { Data1 = 5, Data2 = 26, Data3 = 37 });
items.Add(new myObj() { Data1 = 10, Data2 = 45, Data3 = 47 });
items.Add(new myObj() { Data1 = 10, Data2 = 45, Data3 = 19 });

同様のオブジェクトを取得するには、次のようにしました。

private static List<myObj> GetSimilars(List<myObj> items)
{
    List<myObj> similars = new List<myObj>();
    while (items.Count > 0)
    {
        var q = (from c in items
                 where c.Similar(items[0])
                 select c).ToList();

        if (q.Count > 1)
        {
            similars.AddRange(q);
            foreach (var obj in q)
                items.Remove(obj);
        }
        else
            items.Remove(items[0]);
    }    
    return similars;
}

それを行うより良い方法はありますか?

4

7 に答える 7

2

これを試して:

private static List<myObj> GetSimilars(List<myObj> items)
{
    return items.SelectMany(x => items.Where(z => x != z && x.Similar(z))).ToList();
}

またはこれを好む場合:

private static List<myObj> GetSimilars(List<myObj> items)
    {
        var result = from x in items
                     from y in items
                     where x != y && x.Similar(y)
                     select x;

        return result.ToList();
    }
于 2012-10-18T12:53:02.480 に答える
2

Linq のGroupByandでそれをすべて行うことができますSelectMany:

var similarGroups = from i in items
                    group i by new { i.Data1, i.Data2 } into D1D2Group
                    where D1D2Group.Count() > 1
                    select D1D2Group;

foreach (var grp in similarGroups)
    Console.WriteLine("DataGroup:{0}/{1} Count:{2}"
               , grp.Key.Data1
               , grp.Key.Data2
               , grp.Count());

List<myObj>グループをあなたのようなものにフラット化したい場合 GetSimilars:

 List <myObj> similars = similarGroups.SelectMany(g => g).ToList();
于 2012-10-18T13:01:04.033 に答える
2

再利用可能なこのクラスを作ってみませんか。

public class MyObjSimilarity : EqualityComparer<myObj>
{
    public override bool Equals(myObj a, myObj b)
    {
        if (obj.Data1 == this.Data1 && obj.Data2 == this.Data2)
        {
            return true;
        }

        return false;
    }

    public override int GetHashCode(myObj o)
    {
        int hash = 17;
        hash = hash * 23 + o.Data1.GetHashCode();
        hash = hash * 23 + o.Data2.GetHashCode();
        return hash;
    }
}

あなたはこのように使うことができます、

var similarity = new MyObjSimilarity();
items.Where(o => similarity.Equals(o, w));

または、辞書のコンストラクターに渡します。

var similarity = new MyObjSimilarity();
var lookup = new Dictionary<myObj, string>(similarity);

またはGroupBy

var similarity = new MyObjSimilarity();
items.GroupBy(o => o, o => o, similarity);

または他の答えのように

var similarity = new MyObjSimilarity();
items.GroupBy(
    o => o,
    o => new { Instance = o, Count = Count(o) },
    similarity);

または他のフレームワークに適した場所。

于 2012-10-18T13:26:26.497 に答える
1
return
    items.Where(w => items.Count(c => c.Similar(w)) > 1)
    /* add .Distinct() optional*/
    .ToList();
于 2012-10-18T12:54:19.133 に答える
1

インターフェイスIComparableを実装するか、IComparerを実装する自己記述の比較機能を使用できます。このようにすると、オブジェクトのannyリストを並べ替えることができます。

ここに小さなチュートリアルがあります: http ://support.microsoft.com/kb/320727/en

于 2012-10-18T13:02:48.743 に答える
1

GetSimilars を呼び出すときに項目リストが変更される例の副作用を説明するには、これを行う必要があります。副作用を取り除くには、RemoveAll() を緩めます

 private static List<myObj> GetSimilars(List<myObj> items)
 {
        var similars = from s in items
                 where items.Any(s2 => s != s2 && s.Similar(s2))
                 select s;

        items.RemoveAll(s => similars.Contains(s));

        return similars.ToList();
 }
于 2012-10-18T13:04:07.280 に答える
0

これを行うにはgroupByを使用します。次のように、最初と2番目の値に基づいてアイテムをグループ化できます...

var GroupByValues=items.GroupBy(obj=> new { val1 = obj.Data1,val2=obj.Data2 });

次に、グループを反復処理して値を取得できます...

于 2012-10-18T12:51:21.183 に答える