Объединение списка объектов с точками в уникальные списки - PullRequest
2 голосов
/ 06 апреля 2011

У меня есть список объектов.Каждый объект уникален по идентификатору в сочетании с периодом времени - FromDate и ToDate.

Список может выглядеть так (в реальной жизни есть больше атрибутов):

ID;С даты;ToDate
1;2000-1-1;2019-12-31
2;2000-1-1;2019-12-31
3;2000-1-1;2009-12-31
3;2010-1-1;2019-12-31
4;2000-1-1;2019-12-31
5;2000-1-1;2014-12-31
5;2015-1-1;2019-12-31

Примечание:

  • Оба объекта id = 3 и id = 5 имеют два воплощения (точки)
  • Itможно предположить, что в периоде для идентификатора нет промежутков.
  • Список всегда упорядочен, как показано

Мне нужно несколько списков объектов, каждый из которых имеет уникальный периодсодержит только один из каждого идентификатора:

LIST1:
Период = 2000-1-1 до 2009-12-31
ListOfObjects: 1;2; 3 (первый период); 4; 5 (первый период)

LIST2:
Период = 2010-1-1 до 2014-12-31
ListOfObjects: 1; 2; 3 (второй период); 4; 5 (первый период)

LIST3:
период = 2015-От 1-1 до 2019-12-31
ListOfObjects: 1; 2; 3 (второй период); 4; 5 (второй период)

Я почти уверен, что здесь требуется некоторое рекурсивное вычисление, нокроме этого, я все знаки вопроса.Пожалуйста помоги!!!Напишите мне, если нужна дополнительная информация.

С уважением, Мортен: о)

Ответы [ 4 ]

3 голосов
/ 06 апреля 2011

Сначала я создам ваши уникальные диапазоны дат от дат и дат, упорядочивая уникальные даты с / по возрастанию.Поскольку даты не нарушены, это должен быть непрерывный список.

Затем я выбрал бы каждую «пару» последовательных дат в списке на основе порядка списка и использовал бы его в качестве нового периода.Таким образом, первый период будет датой 0 и датой 1, второй период будет датой 2 и 3, затем датой 4 и 5 и т. Д. Затем я добавлю каждый элемент, который пересекается с этим диапазоном дат, в список объектов в этом диапазоне.,

поэтому на основе вашего списка данных будет:

2000-1-1
2009-12-31
2010-1-1
2014-12-31
2015-1-1
2019-12-31

тогда ваши группы будут:

2000-1-1 до 2009-12-31 объектов 1,2,3 (первый), 4,5 (первый)
2010-1-1 до 2014-12-31 объектов 1,2,3 (второй), 4,5 (первый)
2015-1-1 до2019-12-31 объекты 1,2,3 (второй), 4,5 (второй)

Предполагается, что исходные требуемые результаты были неверными.

РЕДАКТИРОВАТЬ

Первоначальные требуемые результаты были неверными, но теперь соответствуют тому, чего должен достичь этот подход

1 голос
/ 06 апреля 2011

Эта статья позволяет обрабатывать несколько периодов времени.

Сначала давайте определим пользовательский диапазон времени, включая ваш идентификатор:

// ------------------------------------------------------------------------
public class IdTimeRange : TimeRange
{

  // ----------------------------------------------------------------------
  public IdTimeRange( int id, DateTime start, DateTime end ) :
    base( start, end )
  {
    Id = id;
  } // IdTimeRange    

  // ----------------------------------------------------------------------
  public int Id { get; private set; }

  // ----------------------------------------------------------------------
  public override string ToString()
  {
    return Id + ": " + base.ToString();
  } // ToString

} // IdTimeRange

Теперь вы можете использовать TimePeriodCollection в сочетании с TimeLine , чтобы найти все пересекающиеся периоды:

