Перемещение списка для добавления элементов с использованием .NET - PullRequest
4 голосов
/ 23 марта 2010

У меня есть список объектов. Каждый объект имеет целочисленное количество и переменную DateTime, которая содержит значение месяца и года. Я хотел бы просмотреть список и дополнить его, добавив пропущенные месяцы (с количеством 0), чтобы все последующие месяцы были представлены в списке. Каков наилучший способ сделать это?

Пример: Оригинальный список

{10, 3 января}, {10, 4}, {10, 2}, {10, 2 мая}, {10, 3}, {10, 3}, {10, 6}, {10, 3 ноября }, {Dec10, 7}, {Feb11, 3}

Новый список

{10 января, 3}, {10 февраля, 4}, {10 марта, 0} , {10 апреля, 2}, {10 мая, 2}, {10 июня, 0}, {10 июля , 0} {10, 3}, {10, 10}, {10, 6}, {10, 3}, {10, 7}, {11 января, 0} , { 11 февраля 3}

Ответы [ 6 ]

3 голосов
/ 23 марта 2010

Предположим, что структура хранится как List<Tuple<DateTime,int>>.

var oldList = GetTheStartList();
var map = oldList.ToDictionary(x => x.Item1.Month);

// Create an entry with 0 for every month 1-12 in this year 
// and reduce it to just the months which don't already 
// exist 
var missing = 
  Enumerable.Range(1,12)
  .Where(x => !map.ContainsKey(x))
  .Select(x => Tuple.Create(new DateTime(2010, x,0),0))

// Combine the missing list with the original list, sort by
// month 
var all = 
  oldList
  .Concat(missing)
  .OrderBy(x => x.Item1.Month)
  .ToList();
3 голосов
/ 23 марта 2010

Один из возможных алгоритмов - отслеживать предыдущие и текущие месяцы. Если разница между предыдущим и текущим составляет 1 месяц, добавьте текущий к результату. Если разница превышает один месяц, сначала добавьте отсутствующие месяцы, а затем скопируйте текущий месяц.

Foo prev = months.First();
List<Foo> result = new List<Foo> { prev };
foreach (Foo foo in months.Skip(1))
{
    DateTime month = prev.Month;
    while (true)
    {
        month = month.AddMonths(1);
        if (month >= foo.Month)
        {
            break;
        }
        result.Add(new Foo { Month = month, Count = 0 });
    }
    result.Add(foo);
    prev = foo;
}

Результаты:

01-01-2010 00:00:00: 3
01-02-2010 00:00:00: 4
01-03-2010 00:00:00: 0
01-04-2010 00:00:00: 2
01-05-2010 00:00:00: 2
01-06-2010 00:00:00: 0
01-07-2010 00:00:00: 0
01-08-2010 00:00:00: 3
01-09-2010 00:00:00: -3
01-10-2010 00:00:00: 6
01-11-2010 00:00:00: 3
01-12-2010 00:00:00: 7
01-01-2011 00:00:00: 0
01-02-2011 00:00:00: 3

Другой код, необходимый для компиляции:

class Foo
{
    public DateTime Month { get; set; }
    public int Count { get; set; }
}

List<Foo> months = new List<Foo>
{
    new Foo{ Month = new DateTime(2010, 1, 1), Count = 3 },
    new Foo{ Month = new DateTime(2010, 2, 1), Count = 4 },
    new Foo{ Month = new DateTime(2010, 4, 1), Count = 2 },
    new Foo{ Month = new DateTime(2010, 5, 1), Count = 2 },
    new Foo{ Month = new DateTime(2010, 8, 1), Count = 3 },
    new Foo{ Month = new DateTime(2010, 9, 1), Count = -3 },
    new Foo{ Month = new DateTime(2010, 10, 1), Count = 6 },
    new Foo{ Month = new DateTime(2010, 11, 1), Count = 3 },
    new Foo{ Month = new DateTime(2010, 12, 1), Count = 7 },
    new Foo{ Month = new DateTime(2011, 2, 1), Count = 3 }
};

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

1 голос
/ 23 марта 2010
var months = new [] { "Jan", "Feb", "Mar", ... };
var yourList = ...;
var result = months.Select(x => {
  var yourEntry = yourList.SingleOrDefault(y => y.Month = x);
  if (yourEntry != null) {
    return yourEntry;
  } else {
    return new ...;
  }
});
0 голосов
/ 24 марта 2010

