在多个属性

时间:2017-12-14 10:25:24

标签: c# linq

如果不举一个具体的例子,很难解释我的问题。这里可能有一个类似的问题,但我无法找到它,因为我无法用可搜索的术语对其进行措辞。

基本上我需要在列表中查找在多个属性上具有任何重复值的项目。换句话说,原始列表中的任何值都必须是唯一的,无论它属于哪个属性。

这是一个我能想出的一个简单的例子,它可以很好地描述问题:

  

有一个假期日期列表,其中包含额外的房产   可选的更换日期(例如:假期在周末时)。此列表中的每个日期都必须是唯一的,所以   我想找到包含重复日期的项目。

     

PART1:返回重复日期列表   
PART2:返回所有具有重复日期的项目的列表

我相信这是一个很好的例子,因为其中一个属性nullable可能会让它变得更加困难。

型号:

public class Holiday
{
    public Holiday(DateTime hDate, string descr, DateTime? rDate = null)
    {
        HolidayDate = hDate;
        Description = descr;
        ReplacementDate = rDate;
    }

    public DateTime HolidayDate { get; set; }
    public string Description { get; set; }
    public DateTime? ReplacementDate { get; set; }
}

以下是一些让您入门的示例数据(并希望能够解决任何混淆)

var list = new List<Holiday>()
{
    new Holiday(new DateTime(2016,1,1),"NEW YEAR 2016"),
    new Holiday(new DateTime(2016,3,27),"EASTER MONDAY 2016", new DateTime(2016,3,28)),
    new Holiday(new DateTime(2016,12,25),"CHRISTMAS DAY 2016", new DateTime(2016,12,26)),
    new Holiday(new DateTime(2017,1,1),"NEW YEAR 2017", new DateTime(2017,1,2)),
    new Holiday(new DateTime(2017,4,17),"EASTER MONDAY 2017"),
    new Holiday(new DateTime(2017,12,25),"CHRISTMAS DAY 2017"),
    new Holiday(new DateTime(2018,1,1),"NEW YEAR 2018"),
    new Holiday(new DateTime(2018,1,1),"DUPLICATE 1"), //example of a duplicate
    new Holiday(new DateTime(2018,1,2),"DUPLICATE 2", new DateTime(2016,1,1)), //example of a duplicate
    new Holiday(new DateTime(2018,1,3),"DUPLICATE 3", new DateTime(2017,1,2)), //example of a duplicate
    new Holiday(new DateTime(2018,1,4),"DUPLICATE 4", new DateTime(2018,1,4)), //example of a duplicate

};

var result = list; //add your code that finds the items with a duplicate value, preferably a linq query

//PART1: return list of the actual duplicate values
//duplcateDates =  "2016-01-01, 2017-01-02, 2018-01-01, 2018-01-04";

//PART2: return a list with the items that have a duplicate item
var reultString = string.Join(", ", result.Select(q => q.Description));
//resultString = "NEW YEAR 2016, DUPLICATE 2, NEW YEAR 2017, DUPLICATE 3, NEW YEAR 2018, DUPLICATE 1, DUPLICATE 4";

对于那些懒惰并且不想解决这个例子的人,只要你理解我的问题并且可以通过使用你自己的类似例子帮助我或指出我正确的方向,我真的欣赏它。

任何不涉及编写特定foreachfor-loop的解决方案都会接受作为答案的单独检查每个属性的重复项。
我希望能够将这个问题的解决方案应用于不同的类似情况,而不必编写整个代码块来迭代这些可能性。

这就是我想知道linq查询是否可行的原因。但是,如果您有一个解决此类问题的通用方法或扩展,请分享!

5 个答案:

答案 0 :(得分:2)

您可以展平收藏,以便每个日期(假日和替换)由分隔项目表示,然后按日期分组,如下所示:

// flatten
var result = list.SelectMany(c => new[] {
    // always include HolidayDate, it's not nullable
    new {
        Date = c.HolidayDate,
        Instance = c
    },
    // include replacement date if not null
    c.ReplacementDate != null ? new {
        Date = c.ReplacementDate.Value,
        Instance = c
    }: null
})
// filter out null items (that were created for null replacement dates)
.Where(c => c != null)
.GroupBy(c => c.Date)
.Where(c => c.Count() > 1)
.ToArray();

// keys of groups are duplicate dates
var duplcateDates = String.Join(", ", result.Select(c => c.Key.ToString("yyyy-MM-dd")));

var reultString = string.Join(", ", 
      // flatten groups extracting all items
      result.SelectMany(c => c)
      // filter out duplicates
     .Select(c => c.Instance).Distinct()
     .Select(c => c.Description));

答案 1 :(得分:1)

