4

我有一堂课:

public class ShipmentInformation
{
    public string OuterNo { get; set; }
    public long Start { get; set; }
    public long End { get; set; }

}

我有一个List<ShipmentInformation>名为Results.

然后我做:

List<ShipmentInformation> FinalResults = new List<ShipmentInformation>();
var OuterNumbers = Results.GroupBy(x => x.OuterNo);
foreach(var item in OuterNumbers)
{
   var orderedData = item.OrderBy(x => x.Start);

   ShipmentInformation shipment = new ShipmentInformation();
   shipment.OuterNo = item.Key;
   shipment.Start = orderedData.First().Start;
   shipment.End = orderedData.Last().End;

   FinalResults.Add(shipment);
}

我现在遇到的问题是,在每个分组项目中,我都有各种 ShipmentInformation,但起始编号可能不是按 x 顺序排列的。x 可以是 300 或 200,具体取决于传入的参数。为了说明我可以

  1. 开始 = 1,结束 = 300
  2. 开始 = 301,结束 = 600
  3. 开始 = 601,结束 = 900
  4. 开始 = 1201,结束 = 1500
  5. 开始 = 1501,结束 = 1800

因为我有这个跳转,所以我不能使用上面的循环来创建一个实例ShipmentInformation并获取第一个和最后一个项目orderedData以使用它们的数据来填充该实例。

我想要某种方法来识别跳跃 300 或 200 并创建 ShipmentInformation 的实例以添加到数据是连续的 FinalResults 中。

使用上面的示例,我将有 2 个 ShipmentInformation 实例,其起点为 1,终点为 900,另一个实例的起点为 1201,终点为 1800

4

4 回答 4

4

尝试以下操作:

private static IEnumerable<ShipmentInformation> Compress(IEnumerable<ShipmentInformation> shipments) 
{
  var orderedData = shipments.OrderBy(s => s.OuterNo).ThenBy(s => s.Start);
  using (var enumerator = orderedData.GetEnumerator())
  {
    ShipmentInformation compressed = null;
    while (enumerator.MoveNext())
    {
      var current = enumerator.Current;
      if (compressed == null) 
      {
        compressed = current;
        continue;
      }
      if (compressed.OuterNo != current.OuterNo || compressed.End < current.Start - 1)
      {
        yield return compressed;
        compressed = current;
        continue;
      }
      compressed.End = current.End;
    }

    if (compressed != null)
    {
      yield return compressed;
    }
  }
}

可以这样使用:

var finalResults = Results.SelectMany(Compress).ToList();
于 2012-06-07T12:21:50.770 回答
1

如果您想要的东西可能性能很差并且无法理解,但只使用开箱即用的 LINQ,我认为这可能会做到。

var orderedData = item.OrderBy(x => x.Start);
orderedData
    .SelectMany(x => 
        Enumerable
            .Range(x.Start, 1 + x.End - x.Start)
            .Select(n => new { time = n, info = x))
    .Select((x, i) => new { index = i, time = x.time, info = x.info } )
    .GroupBy(t => t.time - t.info)
    .Select(g => new ShipmentInformation {
        OuterNo = g.First().Key,
        Start = g.First().Start(),
        End = g.Last().End });

我的脑袋疼。

(为清楚起见进行编辑:这只是替换了循环中的内容。您可以通过将其放在语句中替换循环foreach来使其变得更加可怕,就像在丰富的答案中一样。)Selectforeach

于 2012-06-07T12:25:36.670 回答
1

这个怎么样?

List<ShipmentInfo> si = new List<ShipmentInfo>();
si.Add(new ShipmentInfo(orderedData.First()));
for (int index = 1; index < orderedData.Count(); ++index)
{
    if (orderedData.ElementAt(index).Start == 
        (si.ElementAt(si.Count() - 1).End + 1))
    {
        si[si.Count() - 1].End = orderedData.ElementAt(index).End;
    }
    else
    {
        si.Add(new ShipmentInfo(orderedData.ElementAt(index)));
    }
}

FinalResults.AddRange(si);
于 2012-06-07T12:42:50.763 回答
0

另一种 LINQ 解决方案是使用 except 扩展方法。

编辑:用 C# 重写,包括将缺失的点组合回范围:

class Program
{
    static void Main(string[] args)
    {

        Range[] l_ranges = new Range[] { 
            new Range() { Start = 10, End = 19 },
            new Range() { Start = 20, End = 29 },
            new Range() { Start = 40, End = 49 },
            new Range() { Start = 50, End = 59 }
        };

        var l_flattenedRanges =
            from l_range in l_ranges
            from l_point in Enumerable.Range(l_range.Start, 1 + l_range.End - l_range.Start)
            select l_point;

        var l_min = 0;
        var l_max = l_flattenedRanges.Max();

        var l_allPoints =
            Enumerable.Range(l_min, 1 + l_max - l_min);

        var l_missingPoints =
            l_allPoints.Except(l_flattenedRanges);

        var l_lastRange = new Range() { Start = l_missingPoints.Min(), End = l_missingPoints.Min() };
        var l_missingRanges = new List<Range>();

        l_missingPoints.ToList<int>().ForEach(delegate(int i)
        {
            if (i > l_lastRange.End + 1)
            {
                l_missingRanges.Add(l_lastRange);
                l_lastRange = new Range() { Start = i, End = i };
            }
            else
            {
                l_lastRange.End = i;
            }
        });
        l_missingRanges.Add(l_lastRange);

        foreach (Range l_missingRange in l_missingRanges) {
            Console.WriteLine("Start = " + l_missingRange.Start + " End = " + l_missingRange.End);
        }

        Console.ReadKey(true);
    }
}

class Range
{

    public int Start { get; set; }
    public int End { get; set; }

}
于 2012-06-07T12:40:06.430 回答