Question

EDIT: j'ai manqué un point crucial: .NET 2.0

Prenons le cas où j'ai une liste d'éléments non triés, par souci de simplicité, d'un type comme celui-ci:

class TestClass
{
    DateTime SomeTime;
    decimal SomePrice;

    // constructor
}

Je dois créer une sortie de type rapport, dans laquelle les prix totaux de chaque jour sont cumulés. Il doit y avoir une ligne pour chaque élément, suivie des lignes de résumé appropriées.

Prenez ces données de test:

List<TestClass> testList = new List<TestClass> { 
new TestClass(new DateTime(2008,01,01), 12),
new TestClass(new DateTime(2007,01,01), 20),
new TestClass(new DateTime(2008,01,01), 18)
};

La sortie souhaitée ressemblerait à ceci:

2007-01-01: 
20
Total: 20

2008-01-01: 
12
18
Total: 30

Quelle est la meilleure façon d’aborder de tels scénarios? Dans le cas d'une telle liste, je mettrais en œuvre l'interface IComparable pour TestClass, afin que la liste puisse être triée.

Pour créer le rapport lui-même, vous pouvez utiliser quelque chose comme ceci (supposons que nous ayons des méthodes pour des tâches telles que l'accumulation des prix, le suivi de la date du jour, etc.):

for (int i=0;i<testList.Count;i++)
{
    if (IsNewDate(testList[i]))
    {
        CreateSummaryLine();
        ResetValuesForNewDate();
    }

    AddValues(testList[i]);
}

// a final summary line is needed to include the data for the last couple of items.
CreateSummaryLine();

Cela fonctionne bien, mais j'ai un sentiment étrange en ce qui concerne la deuxième & "CreateSummaryLines &"; est concerné.

De quelle manière gérez-vous de telles situations (compte tenu en particulier du fait que nous devons travailler avec une liste < > d'éléments plutôt qu'un dictionnaire pré-catégorisé ou quelque chose du genre)?

Était-ce utile?

La solution

[modifier] Puisque vous utilisez .NET 2.0 avec C # 3.0, vous pouvez utiliser LINQBridge pour l'activer.

LINQ; quelque chose comme:

        var groups = from row in testList
                  group row by row.SomeTime;
        foreach (var group in groups.OrderBy(group => group.Key))
        {
            Console.WriteLine(group.Key);
            foreach(var item in group.OrderBy(item => item.SomePrice))
            {
                Console.WriteLine(item.SomePrice);
            }
            Console.WriteLine("Total" + group.Sum(x=>x.SomePrice));
        }

Autres conseils

D'accord, donc si vous ne pouvez pas utiliser LINQ:

(J'utilise var pour économiser de l'espace, mais il est facile de traduire en C # 2.0 si nécessaire ...)

var grouped = new SortedDictionary<DateTime, List<TestClass>>();
foreach (TestClass entry in testList) {
  DateTime date = entry.SomeTime.Date;
  if (!grouped.ContainsKey(date)) {
    grouped[date] = new List<TestClass>();
  }
  grouped[date].Add(entry);
}

foreach (KeyValuePair<DateTime, List<TestClass>> pair in testList) {
  Console.WriteLine("{0}: ", pair.Key);
  Console.WriteLine(BuildSummaryLine(pair.Value));
}

Question cruciale: utilisez-vous .NET 3.5, permettant ainsi d’utiliser LINQ? Si tel est le cas, vous pouvez grouper par SomeTime.Date puis traiter chaque date séparément.

Si vous avez des quantités énormes de données, vous pouvez trouver mon Pousser le projet LINQ utile - mais sinon, LINQ to Objects est votre meilleur choix.

Quelle version de C # /. NET utilisez-vous? Si vous êtes à jour, jetez un coup d'œil à LINQ. Cela permet de regrouper vos données. Dans votre cas, vous pouvez regrouper par date:

var days = from test in testList group test by test.SomeTime;
foreach (var day in days) {
    var sum = day.Sum(x => x.SomePrice);
    Report(day, sum);
}

De cette façon, vous pouvez signaler une liste de valeurs pour chaque day, ainsi que la somme d'argent pour ce jour-là.

Votre sortie souhaitée donne l'impression de créer une structure de données qui représente vos données récapitulatives. Vous souhaitez associer une date à une liste d'objets TestClass, puis en avoir une collection. Vous pouvez utiliser un hachage ou un dictionnaire pour la collection.

Pour le résumé, la structure de données de paires peut implémenter ToString pour vous.

Quelle que soit la manière dont vous générez la sortie du rapport, si vous envisagez de le faire régulièrement dans votre application, envisagez de créer une interface de rapport et d'implémenter des classes pour cette interface afin de localiser la logique de création du rapport à un endroit donné plutôt que de se disperser. le code pour créer des rapports tout au long de votre application. D'après mon expérience, les applications ont généralement (ou évoluent) des exigences pour plusieurs rapports et, éventuellement, pour plusieurs formats.

Si vous ne disposez que d'un seul rapport, ne vous en faites pas avant d'écrire votre deuxième rapport, , mais commencez à travailler sur une architecture de création de rapports facilitant son extension et sa maintenance.

Par exemple (et en utilisant le code de @Marc Gravell):

public interface IReport
{
   string GetTextReportDocument();
   byte[] GetPDFReportDocument();  // needing this triggers the development of interface
}

public class SalesReport : IReport
{
   public void AddSale( Sale newSale )
   {
       this.Sales.Add(newSale);  // or however you implement it
   }

   public string GetTextReportDocument()
   {
       StringBuilder reportBuilder = new StringBuilder();
       var groups = from row in this.Sales
                    group row by row.SomeTime.Date;
       foreach (var group in groups.OrderBy(group => group.Key))
       {            
          reportBuilder.AppendLine(group.Key);
          foreach(var item in group.OrderBy(item => item.SomePrice))            
          {
             reportBuilder.AppendLine(item.SomePrice);
          }
          reportBuilder.AppendLine("Total" + group.Sum(x=>x.SomePrice));
      }

      return reportBuilder.ToString();
   }

   public byte[] GetPDFReportDocument()
   {
        return PDFReporter.GenerateDocumentFromXML( this.ConvertSalesToXML() );
   }

... le reste est laissé comme un exercice pour le lecteur ...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top