【问题标题】:Querying a list to find the count of an ID and merge the contents of two similar IDs查询列表以查找 ID 的计数并合并两个相似 ID 的内容
【发布时间】:2015-07-23 22:46:35
【问题描述】:

我有一个通用 C# 列表 List<object> Results,其中有几个字段,其中包含几行数据,如下所示

List<object> Results

数据:

TrdID    Date       Price Seller Buyer  Side
1000     7/23/2015   1     ABC    NULL   2 
1000     7/23/2015   1     NULL   XYZ    1
1002     7/22/2015   1.5   NULL   ABC    1
1002     8/22/2015   1.5   NULL   ABC    1
1002     7/22/2015   1.5   XYZ    NULL   2
1002     8/22/2015   1.5   XYZ    NULL   2
1010     8/23/2015   2     ACB    NULL   2 
1010     8/23/2015   2     NULL   PQR    1

上面的列表中有 ID 重复的记录,我想合并 IDs 重复两次的记录。并且具有相同IDs 的两条记录将具有相同的值,除了SellerBuyer 其中SellerNULLSide=1Buyer 在 Side=2 时为 NULL 并将它们合并到单个记录中通过将 NULL 值替换为其后续的另一侧,结果列表将如下所示

预期结果

TrdID    Date       Price Seller Buyer  
1000     7/23/2015   1     ABC    XYZ  
1010     8/23/2015   2     ACB   PQR

从上面的结果可以看出IDs 1000 and 1010被重复了两次,所以它合并了它们的每一个SellerBuyer的值并且ID 1002被丢弃,因为它们的Count不是2

我可以知道解决这个问题的方法吗?

【问题讨论】:

  • 您的 List 中的类型是什么?
  • 什么是计数(在你的最后一句话中)?
  • Count 是特定 ID 重复的次数

标签: c# linq list


【解决方案1】:

你可以使用GroupBy和其他LINQ方法:

Results = Result.GroupBy(x => new { x.TrdID, x.Date, x.Price })
    .Where(g => g.Count() == 2)
    .Select(g => new object 
    {
          TrdID  = g.Key.TrdID,
          Date   = g.Key.Date,     
          Price  = g.Key.Price,
          Seller = g.First(x => x.Seller != null).Seller,
          Buyer  = g.First(x => x.Buyer  != null).Buyer 
    })
    .ToList();

这假定总是有一个不为空的Seller,并且总是有一个不为空的Buyer。如果不是这种情况,您会得到一个有意义的InvalidOperationException:“序列不包含匹配元素”。

因此,如果可能的话,您可以分配 null 而不是使用属性:

....
Seller = g.Where(x => x.Seller != null)
          .Select(x => x.Seller)
          .FirstOrDefault(),
Buyer  = g.Where(x => x.Buyer != null)
          .Select(x => x.Buyer)
          .FirstOrDefault(),

【讨论】:

  • 如果 OP 没有从 List&lt;object&gt; Results 更改他的列表,这将不起作用
  • @DavidG:我不明白。他不改变就不行,双重否定,那么它真的有效吗?
  • 哈哈好吧,撇开狡猾的语法不谈,这在目前的形式下不起作用:)
  • 源(即Result)是List&lt;object&gt;,这意味着你不能做x.TrdID。对不起,我只是一个书呆子!
  • @Dev 这就是我在回答中的建议:)
【解决方案2】:

假设你的对象在一个匹配这样的类中:

public class Result
{
    public int TrdID { get; set; }
    public DateTime Date { get; set; }
    public decimal Price { get; set; }
    public string Seller { get; set; }
    public string Buyer { get; set; }
}

然后你可以通过TrdID值分组来查询你的列表:

var groupedResults = Results
    .GroupBy(r => new { r.TrdID, r.Date, r.Price })
    .Where(g => g.Count() == 2)
    .Select(g => new Result
    {
        TrdID = g.Key.TrdID,
        Date = g.Key.Date,
        Price = g.Key.Price,
        Seller = g.First(x => x.Seller != null).Seller,
        Buyer = g.First(x => x.Buyer != null).Buyer
    });

注意:您需要将 Results 变量设置为 List&lt;Result&gt; 才能正常工作,或者先使用 Results.Cast&lt;Result&gt;()

【讨论】:

  • 一点点挑剔:在这种情况下使用FirstOrDefault 毫无意义,因为它不会阻止您使用NullReferenceException 是否没有Seller/Buyer 不为空。这是一个糟糕的交易,您正在将一个有意义的异常(InvalidOperationException,“序列不包含匹配元素”)更改为另一个意义不大的异常。
  • 'g.FirstOrDefault(x => x.Seller != null).Seller' 不合逻辑。您可能希望该组有 1 个不为空的卖家的条目。如果是这样,您应该使用“First”而不是“FirstOrDefault”。如果您不想做出这种假设,则需要将 Seller 属性设置为 null,以防组中的所有记录都没有定义的 Seller。
  • @TimSchmelter 好地方,不会造成问题,但最好没有OrDefault
  • @HashPsi 这是一种更复杂的方式来准确表达 Time 刚刚所说的内容!
  • @Dev 尝试使用ToList() 然后
【解决方案3】:

试试这样的:

(from r in Results
group r by TrdID into g
where g.Count() == 2
let g1 = g.First()
let g2 = g.Last()
select new {
    TrdId = g.Key,
    Date = g1.Date,
    Price = g1.Price,
    Seller = g1.Seller ?? g2.Seller,
    Buyer = g1.Buyer ?? g2.Buyer
})

需要键入“结果”列表,以便在查询中可以使用“TrdId”、“日期”等字段。

【讨论】:

  • 这是假设列表将按特定顺序排列,因此可能无法正常工作。此外,它会出错,因为源是object 的列表。
  • @DavidG 是的,在评论的打字部分。结果应正确键入。没有订单评论,因为预计只有 2 个条目具有相同的 TrdId,并且卖方和买方以外的字段对于两个条目都应相同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-05
  • 2020-05-01
  • 2023-03-08
  • 1970-01-01
  • 2020-02-11
  • 2021-12-31
  • 1970-01-01
相关资源
最近更新 更多