C# 获取数量缺货时的日期

C# 获取数量缺货时的日期,c#,linq,C#,Linq,试图弄清楚如何在LINQ C缺货时获得日期范围结果# 假设我有一个如下的表格结果 EventDate | Qty 2014-02-03 | 6 2014-02-04 | -1 2014-02-05 | -2 2014-02-06 | 2 2014-02-07 | -1 2014-02-08 | -2 2014-02-09 | -3 2014-02-10 | 5 FromDate | ToDate 2014-02-04 | 2014-02-05 2014-02-07 | 2014-02-0

试图弄清楚如何在LINQ C缺货时获得日期范围结果#

假设我有一个如下的表格结果

EventDate  | Qty
2014-02-03 | 6
2014-02-04 | -1
2014-02-05 | -2
2014-02-06 | 2
2014-02-07 | -1
2014-02-08 | -2
2014-02-09 | -3
2014-02-10 | 5
FromDate   | ToDate
2014-02-04 | 2014-02-05
2014-02-07 | 2014-02-09
现在我想得到一个日期范围,当库存中的数量为负0时,就像这样

EventDate  | Qty
2014-02-03 | 6
2014-02-04 | -1
2014-02-05 | -2
2014-02-06 | 2
2014-02-07 | -1
2014-02-08 | -2
2014-02-09 | -3
2014-02-10 | 5
FromDate   | ToDate
2014-02-04 | 2014-02-05
2014-02-07 | 2014-02-09
有人能帮我怎么做到吗

更新


我知道我可以通过将查询相乘来实现这一点,但如果可能的话,我希望只在一个LINQ查询中实现这一点。

您可以编写自己的自定义函数来更改为LINQ表达式

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

namespace ConsoleApplication
    {
    public class Class1
        {
        public static void Main(string[] args)
            {
            IEnumerable<QuantityDate> quantityDates = GetQuantityDates();//I'm sure you already have some way of retrieving these, via EF or Linq to SQL etc.
            var results = quantityDates.Where(qd => qd.Qty < 0).CombineResults(); //This is the main Linq expression
            foreach (var result in results)
                {
                Console.WriteLine("From Date: {0} To Date: {1}", result.FromDate, result.ToDate);
                }
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
            }

        //Just to see the data for the moment. You'll probably get this data via EF or Linq to SQL
        public static List<QuantityDate> GetQuantityDates()
            {
            List<QuantityDate> seed = new List<QuantityDate>()
            {
            new QuantityDate() { EventDate = new DateTime(2014, 2, 3), Qty = 6 },
            new QuantityDate() { EventDate = new DateTime(2014, 2, 4), Qty = -1 },
            new QuantityDate() { EventDate = new DateTime(2014, 2, 5), Qty = -2 },
            new QuantityDate() { EventDate = new DateTime(2014, 2, 6), Qty = 2 },
            new QuantityDate() { EventDate = new DateTime(2014, 2, 7), Qty = -1 },
            new QuantityDate() { EventDate = new DateTime(2014, 2, 8), Qty = -2 },
            new QuantityDate() { EventDate = new DateTime(2014, 2, 9), Qty = -3 },
            new QuantityDate() { EventDate = new DateTime(2014, 2, 10), Qty = 5 }
            };
            return seed;
            }
        }
    public static class Extensions
        {

        //This is where the magic happens, and we combine the results
        public static List<OutOfStockRange> CombineResults(this IEnumerable<QuantityDate> input)
            {
            List<OutOfStockRange> output=new List<OutOfStockRange>();
            OutOfStockRange lastEntered = null;
            foreach(var qd in input.OrderBy(qd => qd.EventDate))
             {
                 if(lastEntered != null && lastEntered.ToDate.AddDays(1) == qd.EventDate)
                 {
                     lastEntered.ToDate = qd.EventDate;
                 }
                 else
                 {
                     lastEntered =new OutOfStockRange(){FromDate = qd.EventDate, ToDate = qd.EventDate};
                     output.Add(lastEntered);
                 }
            }
            return output;
        }
        }

    //This class represents the input data
    public class QuantityDate
        {
        public DateTime EventDate { get; set; }
        public int Qty { get; set; }
        }

    //This class represents the output data
    public class OutOfStockRange
        {
        public DateTime FromDate { get; set; }
        public DateTime ToDate { get; set; }
        }
    }