也有一个: 不确定,如果没有收集数据可能,首先。

//PART0: collecting data 
var holidayDateDates = list.Select(x => x.HolidayDate).ToList();
var replacementDates = list.Select(x => x.ReplacementDate).Where(y => y != null).ToList().ConvertAll<DateTime>(x => x.Value);
holidayDateDates.AddRange(replacementDates);
//PART1: return list of the actual duplicate values
var result = holidayDateDates.GroupBy(x => x)
    .Where(g => g.Count() > 1)
    .Select(y => y.Key)
    .ToList();
var duplicateDates = string.Join(", ", result.Select(c => c.ToString("yyyy-MM-dd")));

//PART2: return a list with the items that have a duplicate item
var dummytime = new DateTime();// this will never be in the list and kills all nulls, see below
var duplicateHolidays = list.Where(x => result.Contains(x.HolidayDate) || result.Contains(x.ReplacementDate??dummytime));
var resultString = string.Join(", ", duplicateHolidays.Select(q => q.Description));

答案 2 :(得分:1)

这里有一个通用扩展,它返回一个自定义对象的列表,该对象包含对泛型对象的引用,重复的值,以及它是否是自我复制。

以下是您需要在新文件中添加的代码:

public class MainObject<TRef, TProp> where TProp : struct
{
    public TRef Reference { get; set; }
    public TProp? Value { get; set; }

    public bool SelfDuplicate { get; set; }

}

public static class Extensions
{
    public static IEnumerable<MainObject<TIn, TProp>> GetDuplicates<TIn, TProp>(this IEnumerable<TIn> @this) where TProp : struct
    {
        var type = typeof(TIn);
        // get the properties of the object type that match the property type
        var props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.PropertyType == typeof(TProp) || prop.PropertyType == typeof(TProp?)).ToList();

        // convert the input enumerable to a list so we don't iterate it multiple times
        var list = @this as IList<TIn> ?? @this.ToList();

        // create a list to hold all duplicates
        var duplicates = new List<MainObject<TIn, TProp>>();

        foreach (var item1 in list)
        {
            var isSelfDupe = item1.IsDuplicate<TIn, TProp>(props);
            if (isSelfDupe.IsDupe)
            {
                duplicates.Add(new MainObject<TIn, TProp>
                {
                    Reference = item1,
                    SelfDuplicate = true,
                    Value = (TProp?)isSelfDupe.Property1.GetValue(item1)
                });
            }

            foreach (var item2 in list)
            {
                if (ReferenceEquals(item1, item2)) continue;

                var isDuplicate = item1.IsDuplicate<TIn, TProp>(item2, props);
                if (isDuplicate.IsDupe)
                {
                    duplicates.Add(new MainObject<TIn, TProp>
                    {
                        Reference = item1,
                        SelfDuplicate = false,
                        Value = (TProp?)isDuplicate.Property1.GetValue(item1)
                    });
                }
            }
        }

        return duplicates.Distinct().ToList();
    }

    private static IsDuplicateResult<TIn> IsDuplicate<TIn, TProp>(this TIn obj1, IEnumerable<PropertyInfo> props) where TProp : struct
    {
        var propList = props as IList<PropertyInfo> ?? props.ToList();

        var valueList = propList.Select(prop => prop.GetValue(obj1)).ToList();
        foreach (var p1 in propList)
        {
            foreach (var p2 in propList)
            {
                if (ReferenceEquals(p1, p2)) continue;

                if (EqualityComparer<TProp?>.Default.Equals((TProp?)p1.GetValue(obj1), (TProp?)p2.GetValue(obj1)))
                {
                    return new IsDuplicateResult<TIn> { IsDupe = true, Reference = obj1, Property1 = p1, Property2 = p2 };
                }
            }
        }
        return new IsDuplicateResult<TIn> { IsDupe = false };
    }

    private static IsDuplicateResult<TIn> IsDuplicate<TIn, TProp>(this TIn obj1, TIn obj2, IEnumerable<PropertyInfo> props) where TProp : struct
    {
        var propList = props as IList<PropertyInfo> ?? props.ToList();

        var dict1 = propList.ToDictionary(prop => prop, prop => prop.GetValue(obj1));
        var dict2 = propList.ToDictionary(prop => prop, prop => prop.GetValue(obj2));

        foreach (var k1 in dict1.Keys)
        {
            foreach (var k2 in dict2.Keys)
            {
                if (dict1[k1] == null || dict2[k2] == null) continue;

                if (EqualityComparer<TProp?>.Default.Equals((TProp?)dict1[k1], (TProp?)dict2[k2]))
                {
                    return new IsDuplicateResult<TIn> { IsDupe = true, Reference = obj1, Property1 = k1, Property2 = k2 };
                }
            }
        }
        return new IsDuplicateResult<TIn> { IsDupe = false };
    }

    private class IsDuplicateResult<TIn>
    {
        public bool IsDupe { get; set; }

        public TIn Reference { get; set; }

        public PropertyInfo Property1 { get; set; }
        public PropertyInfo Property2 { get; set; }
    }
}

