使用 Linq 按可选列分组

本文关键字:Linq 使用 | 更新日期: 2023-09-27 18:35:20

我有一个这样的xml:

<LiftFactors>
  <Products>
     <Product>
       <ProductId>Limella</ProductId>
       <Tactics>
         <Tactic>
           <Typ>PriceRed</Typ>
           <TPRFrom>0</TPRFrom>
           <TprThru>10</TprThru>
           <Lift>14</Lift>
           <VF>2012-01-09</VF>
           <VT>2012-01-11</VT>
        </Tactic>
        <Tactic>
           <Typ>PriceRed</Typ>
           <TPRFrom>10 </TPRFrom>
           <TprThru>20</TprThru>
           <Lift>30</Lift>
           <VF>2012-01-07</VF>
           <VT>2012-20-08</VT>
        </Tactic>
        <Tactic>
            <Typ>Display</Typ>
            <Lift>14</Lift> 
            <VF>2012-01-04</VF>
            <VT>2012-01-06</VT>
        </Tactic>
      </Tactics>
    </Product>
    <Product>
        <ProductId>Empower Cola</ProductId>
        <Tactics>
           <Tactic>
               <Typ>Display</Typ>
               <Lift>20</Lift>
               <VF>2012-01-01</VF>
               <VT>2012-01-08</VT>
           </Tactic>
        </Tactics>
    </Product>
  </Products>
</LiftFactors>

使用以下 linq 语句,我获得了按 ProductId 分组并按 ValidFrom 日期排序的策略数据:

var xml = XElement.Parse(theXML);
var d =  (from e in xml.Descendants(@"Product")
          group e by e.Element("ProductId").Value into Items
         select Items).ToDictionary 
         (x => x.Key, x => ((XElement)x.First()).Descendants("Tactic").ToList().OrderByDescending (y=> ((DateTime)y.Element("VF"))));

输出:

  Limella -> Tactic PriceRed 1
          -> Tactic PriceRed 2
          -> Tactic Display
  Empower Cola -> Tactic Display

现在假设"产品"节点是可选的,我可以在产品节点之外有额外的战术节点:

<LiftFactors>
  <Products>
     <Product>
       <ProductId>Limella</ProductId>
       <Tactics>
         <Tactic>
           <Typ>PriceRed</Typ>
           <TPRFrom>0</TPRFrom>
           <TprThru>10</TprThru>
           <Lift>14</Lift>
           <VF>2012-01-09</VF>
           <VT>2012-01-11</VT>
        </Tactic>
       </Tactics>
     </Product>
   </Products>
   <Tactics>
         <Tactic>
           <Typ>PriceRed</Typ>
           <TPRFrom>0</TPRFrom>
           <TprThru>10</TprThru>
           <Lift>14</Lift>
           <VF>2012-01-09</VF>
           <VT>2012-01-11</VT>
         </Tactic>
   </Tactics>
</LiftFactors>

现在我想要的是这个输出:

Limella -> Tactic 1
        -> ...
<Null>  -> Tactic 2
        -> ....

因此,战术也应该出现在没有分配密钥的组中。这是否可能仅使用一个 linq 查询?

使用 Linq 按可选列分组

试试这个:

var d = xml.Descendants("Tactics")
           .GroupBy(e=>e.Parent.Name.LocalName == "Product" ?
                       e.Parent.Element("ProductId").Value : "")
           .ToDictionary(x => x.Key, x => ((XElement)x.First())
                                     .Descendants("Tactic").ToList()
                                     .OrderByDescending (y=>(DateTime)y.Element("VF")));

注意:在Product外有Tactics的组有键作为empty string,您可以稍微更改代码(通过使用 If-else)将其更改为 null

以下内容似乎适用于您的示例 XML,这为您提供了一个匿名对象的可枚举集合,如果 Tactic产品中,则会填充这些对象的 id 字段,否则为 null:

var xml = XElement.Parse(contents);
var d = xml.Descendants("Tactic").Select(element => element.Parent != null ? new
    {
        id = element.Parent.Parent.Element("ProductId"),
        Tactic = new
            {
                Typ = element.Descendants("Typ").FirstOrDefault().Value,
                TPRFrom = element.Descendants("TPRFrom").FirstOrDefault().Value,
                TprThru = element.Descendants("TprThru").FirstOrDefault().Value,
                VF = element.Descendants("VF").FirstOrDefault().Value,
                VT = element.Descendants("VT").FirstOrDefault().Value
            }
    } : null);