使用系统;
使用System.Collections.Generic;
使用System.Linq;
命名空间控制台应用程序
{
公共班级1
{
公共静态void Main(字符串[]args)
{
IEnumerable quantityDates=GetQuantityDates();//我确信您已经有了通过EF或Linq to SQL等检索这些数据的方法。
var results=quantityDates.Where(qd=>qd.qy<0).CombineResults();//这是主Linq表达式
foreach(结果中的var结果)
{
WriteLine(“从日期:{0}到日期:{1}”,result.FromDate,result.ToDate);
}
控制台。WriteLine(“按任意键退出”);
Console.ReadKey();
}
//现在只需查看数据。您可能会通过EF或LINQtoSQL获取此数据
公共静态列表GetQuantityDates()
{
列表种子=新列表()
{
new QuantityDate(){EventDate=new DateTime(2014,2,3),Quantity=6},
new QuantityDate(){EventDate=new DateTime(2014,2,4),Quantity=-1},
new QuantityDate(){EventDate=new DateTime(2014,2,5),Quantity=-2},
new QuantityDate(){EventDate=new DateTime(2014,2,6),Quantity=2},
new QuantityDate(){EventDate=new DateTime(2014,2,7),Quantity=-1},
new QuantityDate(){EventDate=new DateTime(2014,2,8),Quantity=-2},
new QuantityDate(){EventDate=new DateTime(2014,2,9),Quantity=-3},
new QuantityDate(){EventDate=new DateTime(2014,2,10),Quantity=5}
};
返回种子;
}
}
公共静态类扩展
{
//这就是神奇发生的地方,我们结合了结果
公共静态列表组合结果(此IEnumerable输入)
{
列表输出=新列表();
OutOfStockRange lastEntered=null;
foreach(input.OrderBy中的var qd(qd=>qd.EventDate))
{
if(lastEntered!=null&&lastEntered.ToDate.AddDays(1)==qd.EventDate)
{
lastEntered.ToDate=qd.EventDate;
}
其他的
{
lastEntered=new OutOfStockRange(){FromDate=qd.EventDate,ToDate=qd.EventDate};
输出。添加(上次输入);
}
}
返回输出;
}
}
//此类表示输入数据
公共类QuantityDate
{
公共日期时间事件日期{get;set;}
公共整数数量{get;set;}
}
//此类表示输出数据
公共类超出库存范围
{
公共日期时间FromDate{get;set;}
公共日期时间ToDate{get;set;}
}
}

对于仅使用内置函数的替代方案

这里的策略是选择数量小于零的所有日期,并针对这些日期中的每一个,执行子查询,以构建当前日期之后数量也小于零的所有日期的列表。使用
TakeWhile
,这将在下一个日期之前停止,且数量为非负数。然后取其中的最大值,该值适用于范围的结束日期。最后一步是
GroupBy
,删除映射到同一结束日期的“缺货”范围开始后的所有天数,为您留下一个不同的缺货日期范围

如下所示,它依赖于输入时按时间顺序排序的库存水平

public class StockLevel
{
    public DateTime Date { get; set; }
    public int Quantity { get; set; }                        
}

