【问题标题】:Filter two lists on one property c# using linq使用 linq 在一个属性 c# 上过滤两个列表
【发布时间】:2015-08-24 08:15:13
【问题描述】:

我有两个对象,即 CardTransaction

Card:
public string CardID {get; set;}
public string TransactionRef {get; set;}

Transaction:
public string TxnID {get; set;}
public string TxnDetails {get; set;}

注意:TransactionRef 的格式为 Date|TxnID

我还有List<Card> cardDetailsList<Transaction> transDetails这两个对象的列表

cardDetails:
{CardID = '1', TransactionRef = '20150824|Guid1'}
{CardID = '2', TransactionRef = '20150824|Guid2'}
{CardID = '3', TransactionRef = '20150824|Guid3'}

transDetails:
{TxnID = '23', TxnDetails = 'Guid1'}
{TxnID = '24', TxnDetails = 'Guid2'}

我想根据 TxnDetails 使用 transDetails 过滤 cardDetails,以便它从第二个列表中过滤掉不包含 TxnDetails 的项目。

这应该是输出:

cardDetails:
 {CardID = '3', TransactionRef = '20150824|Guid3'}

我已经尝试过使用 linq:

  cardDetails = cardDetails.Where(x => transDetails.Any(y => x.TransactionRef.Contains(y.TxnDetails) == false)).ToList();

但它总是将列表返回为空白。我已经尝试了这个查询的许多变体,但都没有成功。我知道在搜索他们并尝试他们的解决方案之前和之后都曾提出过这个问题,但我仍然无法正确解决。

谁能建议我的查询有什么问题?

注意:我忘了提一件事,这些列表可以包含 1000 条记录。所以性能也很重要。

【问题讨论】:

  • 这个查询实际上应该返回所有卡片,你确定它没有返回吗?
  • 是的,它不返回任何东西。我只想返回 cardDetails 中的第三个条目,即它不包含第二个列表中的任何 TxnDetails
  • TxnDetails 是字符串时,为什么TxnDetails.ToString()
  • 这个查询的性能指数是 n 的平方。您应该避免在循环内出现这种循环。尝试加入两个列表,以便它使用哈希表。
  • @MatthewWatson 实际上在我的代码中 TxnDetails 是一个 Guid,我将其转换为字符串。这里我只是将 TxnDetails 更改为字符串,但没有更新 linq 查询

标签: c# linq


【解决方案1】:

应该这样做

var cards = 
    from card in cardDetails
    let txnDetails = GetTxnDetails(card)
    where ! transDetails.Any(t => t.TxnDetails == txnDetails)
    select card;


static string GetTxnDetails(Card card)
{
    return card.TransactionRef.Split('|')[1];
}

小提琴:https://dotnetfiddle.net/b9ylFe


对此进行优化的一种方法是将所有可能的交易详细信息预先存储在哈希集中。然后查找应该非常接近 O(1)(假设哈希码分布公平)而不是 O(n) - 将算法的整体复杂性从 O(n * k) 降低到 O(n + k)。

var allTxnDetails = new HashSet<string>(transDetails.Select(t => t.TxnDetails));

var cards = 
    from card in cardDetails
    let txnDetails = GetTxnDetails(card)
    where ! allTxnDetails.Contains(txnDetails)
    select card;

小提琴:https://dotnetfiddle.net/hTYCbj

【讨论】:

  • 你也可以在这种特殊情况下使用EndsWith()
【解决方案2】:

这个查询应该可以解决问题:

// Get all card details whose transactionrefs don't contain txndetails from the second list
cardDetails.Where(cd => transDetails.All(ts => !cd.TransactionRef.EndsWith(ts.TxnDetails)))
    .ToList();

但是,您在一个字段中合并两条数据有什么具体原因吗?我建议将Card 类中的TransactionRef 字段分成两个字段:TransactionDateTransactionID,以避免查询中的字符串操作。

【讨论】:

  • 是的,这是最简单的解决方案。实际上,问题是 Any 应该变成 All
  • @SaedAmini 实际上我正在从 azure 数据库中获取所有数据,并且它仅采用这种格式。我主要关心的是列表中的 100K 条目的性能如何
【解决方案3】:

这个怎么样?

var results = cardDetails.Where(
    card => !transDetails.Any(
        trans => card.TransactionRef.EndsWith("|" + trans.TxnDetails)));

完整演示:

using System;
using System.Linq;

namespace Demo
{
    class Card
    {
        public string CardID;
        public string TransactionRef;
    }

    class Transaction
    {
        public string TxnID;
        public string TxnDetails;
    }

