C# 如何在集合中查找相似的对象

C# 如何在集合中查找相似的对象,c#,linq,find,C#,Linq,Find,我希望根据我实现的方法在集合中查找类似对象 例如,此示例类: class myObj { public int Data1 { get; set; } public int Data2 { get; set; } public int Data3 { get; set; } } 然后在课堂上实施类似的方法: public bool Similar(myObj obj) { if (obj.Data1 == this.Data1 &&a

我希望根据我实现的方法在集合中查找类似对象

例如,此示例类:

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

    public int Data2 { get; set; }

    public int Data3 { get; set; }        
}
然后在课堂上实施类似的方法:

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 });
List items=newlist();
//不相似
添加(新的myObj(){Data1=1,Data2=2,Data3=4});
添加(新的myObj(){Data1=2,Data2=3,Data3=18});
添加(新的myObj(){Data1=3,Data2=4,Data3=75});
添加(新的myObj(){Data1=4,Data2=2,Data3=3});
//相似的
添加(新的myObj(){Data1=5,Data2=26,Data3=97});
添加(新的myObj(){Data1=5,Data2=26,Data3=37});
添加(新的myObj(){Data1=10,Data2=45,Data3=47});
添加(新的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;
}
private静态列表getsimilar(列表项)
{
列表相似=新列表();
而(items.Count>0)
{
变量q=(从项目中的c开始)
其中c.类似(项目[0])
选择c.ToList();
如果(q.计数>1)
{
相似性。AddRange(q);
foreach(q中的var obj)
项目。移除(obj);
}
其他的
项目。删除(项目[0]);
}    
返回相似项;
}

有更好的方法吗?

使用groupBy进行此操作,您可以根据项目的第一个和第二个值对项目进行分组,如下所示

var GroupByValues=items.GroupBy(obj=> new { val1 = obj.Data1,val2=obj.Data2 });
然后,您可以遍历组以获取值…

尝试以下操作:

private static List<myObj> GetSimilars(List<myObj> items)
{
    return items.SelectMany(x => items.Where(z => x != z && x.Similar(z))).ToList();
}
private静态列表getsimilar(列表项)
{
return items.SelectMany(x=>items.Where(z=>x!=z&&x.simular(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();
    }
private静态列表getsimilar(列表项)
{
var结果=项目中的x
从y到项目
其中x!=y&&x.相似(y)
选择x;
返回result.ToList();
}

您可以使用Linq的
GroupBy
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> similars = similarGroups.SelectMany(g => g).ToList();
List similar=similarGroups.SelectMany(g=>g.ToList();

您可以实现接口IComparable或使用自行编写的比较器,后者实现IComparaler。若这样做,可以对对象的任意列表进行排序

以下是一个小教程:

要解释示例中调用GetSimilar时修改项目列表的副作用,您必须这样做。要消除副作用,请松开RemoveAll()

private静态列表getsimilar(列表项)
{
var similars=来自项目中的s
其中items.Any(s2=>s!=s2&&s.simular(s2))
选择s;
items.RemoveAll(s=>similars.Contains(s));
返回similar.ToList();
}

制作这个可重用的类怎么样

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();
var lookup = new Dictionary<myObj, string>(similarity);
或者像另一个答案一样

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

或者在其他对框架友好的地方。

看起来您想要声明自己的
IEqualityComparer
,您可以在众多linq重载中使用它,我不能使用equals。我已经在其他地方使用它了。我不是说重写
等于
,我是说在某个类上实现一个
IEqualityComparer
。你试过这个代码吗?因为它不起作用。它有语法错误。测试它并在LinqPad上工作。。。发布语法错误,我将更正它。“'Key.myObj'不包含'Count'的定义,并且找不到接受'Key.myObj'类型的第一个参数的扩展方法'Count'。顺便问一下,什么是密钥?我认为密钥是您的名称空间。。。请使用我的原始代码验证您。。。在我看来,这就像您将Count()-方法放在对象auf myObj中一样,请确保您的查询使用我刚才复制和粘贴的Count()来访问集合。我什么也没变。无论如何,我会检查它。酷答案!!但是如果我想让他们分组呢。比如“Tim Schmelter”的回答?我并不是说我从一开始就想要它,但当我看到它时,我认为这可能会更好。@Star,
GroupBy
有很多重载,我添加了一个例子。另一个问题:我可以在外部实现“Equals”吗?我的意思是,如果我更改了类型并想要相同的东西。@当然是Star,但一个公共基类型必须公开类似的属性。@Star,我不确定上一个group by的语法,但你明白了。这显然是可行的。
var similarity = new MyObjSimilarity();
items.Where(o => similarity.Equals(o, w));
var similarity = new MyObjSimilarity();
var lookup = new Dictionary<myObj, string>(similarity);
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);