static void Main(string[] args)
{
    List<StockLevel> stockLevels = new List<StockLevel>()
    { 
        new StockLevel() { Date = DateTime.Parse("03-Feb-2014"), Quantity = 6 },
        new StockLevel() { Date = DateTime.Parse("04-Feb-2014"), Quantity = -1 },
        new StockLevel() { Date = DateTime.Parse("05-Feb-2014"), Quantity = -2 },
        new StockLevel() { Date = DateTime.Parse("06-Feb-2014"), Quantity = 2 },
        new StockLevel() { Date = DateTime.Parse("07-Feb-2014"), Quantity = -1 },
        new StockLevel() { Date = DateTime.Parse("08-Feb-2014"), Quantity = -2 },
        new StockLevel() { Date = DateTime.Parse("09-Feb-2014"), Quantity = -3 },
        new StockLevel() { Date = DateTime.Parse("10-Feb-2014"), Quantity = 5 },
    };

    var outOfStockDates = stockLevels
        .Where(a => a.Quantity < 0)
        .Select(a => new 
        { 
                S1 = a.Date, 
                S2 = stockLevels
                        .Where(c => c.Date >= a.Date)
                        .TakeWhile(b => b.Quantity < 0)
                        .Select(b => b.Date).Max() 
        })
        .GroupBy(a => a.S2, a => a.S1, (S2, S1S) => new { FromDate = S1S.Min(), ToDate = S2 });

    Console.ReadKey();
}
公共类库存级别
{
公共日期时间日期{get;set;}
公共整数数量{get;set;}
}
静态void Main(字符串[]参数)
{
列表库存级别=新列表()
{ 
new StockLevel(){Date=DateTime.Parse(“2014年2月3日”),Quantity=6},
new StockLevel(){Date=DateTime.Parse(“2014年2月4日”),数量=-1},
new StockLevel(){Date=DateTime.Parse(“2014年2月5日”),数量=-2},
new StockLevel(){Date=DateTime.Parse(“2014年2月6日”),Quantity=2},
new StockLevel(){Date=DateTime.Parse(“2014年2月7日”),数量=-1},
new StockLevel(){Date=DateTime.Parse(“2014年2月8日”),数量=-2},
new StockLevel(){Date=DateTime.Parse(“2014年2月9日”),数量=-3},
new StockLevel(){Date=DateTime.Parse(“2014年2月10日”),Quantity=5},
};
var outOfStockDates=库存水平
.式中(a=>a.数量<0)
.选择(a=>新建
{ 
S1=a.日期,
S2=库存水平
.其中(c=>c.日期>=a.日期)
.TakeWhile(b=>b.数量<0)
.Select(b=>b.Date).Max()
})
.GroupBy(a=>a.S2,a=>a.S1,(S2,S1S)=>new{FromDate=S1S.Min(),ToDate=S2});
Console.ReadKey();
}

以下是我的做法

从您的数据开始:

var stockQuantities = new []
{
    new { Date = new DateTime(2014, 2, 3), Qty = 6 },
    new { Date = new DateTime(2014, 2, 4), Qty = -1 },
    new { Date = new DateTime(2014, 2, 5), Qty = -2 },
    new { Date = new DateTime(2014, 2, 6), Qty = 2 },
    new { Date = new DateTime(2014, 2, 7), Qty = -1 },
    new { Date = new DateTime(2014, 2, 8), Qty = -2 },
    new { Date = new DateTime(2014, 2, 9), Qty = -3 },
    new { Date = new DateTime(2014, 2, 10), Qty = 5 },
};
然后查询缺货记录:

var outOfStock = stockQuantities.Where(x => x.Qty < 0);
这是我得到的结果:


我想到了以下几点:

var src_objects = new []
{
    new {date = DateTime.Parse("2014-02-03"), qty = 6},
    new {date = DateTime.Parse("2014-02-04"), qty = -1},
    new {date = DateTime.Parse("2014-02-05"), qty = -2},
    new {date = DateTime.Parse("2014-02-06"), qty = 2},
    new {date = DateTime.Parse("2014-02-07"), qty = -1},
    new {date = DateTime.Parse("2014-02-08"), qty = -2},
    new {date = DateTime.Parse("2014-02-09"), qty = -3},
    new {date = DateTime.Parse("2014-02-10"), qty = 5}
};


int i = 0;


var ranges = src_objects
    .OrderBy(key => key.date)
    .Select(obj =>
    {
        if (obj.qty > 0)
        {
            ++i;
            return new { date = obj.date, group_key = 0 };
        }
        else
            return new { date = obj.date, group_key = i };
    })
    .Where(obj => obj.group_key != 0)
    .GroupBy(obj => obj.group_key)
    .Select(g => new { fromdate = g.First().date, todate = g.Last().date });

ranges
.ToList()
.ForEach(range => Console.WriteLine(string.Format("{0} - {1}", range.fromdate, range.todate.Date)));

如果你把问题分成几个小部分,那就不太难了。首先获取数量为负数的日期(Linq的
.where()
函数),然后将结果传递给一个自定义函数,其中返回类型为
List
,并具有一个类