linq查找彼此时间范围内的元素

问题描述 投票:0回答:1

我有一个问题,我需要根据时间属性查找(分组)对象列表。

我需要先按一个属性(人员 ID/键)进行分组,然后在该组中找到另一个属性与代码集匹配(以任何顺序)的条目,最后只计算是否在彼此的时间范围内找到该代码集(在这个例子中是 60 分钟)。

下面我已经很粗暴地尝试这样做了,我知道它一定是非常低效的。

感谢观看

public class TestData
{
    public int PersonKey { set; get; }
    public int EventKey { set; get; }
    public string ActionCode { set; get; }
    public DateTime ActionDate { set; get; }
}

        public static void DoSummary()
        {
            //sample criteria
            var section1 = new { name = "Section 1", codes = new List<string>(new string[] { "code1", "code2" }) };
            var section2 = new { name = "Section 2", codes = new List<string>(new string[] { "code1", "code3", "code4" }) };
            var section3 = new { name = "Section 3", codes = new List<string>(new string[] { "code1", "code2", "code5" }) };
            var list = new List<TestData>();
            //filter original list to only include those containing certain codes
            list = list.Where(x => section2.codes.Contains(x.ActionCode)).ToList();
            for (int year = 2010; year < 2023; year++)
            {
                //storing already counted items to not count multiple times
                var skipEvents = new List<int>();
                //getting the year's data just to make things easier/more managable
                var yearData = list.Where(x => x.ActionDate.Year == year);
                //group by the person key
                var people_in_year = yearData.GroupBy(x => x.PersonKey);
                int count = 0;
                //only check those that have more than one event in the people group
                foreach (var g in people_in_year.Where(x => x.Count() > 1))
                {

                    foreach (var ev in g)
                    {
                        var testList = new List<int>();
                        bool allCodesFound = true;
                        foreach (var otherCode in section1.codes.Where(x => x != ev.ActionCode))
                        {
                            var found = g.Where(x => !skipEvents.Contains(x.EventKey) && x.ActionCode == otherCode &&
                            //in case the current event date is before the checked date in the list
                                (ev.ActionDate - x.ActionDate).TotalMinutes > -60 &&
                                (ev.ActionDate - x.ActionDate).TotalMinutes < 60
                            ).ToList();
                            if (found.Count > 0)
                            {
                                //add the event keys to allow skipping later if all matched
                                testList.AddRange(found.Select(x => x.EventKey));
                            }
                            else
                            {
                                allCodesFound = false;
                            }
                        }
                        if (allCodesFound)
                        {
                            //add all the keys already found to skip later on as they already have been grouped
                            skipEvents.AddRange(testList);
                            count++;
                        }
                    }
                }
            }
        }

编辑(澄清):一旦事件被分组,就不应再考虑它。

c# .net linq
1个回答
0
投票

这是一个示例应用程序,小提琴:https://dotnetfiddle.net/sCHti6

检查它是否做了它应该做的事情。

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

public class TestData
{
    public int PersonKey { get; set; }
    public int EventKey { get; set; }
    public string ActionCode { get; set; }
    public DateTime ActionDate { get; set; }
}

public class Program
{
    public static void Main()
    {
        // Sample data
        var testData = new List<TestData>
        {
            new TestData { PersonKey = 1, EventKey = 1, ActionCode = "code1", ActionDate = new DateTime(2023, 1, 1, 10, 0, 0) }, //This is match
            new TestData { PersonKey = 1, EventKey = 2, ActionCode = "code3", ActionDate = new DateTime(2023, 1, 1, 10, 30, 0) }, //This is match
            new TestData { PersonKey = 1, EventKey = 3, ActionCode = "code4", ActionDate = new DateTime(2023, 1, 1, 11, 0, 0) },
            new TestData { PersonKey = 1, EventKey = 4, ActionCode = "code1", ActionDate = new DateTime(2023, 1, 1, 12, 0, 0) }, //This is match
            new TestData { PersonKey = 1, EventKey = 5, ActionCode = "code3", ActionDate = new DateTime(2023, 1, 1, 12, 30, 0) }, //This is match
            new TestData { PersonKey = 1, EventKey = 6, ActionCode = "code4", ActionDate = new DateTime(2023, 1, 1, 13, 0, 0) },
            new TestData { PersonKey = 1, EventKey = 7, ActionCode = "code2", ActionDate = new DateTime(2023, 1, 1, 14, 0, 0) },
            new TestData { PersonKey = 1, EventKey = 8, ActionCode = "code1", ActionDate = new DateTime(2023, 1, 1, 15, 0, 0) },
            new TestData { PersonKey = 2, EventKey = 9, ActionCode = "code2", ActionDate = new DateTime(2023, 1, 1, 15, 30, 0) },
            new TestData { PersonKey = 2, EventKey = 10, ActionCode = "code5", ActionDate = new DateTime(2023, 1, 1, 16, 0, 0) },
            new TestData { PersonKey = 2, EventKey = 11, ActionCode = "code2", ActionDate = new DateTime(2023, 1, 1, 16, 30, 0) }
        };

        int count = CalculateEventCombinationsCount(testData);
        Console.WriteLine($"Total count: {count}");

    }

    public static int CalculateEventCombinationsCount(List<TestData> list)
    {
        // Sample criteria
        var section2 = new { name = "Section 2", codes = new List<string>(new string[] { "code1", "code3" }) };

        int count = 0;
        for (int year = 2023; year <= 2023; year++)
        {
            // Group by the person key
            var people_in_year = list.Where(x => x.ActionDate.Year == year && section2.codes.Any(code => x.ActionCode == code))
                                     .GroupBy(x => x.PersonKey);

            foreach (var personGroup in people_in_year)
            {
                var events = personGroup.ToList();                       

                for (int i = 0; i < events.Count - 1; i++)
                {
                    // Filter remaining events within the time range of the current event
                    var foundEvent = events.Skip(i + 1).FirstOrDefault(x => (x.ActionDate - events[i].ActionDate).TotalMinutes >= 0 && (x.ActionDate - events[i].ActionDate).TotalMinutes < 60);
                    if (foundEvent!=null) count++;  //Here you can debug the pair - events[i] and foundEvent is the pair
                }
            }
        }
        return count;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.