获取数量缺货的日期

时间:2014-12-08 21:31:15

标签: 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

现在我希望得到一个日期范围,当qty为零时,像这样

FromDate   | ToDate
2014-02-04 | 2014-02-05
2014-02-07 | 2014-02-09

有人可以帮助我,请问如何实现这一目标?

更新

我知道我可以通过乘以查询来做到这一点,但如果可能的话,我想在一个LINQ查询中这样做。

4 个答案:

答案 0 :(得分:2)

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

此处的策略是选择数量小于零的所有日期,并且对于每个日期,执行子查询,该子查询构建当前日期之后所有日期的列表,其数量也小于零。使用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();
}

答案 1 :(得分:0)

您可以编写自己的自定义函数以更改为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; }
        }
    }

答案 2 :(得分:0)

这是我将如何做到的。

从您的数据开始:

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);

现在使用Aggregate来构建结果:

var outOfStockRanges =
    outOfStock
        .Skip(1)
        .Aggregate(
            outOfStock
                .Take(1)
                .Select(x => new { From = x.Date, To = x.Date })
                .ToList(),
            (a, x) =>
            {
                if (a.Last().To.AddDays(1.0) == x.Date)
                    a[a.Count - 1] = new { From = a.Last().From, To = x.Date };
                else
                    a.Add(new { From = x.Date, To = x.Date });
                return a;
            });

这是我得到的结果:

result

答案 3 :(得分: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)));