Home > OS >  Iterating through a list, but can't access distinct item values
Iterating through a list, but can't access distinct item values

Time:10-01

I am building invoices with enter image description here

I've bound it to an ObservableCollection (PaidTrips).

The goal is to create one (1) PDF for each distinct "CompanyName" in the LicenseHolderID column. To achieve this, I convert to a List, and group by LicenseHolderID.

var paidTrips = PaidTrips
    .GroupBy(p => new {
        p.LicenseHolderID
})
.ToList();

After that, I iterate over the list with a foreach-loop:

foreach (var trip in paidTrips) {
    
    // I grab the distinct name
    string licenseholder = trip.Key.LicenseHolderID.ToString();
    
    // I summarize many of the columns
    decimal totalPayment = trip.Sum(x => x.Payment);
    decimal totalPaymentNet = trip.Sum(x => x.Payment);
    decimal totalOrderFee = trip.Sum(x => x.Payment);
    decimal totalPaymentFee = trip.Sum(x => x.Payment);
    
    // I grab the first value of some other columns, which won't change
    string licenseholderInvoiceID = trip.Select(x => x.LicenseHolderInvoiceID).FirstOrDefault().ToString();
    string ridelRegionInvoiceID = trip.Select(x => x.RidelRegionInvoiceID ).FirstOrDefault().ToString();

// Creating PDF document using PDFsharp:
// PDFsharp code
// PDFsharp code
// PDFsharp code
}

I am able to create a PDF-document, one for each distinct LicenseHolderID, by adding the licenseHolderID-string to the file-path of PDFsharps document.Save().

But I am not able to fill the PDF-document with all the information I need. I have the summarized amounts - good, because I absolutely need to display the full invoice amount. But -- I also need to go into detail. I do not have amounts per VehicleID.

For "CompanyName1", that'd be AG4203000002 and AG4203000003, with their corresponding row data.

I did do this...

IEnumerable<string> vehicleIds = trip
    .Select(x => x.VehicleID)
    .Distinct()
    .ToArray();
string vehicle = string.Join(", ", vehicleIds);

...in order to separate the VehicleID distinct values from eachother. I then put the string vehicle into PDFsharps way of drawing text onto the PDF (DrawString):

gfx.DrawString(vehicle, /* font and color customization */);

Which gives me the correct amount of VehicleID for each LicenseHolderID, but in one, long string... Not optimal.

Which brings me to my question: A): I need to bring along the row data of the other columns pertaining to the distinct vehicles in VehicleID, so that I can fill in details in my PDFs, and not just the summarized values, and B): if the solution to A) involves getting rid of the long, hacky string (vehicle), that'd be optimal, if not - that's okay too.

UPDATE (adding code from @Dai's answer):

// PaidTrip = My holder class, which I've bound to a ObservableCollection (PaidTrips)
var paidTrips = PaidTrips.ToList(); // I was unsure about this one
IEnumerable<IGrouping<String, PaidTrip>> tripsGroupedByCompany = paidTrips.GroupBy(pt => pt.LicenseHolderID);

foreach (IGrouping<String, PaidTrip>> companyGroup in tripsGroupedByCompany) {
    
    string licenseHolderId = companyGroup.Key;
    gfx.DrawString(/* code goes foo */);

    // I tried adding Key here, but that gave me a squiggly under "t.VehicleID"
    var groupedByVehicle = companyGroup.GroupBy(t => t.VehicleID);
    
    foreach (IGrouping<String, PaidTrip> vehicleGroup in groupedByVehicle) {

        // This is where I get a red squiggly; under Key
        String vehicleId = groupedByVehicle.Key;

        gfx.DrawString(/* code goes foo */);
        
        foreach (PaidTrip trip in vehicleGroup) {
      
            gfx.DrawString(/* code goes foo */);
        }
    }
}

This is the error:

CS1061: IEnumerable<IGrouping<string, PaidTrip>> does not contain a definition for 'Key' and no accessible extension method 'Key' accepting a first argument of type 'IEnumerable<IGrouping<string, PaidTrip>>' could be found (are you missing a using directive or an assembly reference?)

CodePudding user response:

I need to bring along the row data of the other columns pertaining to the distinct vehicles in VehicleId, so that I can fill in details in my PDFs, and not just the summarized values.

If I understand you correctly, for each company (LicenseHolderId) you want their relevant Trip objects, but grouped by VehicleId - that's straightforward, just add another GroupBy - and you can iterate over them in an inner foreach:

List<Trip> paidTrips = ...

IEnumerable< IGrouping<String,Trip> > tripsGroupedByCompany = paidTrips.GroupBy( pt => pt.LicenseHolderId );

foreach( IGrouping<String,Trip> companyGroup in tripsGroupedByCompany )
{
    String licenseHolderId = companyGroup.Key;

    gfx.DrawString( "Company: "   licenseHolderId   "\r\n" );

    var groupedByVehicle = companyGroup.GroupBy( t => t.VehicleId );
    foreach( IGrouping<String,Trip> vehicleGroup in groupedByVehicle )
    {
        String vehicleId = vehicleGroup.Key;
        
        gfx.DrawString( "\tVehicle: "   vehicleId   "\r\n" );

        foreach( Trip trip in vehicleGroup )
        {
            gfx.DrawString( $"\t\tTrip: {trip.Year}-{trip.Month:00}. {trip.PaymentNet,11:C2}\r\n" );
        }
    }
}
  • I added some formatting instructions and characters which only really apply to text-mode (console applications), not PDF rendering, but if you're curious:

    • I used tab characters (\t) to indicate indent so related data is visually grouped.
    • I used formatting specifier :00 to ensure the Month value is displayed as a 2-digit value with a leading zero.
    • I used formatting specifier ,11:C2 to ensure the PaymentNet value is formatted as a Currency value with 2 decimal places, and is always left-padded to at-least 11 characters width.
  • This will give you output like this (below).

  • Note that while in your source-data each VehicleId has only a single trip associated with it, my code above allows a single VehicleId to have multiple trips with both the same, and different, LicenseHolderId values, though the sample print-out below only shows 1 trip per Vehicle.

Company: CompanyName1
    Vehicle: AG4203000002
        Trip: 2021-07. $107,088.68

    Vehicle: AG4203000003
        Trip: 2021-07. $138,761.32

Company: CompanyName2:
    Vehicle: AG4203000004
        Trip: 2021-07. $129,264.15

    Vehicle: AG4203000005
        Trip: 2021-07.  $87,273.58

But note that this code above is bad because it crosses concerns (it does 2 separate things: it traverses a non-trivial object-graph, and it renders data to your PDF library).

A better design would separate out the graph-traversal (perhaps to a single extension method) thus making the PDF rendering code much simpler, however I cannot give you any code examples of this without knowing more about your database design and if/how you're using EF.

  • Related