从List <T>中选择与不能直接在属性上指定的条件匹配的组

时间:2019-06-19 10:19:33

标签: c# linq

我有下面的课程指定数据记录

class DataRecord
{
    public double MeasuredValue { get; set; }
    public DateTime MeasurementDate { get; set; }
}

我想选择在一小时(日期无所谓)范围内记录的记录,其中记录的数量最大。

示例:

  1. 测量日期:2019/01/31 11:50
  2. 测量日期:2019/02/02 17:21
  3. 测量日期:2019/03/01 17:59
  4. 测量日期:2019/03/12 10:54
  5. 测量日期:2019/05/28 11:15

预期输出: 1、4、5 (因为在10:50-11:50范围内进行了3次测量)

我想到的代码是

List<DataRecord> records = new List<DataRecord>();
var maxRecordsInAnHour = records.GroupBy(x => x.MeasurementDate.Hour)
                                .Aggregate((g1, g2) => { return g2.Count() > g1.Count() ? g2 : g1; });

当我按Hour属性分组时,此代码返回 2和3 1和5 (取决于顺序),并仅使用Hour中的相同值被分组。

如何调整代码以获得预期的输出?

3 个答案:

答案 0 :(得分:1)

我将针对您的问题提出2种解决方案,具体取决于列表的长度。

初始化:

var myList = new List<DataRecord>
{
    new DataRecord
    {
        MeasurementDate = new DateTime(2019, 1, 31, 11, 50, 0)
    },
    new DataRecord
    {
        MeasurementDate = new DateTime(2019, 1, 31, 17, 21, 0)
    },
    new DataRecord
    {
        MeasurementDate = new DateTime(2019, 1, 31, 17, 59, 0)
    },
    new DataRecord
    {
        MeasurementDate = new DateTime(2019, 1, 31, 10, 54, 0)
    },
    new DataRecord
    {
        MeasurementDate = new DateTime(2019, 1, 31, 11, 54, 0)
    },
};

List<DataRecord> result = new List<DataRecord>();

解决方案1:

var minimumMinutes = myList.Min(x => x.MeasurementDate.Hour * 60 + x.MeasurementDate.Minute);
var maximumMinutes = myList.Max(x => x.MeasurementDate.Hour * 60 + x.MeasurementDate.Minute);

for (int minutes = minimumMinutes; minutes < maximumMinutes; minutes++)
{
    var list = myList.Where(x =>
        x.MeasurementDate.Hour * 60 + x.MeasurementDate.Minute <= minutes + 60 &&
        x.MeasurementDate.Hour * 60 + x.MeasurementDate.Minute >= minutes);

    if (result.Count < list.Count())
    {
        result = list.ToList();
    }
}

解决方案2:

foreach (var dataRecord in myList)
{
    var minutes = dataRecord.MeasurementDate.Hour * 60 + dataRecord.MeasurementDate.Minute;
    var before = myList.Where(x =>
        x.MeasurementDate.Hour * 60 + x.MeasurementDate.Minute >= minutes - 60 &&
        x.MeasurementDate.Hour * 60 + x.MeasurementDate.Minute <= minutes).ToList();
    var after = myList.Where(x =>
        x.MeasurementDate.Hour * 60 + x.MeasurementDate.Minute <= minutes + 60 &&
        x.MeasurementDate.Hour * 60 + x.MeasurementDate.Minute >= minutes).ToList();

    if (before.Count > result.Count ||
        after.Count > result.Count)
    {
        result = before.Count > after.Count ? before.ToList() : after.ToList();
    }
}

答案 1 :(得分:1)

正如我在评论中提到的那样,此代码的性能不高,但这可以解决问题。

// DummyData
List<DateTime> dates = new List<DateTime>
{
    DateTime.Parse("2019/01/31 11:50"),
    DateTime.Parse("2019/02/02 17:21"),
    DateTime.Parse("2019/03/01 17:59"),
    DateTime.Parse("2019/03/12 10:54"),
    DateTime.Parse("2019/05/28 11:15"),
};

// Storage for final Output
List<DateTime> finalOp = new List<DateTime>();

// Main logic goes here
// foreach Hour in list we will compare that with every other Hour in list
// and it is in 1 hour range we will add it to list
foreach (DateTime dateTime in dates)
{
    List<DateTime> temp = new List<DateTime>();
    foreach (DateTime innerDateTime in dates)
    {
        // find the difference between two hours
        var timeSpan = dateTime.TimeOfDay - innerDateTime.TimeOfDay;

        // add it to same list if we have +/- 1 Hour difference
        if (timeSpan.TotalHours <= 1 && timeSpan.TotalHours >= -1)
        {
            temp.Add(innerDateTime);
        }

    }

    // once we have final group for date we will check if this has maximum number of common dates around
    // if so replace it with previous choice
    if (finalOp.Count < temp.Count)
    {
        finalOp = temp;
    }
}

// print the results
foreach (var data in finalOp)
{
    Console.WriteLine(data.ToShortTimeString());
}

答案 2 :(得分:0)

对于myList集合中的10,000个项目,此解决方案可以很快:

private static List<DataRecord> Get(List<DataRecord> myList)
    {
        var ordered = myList.OrderBy(x => x.MeasurementDate.TimeOfDay).ToList();
        int i = 0;
        int count = myList.Count();
        var temp =
            (from s in ordered
            let index = ++i
            let next = ordered.ElementAtOrDefault(index != count ? index : 0)
            select new
            {
                Cur = s.MeasurementDate,
                Diff = index != count
                    ? (next.MeasurementDate.TimeOfDay - s.MeasurementDate.TimeOfDay).TotalMinutes
                    : 24 * 60 - (ordered.ElementAtOrDefault(count - 1).MeasurementDate.TimeOfDay - ordered.ElementAtOrDefault(0).MeasurementDate.TimeOfDay).TotalMinutes
            }).ToList();

        Dictionary<int, int> dict = new Dictionary<int, int>();
        count = 0;
        double minutes = 0;
        for (int index = 0; index < temp.Count(); index++)
        {
            for (int j = index; j < temp.Count(); j++)
            {
                minutes += temp[j].Diff;
                if (minutes > 60)
                {
                    dict.Add(index, count);
                    count = 0;
                    minutes = 0;
                    break;
                }
                else
                {
                    count++;
                }
            }
        }

        var max = dict.First(d => d.Value == dict.Values.Max());
        var finalResult = ordered.Skip(max.Key).Take(max.Value + 1).ToList();
        return finalResult;
    }

请注意,它会返回有序结果。