Учитывая годы, скорость и расширяемость, это можно сделать как перечислимое расширение (возможно, даже с использованием универсального селектора свойств). Если даты уже усечены до месяца и список упорядочен до выполнения FillMissing, рассмотрите этот метод:

public static class Extensions
{
    public static IEnumerable<Tuple<DateTime, int>> FillMissing(this IEnumerable<Tuple<DateTime, int>> list)
    {
        if(list.Count() == 0)
            yield break;
        DateTime lastDate = list.First().Item1;
        foreach(var tuple in list)
        {
            lastDate = lastDate.AddMonths(1);
            while(lastDate < tuple.Item1)
            {
                yield return new Tuple<DateTime, int>(lastDate, 0);
                lastDate = lastDate.AddMonths(1);
            }
            yield return tuple;
            lastDate = tuple.Item1;
        }
    }
}

и в примере:

    private List<Tuple<DateTime, int>> items = new List<Tuple<DateTime, int>>()
    {
        new Tuple<DateTime, int>(new DateTime(2010, 1, 1), 3),
        new Tuple<DateTime, int>(new DateTime(2010, 2, 1), 4),
        new Tuple<DateTime, int>(new DateTime(2010, 4, 1), 2),
        new Tuple<DateTime, int>(new DateTime(2010, 5, 1), 2),
        new Tuple<DateTime, int>(new DateTime(2010, 8, 1), 3),
        new Tuple<DateTime, int>(new DateTime(2010, 9, 1), -3),
        new Tuple<DateTime, int>(new DateTime(2010, 10, 1), 6),
        new Tuple<DateTime, int>(new DateTime(2010, 11, 1), 3),
        new Tuple<DateTime, int>(new DateTime(2010, 12, 1), 7),
        new Tuple<DateTime, int>(new DateTime(2011, 2, 1), 3)
    };

    public Form1()
    {
        InitializeComponent();
        var list = items.FillMissing();
        foreach(var element in list)
        {
            textBox1.Text += Environment.NewLine + element.Item1.ToString() + " - " + element.Item2.ToString();
        }
    }

, в результате чего появится текстовое поле, содержащее:

2010-01-01 00:00:00 - 3
2010-02-01 00:00:00 - 4
2010-03-01 00:00:00 - 0
2010-04-01 00:00:00 - 2
2010-05-01 00:00:00 - 2
2010-06-01 00:00:00 - 0
2010-07-01 00:00:00 - 0
2010-08-01 00:00:00 - 3
2010-09-01 00:00:00 - -3
2010-10-01 00:00:00 - 6
2010-11-01 00:00:00 - 3
2010-12-01 00:00:00 - 7
2011-01-01 00:00:00 - 0
2011-02-01 00:00:00 - 3
0 голосов
/ 23 марта 2010

Если я правильно понял с месяцем "DateTime":

    for (int i = 0; i < 12; i++)
        if (!original.Any(n => n.DateTimePropery.Month == i))
            original.Add(new MyClass {DateTimePropery = new DateTime(2010, i, 1), IntQuantity = 0});
    var sorted = original.OrderBy(n => n.DateTimePropery.Month);
0 голосов
/ 23 марта 2010

Один из способов - реализовать IEqualityComparer <> вашего объекта, затем вы можете создать список объектов-заполнителей для добавления в существующий список, используя метод расширения «Кроме». Вроде как ниже

public class MyClass
{
    public DateTime MonthYear { get; set; }
    public int Quantity { get; set; }
}

public class MyClassEqualityComparer : IEqualityComparer<MyClass>
{
    #region IEqualityComparer<MyClass> Members

    public bool Equals(MyClass x, MyClass y)
    {
        return x.MonthYear == y.MonthYear;
    }

    public int GetHashCode(MyClass obj)
    {
        return obj.MonthYear.GetHashCode();
    }

    #endregion
}

И тогда вы можете сделать что-то вроде этого

// let this be your real list of objects    
List<MyClass> myClasses = new List<MyClass>() 
{
    new MyClass () { MonthYear = new DateTime (2010,1,1), Quantity = 3},
    new MyClass() { MonthYear = new DateTime (2010,12,1), Quantity = 2}
};

List<MyClass> fillerClasses = new List<MyClass>();
for (int i = 1; i < 12; i++)
{
    MyClass filler = new MyClass() { Quantity = 0, MonthYear = new DateTime(2010, i, 1) };
    fillerClasses.Add(filler);
}

myClasses.AddRange(fillerClasses.Except(myClasses, new MyClassEqualityComparer()));
...