linq 在C#中合并列表中的两个对象的最佳方法是什么?

daupos2t  于 2023-11-14  发布在  C#
关注(0)|答案(2)|浏览(142)

此问题在此处已有答案

Select multiple fields group by and sum(4个回答)
上个月关门了。
我有一个下面的列表,其中LineId可以是相同的。在这种情况下,我想合并它们,它们是共同的。即,如果LineId是相同的,然后总结PriceTotalAccountingTotal和合并Description文本。
我认为如果这个列表很大,我所做的方式不是最佳的。

  • 我如何才能使这种性能有效?
  • 什么是更好的方法来实现同样的结果?
{
  "Lines": [
    {
      "Id": 1,
      "AccountingTotal": 10.2,
      "Description": "Description 1",
      "Price": "10.00",
      "Total": "10.00",
      "LineId": "803"
    },
    {
      "Id": 2,
      "AccountingTotal": 10,
      "Description": "Description 2",
      "Price": "10.00",
      "Total": "10.00",
      "LineId": "804"
    },
    {
      "Id": 3,
      "AccountingTotal": 10.2,
      "Description": "Description 3",
      "Price": "10.00",
      "Total": "10.00",
      "LineId": "803"
    }
  ]
}

字符串
期望列表:

{
  "Lines": [
    {
      "Id": 1,
      "AccountingTotal": 20.4,
      "Description": "Description 1 | Description 3",
      "Price": "20.00",
      "Total": "20.00",
      "LineId": "803"
    },
    {
      "Id": 2,
      "AccountingTotal": 10,
      "Description": "Description 2",
      "Price": "10.00",
      "Total": "10.00",
      "LineId": "804"
    }
  ]
}


代码:

public class Line
{
    public int Id { get; set; }
    // This should be combination of id. i.e., "1|2|3", etc.
    public string MergedId { get; set; }
    public decimal? AccountingTotal { get; set; }
    public string Description { get; set; }
    public string Price { get; set; }
    public string Total { get; set; }
    public string LineId { get; set; }
}

// Data
var lines = new List<Line>
{
    new Line
    {
        Id = 1,
        AccountingTotal = (decimal?)10.2,
        Description = "Description 1",
        Price = "10.00",
        Total = "10.00",
        LineId = "803"
    },
    new Line
    {
        Id = 2,
        AccountingTotal = (decimal?)10.2,
        Description = "Description 2",
        Price = "10.00",
        Total = "10.00",
        LineId = "804"
    },
    new Line
    {
        Id = 3,
        Description = "Description 3",
        Price = "10.00",
        Total = "10.00",
        LineId = "803"
    }
};

var objNew = new List<Line>();
for (var i = 0; i < lines.Count; i++)
{
    for (var j = i + 1; j < lines.Count; j++)
    {
        if (lines[i].LineId == lines[j].LineId)
        {
            lines[i].AccountingTotal += Math.Round(lines[i].AccountingTotal + lines[j].AccountingTotal, 2);
            lines[i].Price  = (Convert.ToDouble(lines[i].Price) + Convert.ToDouble(lines[j].Price)).ToString(CultureInfo.InvariantCulture);
            lines[i].Total = (Convert.ToDouble(lines[i].Total) + Convert.ToDouble(lines[j].Total)).ToString(CultureInfo.InvariantCulture);
            lines[i].Description += "|" + lines[j].Description;
        }
    }

    objNew.Add(lines[i]);
}

// Pick first out of many dup ids
var result = objNew.GroupBy(x => x.LineId).Select(x => x.First()).ToList();

ncecgwcz

ncecgwcz1#

合并是可能的.不那么复杂的方式。
如果你可以只通过LineId对“相似”的Line进行分组,那么在分组之后,你可以使用Aggregate将组中每个Line到第一个Line的值相加:

var groupedLines = lines.GroupBy(line => line.LineId)
                        .Select(linesGroup => linesGroup.Aggregate((firstLine, nextLine) =>
{
    // firstLine is first line in each group.
    // nextLine is second and other lines in same group
    // Aggregation uses first line as base and iterating through
    // each other line in group with code below (which sums properties)
  
    // Sum up Price
    if (double.TryParse(firstLine.Price, NumberStyles.Any, CultureInfo.InvariantCulture, out var firstLinePrice) 
        && double.TryParse(nextLine.Price, NumberStyles.Any, CultureInfo.InvariantCulture, out var nextLinePrice))
        firstLine.Price = (firstLinePrice + nextLinePrice).ToString("0.00", CultureInfo.InvariantCulture);

    // Sum up Total
    if (double.TryParse(firstLine.Total, NumberStyles.Any, CultureInfo.InvariantCulture, out var firstLineTotal) 
        && double.TryParse(nextLine.Total, NumberStyles.Any, CultureInfo.InvariantCulture, out var nextLineTotal))
        firstLine.Total = (firstLineTotal + nextLineTotal).ToString("0.00", CultureInfo.InvariantCulture);

    // Sum up AccountingTotal
    firstLine.AccountingTotal = Math.Round(firstLine.AccountingTotal + nextLine.AccountingTotal, 2);
    // Concat Description
    firstLine.Description += " | " + nextLine.Description;

    return firstLine;
})).ToList();

字符串
代码看起来很乱,因为你把十进制值存储为字符串,为了“安全”转换,我使用了TryParse。如果你把PriceTotal改为decimaldouble,甚至可以为空,代码会短得多。
我的意思是,如果你允许更改属性类型,你可以将代码缩小到:

public class Line
{
    public int Id { get; set; }
    public double AccountingTotal { get; set; }
    public string? Description { get; set; }
    public double? Price { get; set; }
    public double? Total { get; set; }
    public string? LineId { get; set; }

    public Line AddValuesFrom(Line other)
    {
        Price += other.Price;
        Total += other.Total;
        AccountingTotal += other.AccountingTotal;
        Description += $" | {other.Description}";

        return this;
    }
}

// ...

var lines = // new List<Line>(); or how you get them
var groupedLines = lines.GroupBy(line => line.LineId)
                   .Select(linesGroup => 
                       linesGroup.Aggregate((firstLine, nextLine) => 
                          firstLine.AddValuesFrom(nextLine)))
                   .ToList();

eit6fx6z

eit6fx6z2#

这里有一个方法,首先使用分组,然后根据您的需求进行“合并”:

var newLines = new List<Line>();
var grouped = lines.GroupBy(x => x.LineId);
foreach (var group in grouped) {
    newLines.Add(new Line
    {
        AccountingTotal = group.Select(c => c.AccountingTotal).Sum(),
        Description = string.Join("|", group.Select(c => c.Description)),
        Price = Math.Round(
            group.Select(c => Convert.ToDouble(c.Price)).Sum(), 2)
            .ToString(CultureInfo.InvariantCulture),
        Total = Math.Round(
            group.Select(c => Convert.ToDouble(c.Total)).Sum(), 2)
            .ToString(CultureInfo.InvariantCulture),
        LineId = group.Key
    });
}

字符串

相关问题