How to find similar objects in the collection

I want to find similar objects in the collection depending on the method that I implement

for example, this example class:

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

then we implement the Similar method in the class:

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

I now have this collection:

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

to get similar objects, I did this:

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

Is there a better way to do this?

+4
source share
7 answers

How to make this class that can be reused.

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

You can use this,

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

or go to the dictionary constructor,

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

or in GroupBy

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

or as another answer

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

or in other friendly places.

+1
source

You can do this with Linq GroupBy and 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()); 

If you want to flatten groups to List<myObj> , like your GetSimilars :

  List <myObj> similars = similarGroups.SelectMany(g => g).ToList(); 
+2
source

try the following:

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

or if you prefer this:

 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(); } 
+1
source
 return items.Where(w => items.Count(c => c.Similar(w)) > 1) /* add .Distinct() optional*/ .ToList(); 
+1
source

you could Implant the IComparable interface or use a self-recording comparator that implements IComparer. If you do this like this, you can sort the anny List of your object.

Here is a short tutorial: http://support.microsoft.com/kb/320727/en

+1
source

To take into account the side effect in the example, when the list of elements changes when GetSimilars is called, you will need to do this. To get rid of the side effect, remove 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(); } 
+1
source

Use groupBy for dothis, you can group elements based on their first and second values, as follows ...

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

Then you can iterate through the group to get the values ​​...

0
source

Source: https://habr.com/ru/post/1440538/


All Articles