使用它非常简单明了,您所要做的就是调用扩展方法。

var result = holidayList.GetDuplicates<Holiday, DateTime>().ToList();
// printing the results
Console.WriteLine(string.Join(", ", result.Select(q => q.Reference.Description)));
// printing the values
var values = result.Select(r => r.Value).Distinct();
Console.WriteLine(string.Join("\r\n", values.Select(q => $"{q:dd/MM/yyyy}")));

DotNetFiddle链接:https://dotnetfiddle.net/OGwpb4

DotNetFiddle链接(带有属性注释):https://dotnetfiddle.net/vzjS2t

你可能需要更多地摆弄这些功能,看看是否有任何问题,因为我没有太多时间这样做,但是,我认为这应该让你朝着正确的方向前进:)

答案 3 :(得分:0)

如果有人在将来寻找通用解决方案。

注意: 此解决方案会将可为空的属性转换为不可为空的类型,并比较它们的值。

通用解决方案

为了便于实现,我们将定义两个方法:

  1. IsSelfDuplicate :将检测对象是否具有两个具有相同值的属性。

    private bool IsSelfDuplicate(object myObj)
    {
        PropertyInfo[] props = myObj.GetType().GetProperties();
        for (int i = 0; i < props.Length; i++)
        {
            if (props[i].GetValue(myObj) == null)
                continue;
    
            Type iType = (Nullable.GetUnderlyingType(props[i].PropertyType) ?? props[i].PropertyType);
            object iValue = Convert.ChangeType(props[i].GetValue(myObj), iType);
    
            for (int j = i + 1; j < props.Length; j++)
            {
                if (props[j].GetValue(myObj) == null)
                    continue;
    
                Type jType = (Nullable.GetUnderlyingType(props[j].PropertyType) ?? props[j].PropertyType);
                object jValue = Convert.ChangeType(props[j].GetValue(myObj), jType);
    
                if (iType == jType && iValue.ToString() == jValue.ToString())
                    return true;
            }
        }
        return false;
    }
    
  2. IsDuplicate :会检测两个对象,每个对象的属性值等于另一个。

    private bool IsDuplicate(object obj1, object obj2)
    {
        PropertyInfo[] props = obj1.GetType().GetProperties();
    
        for (int i = 0; i < props.Length; i++)
        {
            if (props[i].GetValue(obj1) == null)
                continue;
    
            Type iType = (Nullable.GetUnderlyingType(props[i].PropertyType) ?? props[i].PropertyType);
            object iValue = Convert.ChangeType(props[i].GetValue(obj1), iType);
    
            for (int j = 0; j < props.Length; j++)
            {
                if (props[j].GetValue(obj2) == null)
                    continue;
    
                Type jType = (Nullable.GetUnderlyingType(props[j].PropertyType) ?? props[j].PropertyType);
                object jValue = Convert.ChangeType(props[j].GetValue(obj2), jType);
    
                if (iType == jType && iValue.ToString() == jValue.ToString())
                    return true;
            }
        }
        return false;
    }
    
  3. 要使用IsSelfDuplicate方法,只需使用:

    List<Holiday> selfDuplicate = list.Where(l => IsSelfDuplicate(l)).ToList();
    

    要使用IsDuplicate方法,请在对象列表之间进行迭代:

    List<Holiday> duplicates = new List<Holiday>();
    
    for (int i = 0; i < list.Count; i++)
        for (int j = i + 1; j < list.Count; j++)
        {
             if (IsDuplicate(list[i], list[j]))
             {
                  duplicates.Add(list[i]);
                  duplicates.Add(list[j]);
                  break;
             }
        }
    

    最后,这是一个对象的示例列表,正如OP提供的那样:

    var list = new List<Holiday>()
    {
         new Holiday(new DateTime(2016,1,1),"NEW YEAR 2016"),
         new Holiday(new DateTime(2016,3,27),"EASTER MONDAY 2016", new DateTime(2016,3,28)),
         new Holiday(new DateTime(2016,12,25),"CHRISTMAS DAY 2016", new DateTime(2016,12,26)),
         new Holiday(new DateTime(2017,1,1),"NEW YEAR 2017", new DateTime(2017,1,2)),
         new Holiday(new DateTime(2017,4,17),"EASTER MONDAY 2017"),
         new Holiday(new DateTime(2017,12,25),"CHRISTMAS DAY 2017"),
         new Holiday(new DateTime(2018,1,1),"NEW YEAR 2018"),
         new Holiday(new DateTime(2018,1,1),"DUPLICATE 1"), //example of a duplicate
         new Holiday(new DateTime(2018,1,2),"DUPLICATE 2", new DateTime(2016,1,1)), //example of a duplicate
         new Holiday(new DateTime(2018,1,3),"DUPLICATE 3", new DateTime(2017,1,2)), //example of a duplicate
         new Holiday(new DateTime(2018,1,4),"DUPLICATE 4", new DateTime(2018,1,4)), //example of a duplicate
    };
    

    现在,你将有两个自我和非自我(如果我可以称之为)重复的结果。享受;)