// ----------------------------------------------------------------------
public void TimeLinePeriodsSample()
{
  TimePeriodCollection periods = new TimePeriodCollection();
  periods.Add( new IdTimeRange( 1, new DateTime( 2000, 1, 1 ), new DateTime( 2019, 12, 31 ) ) );
  periods.Add( new IdTimeRange( 2, new DateTime( 2000, 1, 1 ), new DateTime( 2019, 12, 31 ) ) );
  periods.Add( new IdTimeRange( 3, new DateTime( 2000, 1, 1 ), new DateTime( 2009, 12, 31 ) ) );
  periods.Add( new IdTimeRange( 3, new DateTime( 2010, 1, 1 ), new DateTime( 2019, 12, 31 ) ) );
  periods.Add( new IdTimeRange( 4, new DateTime( 2000, 1, 1 ), new DateTime( 2019, 12, 31 ) ) );
  periods.Add( new IdTimeRange( 5, new DateTime( 2000, 1, 1 ), new DateTime( 2014, 12, 31 ) ) );
  periods.Add( new IdTimeRange( 5, new DateTime( 2015, 1, 1 ), new DateTime( 2019, 12, 31 ) ) );
  foreach ( ITimePeriod period in periods )
  {
    Console.WriteLine( "Period: " + period );
  }

  // time line with all period start and end moments
  ITimeLineMomentCollection moments = new TimeLineMomentCollection();
  moments.AddAll( periods );
  DateTime start = periods.Start;
  foreach ( ITimeLineMoment moment in moments )
  {
    if ( moment.EndCount <= 0 ) // search the next period end
    {
      continue;
    }
    DateTime end = moment.Moment;
    TimeRange timeRange = new TimeRange( start, end );
    Console.WriteLine( "Period: {0}", timeRange );
    ITimePeriodCollection intersections = periods.IntersectionPeriods( timeRange );
    foreach ( ITimePeriod intersection in intersections )
    {
      Console.WriteLine( "  Intersection: {0}", intersection );
    }
    start = moment.Moment;
  }
} // TimeLinePeriodsSample
1 голос
/ 06 апреля 2011

Посмотрите на группу с помощью команд linq:

источник: http://msdn.microsoft.com/en-us/vcsharp/aa336754.aspx#simple1

Обновление: Время истекает, выкладываю незаконченный результат, возможно, это поможет. Надеюсь, я смогу продолжить через несколько часов. Это возвращает две коллекции, содержащие 1,2,3 (первый), 4,5 (первый) и 1,2,3 (второй), 4,5 (второй).

    [Test]
    public void Test()
    {
        var xs = new List<X>
                     {
                         new X("1; 2000-1-1; 2019-12-31"),
                         new X("2; 2000-1-1; 2019-12-31"),
                         new X("3; 2000-1-1; 2009-12-31"),
                         new X("3; 2010-1-1; 2019-12-31"),
                         new X("4; 2000-1-1; 2019-12-31"),
                         new X("5; 2000-1-1; 2014-12-31"),
                         new X("5; 2015-1-1; 2019-12-31")
                     };

        var groupedById = (from x in xs
                          group x by x.Id into ids
                          select ids);

        var maxOccurances = groupedById
            .Max(x => x.Count());

        var result = new List<List<X>>();

        for (var i = 0; i < maxOccurances; i++)
        {
            var list = groupedById.Select(idGroup => idGroup.Count() < i
                                                         ? idGroup.ElementAt(i)
                                                         : idGroup.Last())
                .ToList();
            result.Add(list);
        }
    }

    public class X
    {
        public int Id { get; set; }
        public DateTime DateFrom { get; set; }
        public DateTime DateTo { get; set; }

        public X(string input)
        {
            var splitted = input.Split(';');
            Id = Convert.ToInt32(splitted[0]);
            DateFrom = Convert.ToDateTime(splitted[1]);
            DateTo = Convert.ToDateTime(splitted[2]);
        }
    }
0 голосов
/ 07 апреля 2011

Вдохновленный вашим вкладом - и спасибо за это - я придумал решение. Я создаю дерево всех возможных комбинаций. Корень дерева - это первый элемент в списке объектов. Мой класс обработчика дерева вызывает себя рекурсивно, чтобы пройти через объекты. В листовом узле я проверяю пересечение периодов всех узлов в пути. Если есть пересекающийся период, у меня есть правильная комбинация, и обработчик дерева передает ее через делегата. Если путь не имеет пересекающегося периода, его нельзя использовать. Я исключил вторичные классы, но я могу вставить их, если вы запросите это:

internal delegate void PathDelivery(JPTreeNode treeNode, Period periodInCommon); //Deliver a path through this delegate


internal class TreeHandler //The main class
{
    private readonly PathDelivery _pathDelivery;

    // Construct with 
    public TreeHandler(PathDelivery pathDelivery)
    {
        _pathDelivery = pathDelivery; 
    }

    public void HandleNode(IList<SourceRow> jpSource, int level,JPTreeNode parentNode, Period intersectingPeriod)
    {
        int sequenceId = level + 1;

        IList<SourceRow> children = new List<SourceRow>(System.Linq.Enumerable.Where(jpSource, S => S.Id == sequenceId));
        if (children.Count == 0) //The last leaf level... return the path throug PathDelivery delegate if valid dates
        {
            if (intersectingPeriod!= null)
                _pathDelivery(parentNode, intersectingPeriod); //Only deliver this path if all nodes in path has an intersecting period
        }
        else //This node has children to add
        {
            foreach (SourceRow child in children) //Loop and create children 
            {
                Stop stop = new Stop(child.Id, child.Period, child.Description); //Value object for child node

                //Re-calculate the valid period (intersect) for the tree path 
                Period newIntersect = Period.Intersect(stop.Period, intersectingPeriod); 

                JPTreeNode childNode = new JPTreeNode(parentNode, stop); //Create the child node 

                if (parentNode!= null) //If not at root level, add child node to parent node
                    parentNode.ChildNodes.Add(childNode);

                // Recursive call, handle possible grandchildren (children of this childNode) or finish path
                HandleNode(jpSource, sequenceId, childNode, newIntersect); 
            }
        }

    }

}