    internal class Program
    {
        private static void Main()
        {
            var cardDetails = new[]
            {
                new Card {CardID = "1", TransactionRef = "20150824|Guid1"},
                new Card {CardID = "2", TransactionRef = "20150824|Guid2"},
                new Card {CardID = "3", TransactionRef = "20150824|Guid3"}
            };

            var transDetails = new[]
            {
                new Transaction {TxnID = "23", TxnDetails = "Guid1"},
                new Transaction {TxnID = "24", TxnDetails = "Guid2"}
            };

            var results = cardDetails.Where(card => !transDetails.Any(trans => card.TransactionRef.EndsWith("|" + trans.TxnDetails)));

            foreach (var item in results)
                Console.WriteLine(item.CardID + ": " + item.TransactionRef);    
        }
    }
}

【讨论】:

    【解决方案4】:

    对 LINQ 使用方法链接语法:

    List<Card> result = cardDetails.Where(
        card => !transDetails.Exists(
             tran => tran.TxnDetails == card.TransactionRef.Split('|')[1]
    )).ToList();
    

    您的查询有什么问题?

     cardDetails = cardDetails.Where(x => transDetails.Any(y => x.TransactionRef.Contains(y.TxnDetails) == false)).ToList();
    

    这是你写的:

    找到所有满足此条件的卡片: 我的交易列表中是否有任何交易在此特定交易的 TxnDetails 中无法在此特定卡的 TxnDetails 中找到?

    我可以在这里看到问题:

    如果任何交易有另一个 TxnId 而不是卡(机会很高),则返回此卡。

    因此,如果您的交易列表中至少有 2 个不同的交易 ID,则基本上您应该从查询中获取所有卡片

    【讨论】:

      【解决方案5】:

      这只是一个括号问题,== false 应该在)) 之后而不是第一个结束。

      cardDetails = cardDetails.Where(x => transDetails.Any(y => x.TransactionRef.Contains(y.TxnDetails)) == false).ToList();
      

      因为你的实际代码,你只是做你想要的相反!

      你也可以

      cardDetails = cardDetails.Where(x => !transDetails.Any(y => x.TransactionRef.Contains(y.TxnDetails))).ToList();
      

      或任何建议的改进,但您的代码基本上与正确的代码非常接近;)

      【讨论】:

      • 是的,你是对的! :) 我改变了它,它的工作原理。谢谢
      【解决方案6】:

      如果性能很重要,我建议您应该首先给类 Card 一个属性,该属性返回“|”之后的部分特点。根据您希望执行此查询的频率与构建 Card 的频率相比,甚至可能希望让构造函数将 transactionRef 分成“|”之前的一部分和'|'之后的一部分。

      您选择哪种方法对查询并不重要。假设 Card 类有一个属性:

      string Guid {get {return ...;}
      

      我知道您想要一个序列 cardDetails 中所有卡片的序列,这些卡片的 Guid 不等于 transDetails 序列中交易的任何 TxnDetails。

      或者换句话说:如果您要在 TxnDetails 中创建所有已使用 guid 的序列,您希望 CardDetails 中的所有卡片的 guid 不在所有已使用 guid 的序列中。

      您可以为此使用 Any(),但这意味着您必须在 transDetails 序列中搜索您想要检查的每张卡片。

      当您必须检查任何特定项目是否在序列中时,最好将序列转换为 Dictionary 或 HashSet 一次。无论您创建哪个,都取决于您是只需要密钥还是拥有密钥的元素。只创建一次字典/散列集,并以非常快的速度搜索带有键的项目。

      在我们的例子中,我们只想要一个带有已使用 guid 的序列,它在哪个事务中使用并不重要。

      var usedGuids = transDetails.Select(transDetail => transDetail.TxnDetails).Distinct();
      var hashedGuids = new HashSet(usedGuids);
      

      (我做了两个声明以便更容易理解所做的事情)

      现在,只要我有一个 GUID,我就可以非常快速地检查它是否被使用:

      bool guidIsUsed = usedGuids.Contains(myGuid);
      

      因此,您在 cardDetails 中的卡片序列,其 GUID 不在 transDetails 中:

      var hashedGuids = new HashSet(transDetails.Select(transDetail => transDetail.TxnDetails).Distinct());
      var requestedCards = cardDetails.Where(card => !hashedGuids.Contains(card.Guid));
      

      【讨论】:

        猜你喜欢
        • 2013-06-23
        • 1970-01-01
        • 1970-01-01
        • 2021-12-28
        • 2021-02-06
        • 2021-04-06
        • 1970-01-01
        • 1970-01-01
        • 2018-08-11
        相关资源
        最近更新 更多