将具有句点的对象列表组合到唯一列表中

时间:2011-04-06 12:02:40

标签: c# linq recursion tree

我有一个对象列表。每个对象都是唯一的ID,结合时间段 - 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;情节中字; 2019年12月31日

注意:

  • 对象id = 3和id = 5都有两个化身(句号)
  • 可以假设id期间没有间隙。
  • 列表总是如图所示排序

我想要的是几个对象列表,每个对象都有一个唯一的句点,其中只包含每个id中的一个:

LIST1:
期间= 2000-1-1至2009-12-31
ListOfObjects:1; 2; 3(第一期); 4; 5(第一期)

列表2:
期间= 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(第二期)

我很确定这里需要一些递归计算,但除此之外,我都是问号。请帮忙!!!写信给我,如果需要进一步的信息。

此致 莫滕:o)

4 个答案:

答案 0 :(得分:3)

我首先通过按递增顺序排序唯一的from / to日期,从日期和日期创建您的独特日期范围。由于日期是不间断的,这应该是一个连续的列表。

然后,我将根据列表的顺序选择列表中每个连续日期的“对”,并将其用作新的句点。所以第一个时期是日期0和日期1,第二个时期是日期2和3,然后是日期4和5等等然后我会将与该日期范围相交的每个项目添加到范围内的对象列表中。

所以基于你的数据列表将是:

2000-1-1
2009-12-31
2010-1-1
2014-12-31
情节中字
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 :(得分:1)

通过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]);
        }
    }

答案 2 :(得分:1)

article允许您处理多个时间段。

首先让我们定义一个自定义时间范围,包括你的身份:

// ------------------------------------------------------------------------
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

答案 3 :(得分:0)

受到您的意见的启发 - 并且由此感谢 - 我想出了一个解决方案。我创建了一个包含所有可能组合的树。树的根是对象列表中的第一个项。我的树处理程序类以递归方式调用自身来循环对象。在叶节点,我检查路径中所有节点的周期的交集。如果有一个交叉句点,我有一个有效的组合,并且树处理程序通过委托传递。如果路径没有交叉句点,则无法使用。我已经排除了二级课程,但如果您提出要求,我可以插入它们:

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&gt;&gt;期间列表0&gt;&gt;期间:2000年1月1日至:12/31/2004
停止:
4; 4A;来自:2000年1月1日至:12/31/2008
3; 3;来自:2000年1月1日至:12/31/2009
2; 2A;来自:2000年1月1日至:12/31/2004
1; 1;来自:1/1/2000至:12/31/2009

列表1&gt;&gt;期间列表1&gt;&gt;期间自:2005年1月1日至:12/31/2008
停止:
4; 4A;来自:2000年1月1日至:12/31/2008
3; 3;来自:2000年1月1日至:12/31/2009
2; 2B;来自:2005年1月1日至:12/31/2009
1; 1;来自:1/1/2000至:12/31/2009

列表2&gt;&gt;期间清单2&gt;期间自:2009年1月1日至:12/31/2009 停止:
4; 4B;自:2009年1月1日至:12/31/2009
3; 3;来自:2000年1月1日至:12/31/2009
2; 2B;来自:2005年1月1日至:12/31/2009
1; 1;来自:1/1/2000至:12/31/2009