处理2个清单

时间:2014-10-17 00:07:27

标签: c# linq

我有xml文件来读取和处理数据。我有详细记录清单。我的要求是删除具有相同Number和RefNo但具有正值和负值的记录。

场景1:应返回1条记录

<Detail>
    <Number>1</Number>
    <Amount>20.0</Amount>
    <RefNo>1</RefNo>
</Detail>

场景2:应返回0记录,因为数量为正数且为负数

<Detail>
    <Number>1</Number>
    <Amount>20.0</Amount>
    <RefNo>1</RefNo>
</Detail>
<Detail>
    <Number>1</Number>
    <Amount>-20.0</Amount>
    <RefNo>1</RefNo>
</Detail>

我通过使用2个正负值列表来实现它。我使用以下代码解决了上述场景。

var actualRecords =
    (from detailList in positiveDetails
    where !negativeDetails.Any(x => x.Number == detailList.PolicyNo
                                 && x.RefNo == detailList. RefNo)
    select detailList).ToList(); 

但是上面的代码在以下场景中返回零记录。 正面&gt;否定&gt;正。对于相同的Number和RefNo,它应该返回1个记录,取消1个正面和1个负面记录。

我想要解决以下具有相同编号和参考号的场景

•正面&gt;否定&gt;正面 - 1记录
•正面&gt;否定&gt;正面&gt;否定 - 0记录
•正面&gt;否定&gt;正面&gt;否定&gt;正面 - 1记录
•正面&gt;否定&gt;正面&gt;否定&gt;正面&gt;否定 - 0记录
•正面&gt;否定&gt;正面&gt;否定&gt;正面&gt; Ñ

假设 负值始终出现在正值之后。我可以通过for循环来做到这一点。但我正在寻找更好的解决方案。 感谢有人可以帮助我。

2 个答案:

答案 0 :(得分:0)

我猜一种方法可能是GroupBy NumberRefNo然后Count正面和负面Amount如果计数不匹配然后返回那些Detail元素

类似的东西:

var actualRecords = detailList.GroupBy(x => new{ x.Number, x.RefNo}) // group by Number and RefNo
                              .Where(x => x.Count(p => p.Amount > 0) // count positives
                                       != x.Count(n => n.Amount < 0)) // count negatives
                              .SelectMany(detail => detail); // contains the Details that have unequal amounts of positive and negative amounts

答案 1 :(得分:0)

您是仅仅汇总细节还是实际尝试匹配相互抵消的细节?前者会更容易写。

例如,对于看起来像这样的文档

<Root>
    <Details>
        <Detail>
            <Number>1</Number>
            <Amount>40.0</Amount>
            <RefNo>1</RefNo>
        </Detail>
        <Detail>
            <Number>1</Number>
            <Amount>-20.0</Amount>
            <RefNo>1</RefNo>
        </Detail>
        <Detail>
            <Number>1</Number>
            <Amount>20.0</Amount>
            <RefNo>1</RefNo>
        </Detail>
        <Detail>
            <Number>1</Number>
            <Amount>-30.0</Amount>
            <RefNo>2</RefNo>
        </Detail>
        <Detail>
            <Number>1</Number>
            <Amount>20.0</Amount>
            <RefNo>1</RefNo>
        </Detail>
    </Details>
</Root>
XDocument doc = ...;
var details = doc.XPathSelectElement("/Root/Details");
var newDetails =
    from detail in details.Elements("Detail")
    let amount = (decimal)detail.Element("Amount")
    let number = (int)detail.Element("Number")
    let refNo = (int)detail.Element("RefNo")
    // group amounts by number and refno
    group amount by new { number, refNo } into g
    let amount = g.Sum()
    // filter out completely canceled out groups
    where amount != 0M
    select new XElement("Detail",
        new XElement("Number", g.Key.number),
        new XElement("Amount", amount.ToString("N1")),
        new XElement("RefNo", g.Key.refNo)
    );
details.ReplaceAll(newDetails);

收率:

<Root>
  <Details>
    <Detail>
      <Number>1</Number>
      <Amount>60.0</Amount>
      <RefNo>1</RefNo>
    </Detail>
    <Detail>
      <Number>1</Number>
      <Amount>-30.0</Amount>
      <RefNo>2</RefNo>
    </Detail>
  </Details>
</Root>

否则,您需要匹配相互抵消的元素。它仍然可以在一个查询中完成,但稍微复杂一点。

XDocument doc = ...;
var details = doc.XPathSelectElement("/Root/Details");
var newDetails =
    from detail in details.Elements("Detail")
    let amount = (decimal)detail.Element("Amount")
    let number = (int)detail.Element("Number")
    let refNo = (int)detail.Element("RefNo")
    let key = Math.Abs(amount) // cancellable amounts
    // group amounts by key, number and refno
    group amount by new { key, number, refNo } into g
    let amount = g.Sum()
    // filter out completely canceled out groups
    where amount != 0M
    let count = (int)Math.Abs(amount / g.Key.key) // how many to recreate
    let sign = Math.Sign(amount)
    // recreate uncancelled values
    from a in Enumerable.Repeat(sign * g.Key.key, count)
    select new XElement("Detail",
        new XElement("Number", g.Key.number),
        new XElement("Amount", a.ToString("N1")),
        new XElement("RefNo", g.Key.refNo)
    );
details.ReplaceAll(newDetails);

收率:

<Root>
  <Details>
    <Detail>
      <Number>1</Number>
      <Amount>40.0</Amount>
      <RefNo>1</RefNo>
    </Detail>
    <Detail>
      <Number>1</Number>
      <Amount>20.0</Amount>
      <RefNo>1</RefNo>
    </Detail>
    <Detail>
      <Number>1</Number>
      <Amount>-30.0</Amount>
      <RefNo>2</RefNo>
    </Detail>
  </Details>
</Root>