Mihaimyh
Mihaimyh

Reputation: 1410

Using Epplus to generate xlsx with nested list

I am using EPPlus to generate a xlsx document.

My model below includes a List<string> and this is where things got complicated for me:

                var tableBody = worksheet.Cells["B2:B2"].LoadFromCollection(
                    from f in saaOperators.OrderBy(x => x.Identifier).Where(t => t.Identifier.StartsWith(worksheet.Name) ||

                    (t.Identifier.StartsWith("W") && worksheet.Name.Equals("BIR")))

                    from u in f.Units

                    select new { f.Identifier, f.Name, f.Profile, u }, true);

If I am doing select new {f.Identifier, f.Name, f.Profile, f.Units } it returns only the first item in the Units list.

If I am doing select new { f.Identifier, f.Name, f.Profile, u } for every item in Units it creates new rows with duplicate Identifier, Name and Profile.

Here is my model class:

public class SaaOperator
{
    public string Identifier { get; set; }
    public string Name { get; set; }
    public string Profile { get; set; }
    public List<string> Units { get; set; } = new List<string>();
}

What is the proper way to identify cells with the same value for Identifier, Name and Profile and merge them?

For example, in the screenshot below I need to merge B3:B4, B5:B6, C3:C4, C5:C6, D3:D4, D5:D6.

I know I can use worksheet.Cells["B3:B4"].Merge = true;, but I need a way to programatically identify the start and end cell with the same value.

EDIT 1: Added a screenshot with duplicated values. duplicated values.

EDIT 2 - Based on Ernie's comment, I need a way to programatically search the worksheet for cells with the same value and merge them.

Upvotes: 1

Views: 2054

Answers (2)

Ernie S
Ernie S

Reputation: 14250

Based on the comments, I would avoid the LoadFromCollection method and do a traditional for since what you are doing is too specific. And I would also avoid trying to merge cells since that makes thing unnecessarily complicated. This should do it:

var data = saaOperators
    .Where(t => t.Identifier.StartsWith(worksheet.Name) || (t.Identifier.StartsWith("W") && worksheet.Name.Equals("BIR")))
    .OrderBy(x => x.Identifier)
    .ToList();

var r = 2;

worksheet.Cells[r, 1].Value = "Identifier";
worksheet.Cells[r, 2].Value = "Name";
worksheet.Cells[r, 3].Value = "Profile";
worksheet.Cells[r, 4].Value = "Unit";
r++;

for (var i = 0; i < data.Count; i++)
{
    var op = data[i];
    worksheet.Cells[r + i, 1].Value = op.Identifier;
    worksheet.Cells[r + i, 2].Value = op.Name;
    worksheet.Cells[r + i, 3].Value = op.Profile;

    if (!op.Units.Any())
        continue;

    for (var j = 0; j < op.Units.Count; j++)
        worksheet.Cells[r + i + j, 4].Value = op.Units[j];

    r += op.Units.Count - 1;
}

Upvotes: 2

Scott Hannen
Scott Hannen

Reputation: 29222

Here's an approach:

Given a List<SaaOperator> or other IEnumerable<SaaOperator>:

foreach (var saaOperator in saaOperators)
{
    string[] valuesToDisplay;
    for (var x = 0; x < saaOperator.Units.Count; x++)
    {
        if (x == 0) // show all the properties
            valuesToDisplay = new[]
                {saaOperator.Identifier, 
                 saaOperator.Name, 
                 saaOperator.Profile, 
                 saaOperator.Units[x]};
        else // after the first unit, don't repeat the other properties
            valuesToDisplay = new[]
                {"", "", "", saaOperator.Units[x]};
    }
}

In other words,

  • iterate over all of the items in the list
  • for each item, iterate over all of the values in the Units list.
  • If it's the first item, display all the properties. If it's not the first item, only display the unit.

Upvotes: 0

Related Questions