Ниже приведено устройство для модульного тестирования, демонстрирующее использование:

[TestFixture]
public sealed class TestRecursiveDataHandling
{
    private IList<Path> _pathList;

    [SetUp]
    public void SetUp()
    {
        _pathList = new List<Path>();
    }

    [TearDown]
    public void TearDown()
    {
        _pathList = null; 
    }

    [Test]
    public void CreateLists()
    {
        //Create the data source 
        IList<SourceRow> jpSource = new List<SourceRow>();
        jpSource.Add(new SourceRow("1", 1, new DateTime(2000, 1, 1), new DateTime(2009, 12, 31)));
        jpSource.Add(new SourceRow("2A", 2, new DateTime(2000, 1, 1), new DateTime(2004, 12, 31)));
        jpSource.Add(new SourceRow("2B", 2, new DateTime(2005, 1, 1), new DateTime(2009, 12, 31)));
        jpSource.Add(new SourceRow("3", 3, new DateTime(2000, 1, 1), new DateTime(2009, 12, 31)));
        jpSource.Add(new SourceRow("4A", 4, new DateTime(2000, 1, 1), new DateTime(2008, 12, 31)));
        jpSource.Add(new SourceRow("4B", 4, new DateTime(2009, 1, 1), new DateTime(2009, 12, 31)));


        //Instantiate handler
        TreeHandler handler = new TreeHandler(CreatePathFromButtonNode);

        //Handle root node, and recurse 
        handler.HandleNode(jpSource, 0, null, Period.Infinite);


        //DISPLAY TREE PATHS IN CONSOLE 
        DisplayResultInConsole();

        //A simple assertion. Expect that 3 paths were created.
        Assert.That(_pathList.Count, Is.EqualTo(3));


    }

    private void DisplayResultInConsole()
    {
        for (int i = 0; i < _pathList.Count;  i++ )
        {
            IList<Stop> jpList = _pathList[i].Stops;
            string stopList = string.Empty; 
            foreach (Stop stop in jpList)
            {
                stopList = stopList + stop.ID + ";" + stop.Description + ";"+ stop.Period + " \n";
            }

            string p = "List " + i + ">> Period " + _pathList[i].Period;

            Console.WriteLine("List " + i + ">> Period " +  p);
            Console.WriteLine("Stops: ");
            Console.WriteLine(stopList);
            Console.WriteLine();
        }
    }



    //Method for receiving paths from leaf nodes. Passed as delegate to TreeHandler
    private void CreatePathFromButtonNode(JPTreeNode treeNode, Period periodInCommon)
    {
        IList<Stop> stopList = new List<Stop>();
        JPTreeNode currentNode = treeNode; 
        while (currentNode!= null)
        {
            stopList.Add(currentNode.NodeValue);
            currentNode = currentNode.ParentNode; 
        }

         _pathList.Add(new Path(periodInCommon, stopList));
    }





}

Этот тест генерирует следующий вывод на консоль. Обратите внимание, что путь (комбинация) 4B; 3; 2A; 1 не был возвращен. Это потому, что периоды 2A и 4B не пересекаются:

Список 0 >> Список периодов 0 >> Период С: 01.01.2000 До: 12/31/2004
Остановки:
4; 4A; От: 01.01.2000 Кому: 12/31/2008
3; 3; От: 01.01.2000 До: 12/31/2009
2; 2A; От: 01.01.2000 Кому: 12/31/2004
1; 1; От: 01.01.2000 Кому: 31.12.2009

Список 1 >> Период Список 1 >> Период С: 01.01.2005 До: 12/31/2008
Остановки:
4; 4A; От: 01.01.2000 Кому: 12/31/2008
3; 3; От: 01.01.2000 Кому: 12/31/2009
2; 2B; От: 01.01.2005 До: 12/31/2009
1; 1; От: 01.01.2000 Кому: 31.12.2009

Список 2 >> Список периодов 2 >> Период С: 01.01.2009 До: 31.12.2009 Остановки:
4; 4B; От: 01.01.2009 До: 12/31/2009
3; 3; От: 01.01.2000 До: 12/31/2009
2; 2B; От: 01.01.2005 До: 12/31/2009
1; 1; От: 01.01.2000 Кому: 31.12.2009

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...