答案 4 :(得分:0)

您可以使用IEnumerable和IEnumerator

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{

    class Program
    {
        static void Main(string[] args)
        {
            Holiday holiday = new Holiday();

            foreach (Holiday day in Holiday.holidays)
            {
                foreach (object field in  new HolidayField(day))
                {
                    int a = 1;
                }
                HolidayField hf = new HolidayField(day);
                for (HolidayField.Field field = HolidayField.Field.HOLIDAY_DATE; field < HolidayField.Field.END; field++)
                {
                    object o = hf.GetEnumerator(field);
                }
            }
            var groups = Holiday.holidays.GroupBy(x => x.HolidayDate).ToList();


        }
    }
    public class HolidayField : IEnumerator, IEnumerable
    {
        public Holiday holiday;

        public enum Field
        {
            RESET,
            HOLIDAY_DATE,
            DESCRIPTION,
            REPLACEMENT_DATE,
            END
        }
        Field _holidayProperties;

        public HolidayField(Holiday holiday)
        {
            this.holiday = holiday;

        }

        public Field GetIndex()
        {
            return _holidayProperties;
        }

        public object GetEnumerator(Field field)
        {
            return GetCurrent(holiday, field);
        }

        public HolidayField GetEnumerator()
        {
            return this;
        }

        // Implementation for the GetEnumerator method.
        IEnumerator IEnumerable.GetEnumerator()
        {
            return (IEnumerator) GetEnumerator();
        }



        public object Current
        {
            get
            {
                return GetCurrent(holiday, _holidayProperties);
            }
        }
        public object GetCurrent(Holiday holiday, Field field)
        {
            object results = null;

            switch (field)
            {
                case Field.DESCRIPTION:
                    results = (object)holiday.Description;
                    break;
                case Field.HOLIDAY_DATE:
                    results = (object)holiday.HolidayDate;
                    break;
                case Field.REPLACEMENT_DATE:
                    results = (object)holiday.ReplacementDate;
                    break;
            }

            return results;
        }
        public bool MoveNext()
        {
            _holidayProperties += 1;
            return _holidayProperties == Field.END ? false : true;
        }

        public void Reset()
        {
            _holidayProperties = Field.RESET;
        }
        public void Dispose()
        {
        }

    }
    public class Holiday
    {
        public static List<Holiday> holidays = new List<Holiday>()
        {
            new Holiday(new DateTime(2016,1,1),"NEW YEAR 2016"),
            new Holiday(new DateTime(2016,3,27),"EASTER MONDAY 2016", new DateTime(2016,3,28)),
            new Holiday(new DateTime(2016,12,25),"CHRISTMAS DAY 2016", new DateTime(2016,12,26)),
            new Holiday(new DateTime(2017,1,1),"NEW YEAR 2017", new DateTime(2017,1,2)),
            new Holiday(new DateTime(2017,4,17),"EASTER MONDAY 2017"),
            new Holiday(new DateTime(2017,12,25),"CHRISTMAS DAY 2017"),
            new Holiday(new DateTime(2018,1,1),"NEW YEAR 2018"),
            new Holiday(new DateTime(2018,1,1),"DUPLICATE 1"), //example of a duplicate
            new Holiday(new DateTime(2018,1,2),"DUPLICATE 2", new DateTime(2016,1,1)), //example of a duplicate
            new Holiday(new DateTime(2018,1,3),"DUPLICATE 3", new DateTime(2017,1,2)), //example of a duplicate
            new Holiday(new DateTime(2018,1,4),"DUPLICATE 4", new DateTime(2018,1,4)), //example of a duplicate

        };

        public DateTime HolidayDate { get; set; }
        public string Description { get; set; }
        public DateTime? ReplacementDate { get; set; }


        public Holiday() { }
        public Holiday(DateTime hDate, string descr, DateTime? rDate = null)
        {
            HolidayDate = hDate;
            Description = descr;
            ReplacementDate = rDate;
        }

    }


}