我试图使用linq在一组属性的自定义对象列表中找到最佳匹配。在下面创建的MyObjects列表中,我希望在MyObject的所有四个属性中找到与testObject最接近的一个:
IList<MyObject> list = new List<MyObject>();
list.Add(new MyObject { Property1 = "A", Property2 = "B", Property3 = "C", Property4 = "D" });
list.Add(new MyObject { Property1 = "A", Property2 = "A", Property3 = "C", Property4 = "D" });
list.Add(new MyObject { Property1 = "A", Property2 = "A", Property3 = "A", Property4 = "D" });
var testObject = new MyObject { Property1 = "A", Property2 = "A", Property3 = "A", Property4 = "A" };在上面的示例中,我希望将最后一个对象匹配为3,或者将4个属性与testObject中的属性匹配。
通过这样做,我可以计算出匹配的属性有多少:
 var matchCount = list.Max(x => (x.Property1 == testObject.Property1 ? 1 : 0) +
        (x.Property2 == testObject.Property2 ? 1 : 0) +
        (x.Property3 == testObject.Property3 ? 1 : 0) +
        (x.Property4 == testObject.Property4 ? 1 : 0));但是,除了写出一个非常长的linq表达式,检查每个属性组合上的3个匹配项之外,我还想不出如何获取与其中三个属性匹配的实体。理想情况下,我想要一个适合于具有10个属性的对象的解决方案。
有没有人知道是否有一种被接受的方法来做到这一点?
编辑
我从最初的问题中又遗漏了一条信息.如果有多个匹配对象,那么我需要选择与该级别匹配的对象列表(也就是说,如果有一个对象在3个属性上匹配,那么我需要找到与3个属性匹配的所有对象)。
解决方案
根据树懒的答案,我已经能够得到我想要的使用这个。我很想看看有没有人对此有更清晰的答案.
var grouping = list.GroupBy(x => (x.Property1 == testObject.Property1 ? 1 : 0) +
(x.Property2 == testObject.Property2 ? 1 : 0) +
(x.Property3 == testObject.Property3 ? 1 : 0) +
(x.Property4 == testObject.Property4 ? 1 : 0));
var maxCount = grouping.Max(x => x.Key);
var resultSet = grouping.FirstOrDefault(x => x.Key == maxCount).Select(g => g).ToList();发布于 2015-06-29 13:57:03
你也可以试试这个
    IList<MyObject> list = new List<MyObject>();
    list.Add(new MyObject { Property1 = "A", Property2 = "B", Property3 = "C", Property4 = "D" });
    list.Add(new MyObject { Property1 = "A", Property2 = "A", Property3 = "C", Property4 = "D" });
    list.Add(new MyObject { Property1 = "A", Property2 = "A", Property3 = "A", Property4 = "D" });
    var testObject = new MyObject { Property1 = "A", Property2 = "A", Property3 = "A", Property4 = "A" };
//list of objects with 3 or matches
    var sorted = list.Select(x => new
    {
        MatchCount = (x.Property1 == testObject.Property1 ? 1 : 0)
                    + (x.Property2 == testObject.Property2 ? 1 : 0)
                    + (x.Property3 == testObject.Property3 ? 1 : 0)
                    + (x.Property4 == testObject.Property4 ? 1 : 0),
        MyObj = x
    })
    .OrderBy( x => x.MatchCount)
    .Where( x => x.MatchCount >= 3 );
//gets the first object from the list
    var match = sorted.Any() ? sorted.OrderBy(x => x.MatchCount).FirstOrDefault().MyObj : null;发布于 2015-06-29 12:33:23
您可以使用一些好的旧映像:
// get all get methods of all public properties
var getter = typeof(MyObject).GetProperties().Select(prop => prop.GetMethod).ToList();
// sort by number of matches
var result = list.OrderBy(l => getter.Count(a => a.Invoke(l, null).Equals(a.Invoke(testObject, null)))).LastOrDefault();不是快速的方法,而是简单。
在回应你的评论时:
只需使用GroupBy
var grouped = list.GroupBy(l => getter.Count(a => a.Invoke(l, null).Equals(a.Invoke(testObject, null))))
                  .OrderBy(grp => grp.Key)
                  .LastOrDefault();grouped现在包含与最佳匹配的所有项。
发布于 2015-06-30 05:50:22
Eric的答案是好的,但可能拆分propertyMatches和linq查询的实现对实心更好,并为您提供了更多可读性的查询。
此外,还可以直接从linq查询返回MyObject。
using System;
using System.Linq;
using System.Collections.Generic;
namespace ConsoleApplication1
{
  public class Program
{
    public static void Main()
    {
        IList<MyObject> list = new List<MyObject>();
        list.Add(new MyObject { P1 = "A", P2 = "B", P3 = "C", P4 = "D" });
        list.Add(new MyObject { P1 = "A", P2 = "A", P3 = "C", P4 = "D" });
        list.Add(new MyObject { P1 = "A", P2 = "A", P3 = "A", P4 = "D" });
        var testObject = new MyObject { P1 = "A", P2 = "A", P3 = "A", P4 = "A" };
       //create a list of annonymous class with inner MyObject and propertyMatches count, filter it by matches and return its inner MyObject
       //I think this query is easy to read, returs what you want (list of MyObject) in one line and can be applied without changes to any class as long implements propertyMatches.
        var res = from ao in (from obj in list select new { obj = obj, matches = obj.propertyMatches(testObject) }) where ao.matches >= 3 select ao.obj;
      //same query in method call form
      var res2 = list.Select(o => new
          {
             matches = o.propertyMatches(testObject),
             obj = o
          }).Where(ao => ao.matches >= 3).Select(ao => ao.obj);
        Console.WriteLine(res.First().P1);
        Console.WriteLine(res.First().P2);
        Console.WriteLine(res.First().P3);
        Console.WriteLine(res.First().P4);
        Console.WriteLine(res2.First().P1);
        Console.WriteLine(res2.First().P2);
        Console.WriteLine(res2.First().P3);
        Console.WriteLine(res2.First().P4);
    }
}propertyMatches可以是MyObject基类中的抽象方法,也可以是接口方法或扩展方法,或者您需要的任何方法,这取决于您的设计和体系结构。
即
 public static class oMyExtensions
        {
            public static int propertyMatches(this MyObject o, MyObject otherObj)
            {
                return (o.P1 == otherObj.P1 ? 1 : 0)
                + (o.P2 == otherObj.P2 ? 1 : 0)
                + (o.P3 == otherObj.P3 ? 1 : 0)
                + (o.P4 == otherObj.P4 ? 1 : 0);
            }
        }完整的示例这里。
https://stackoverflow.com/questions/31114892
复制相似问题