【问题标题】:Dictionary look up where we want the keys contained in a string字典查找我们希望字符串中包含的键的位置
【发布时间】:2023-03-14 12:37:01
【问题描述】:

我有一个包含密钥的dictionary,例如

"Car"
"Card Payment"

我有一个字符串description,例如“向 tesco 支付卡”,我想在字典中找到与该字符串对应的项目。

我试过这个:

var category = dictionary.SingleOrDefault(p => description.ToLowerInvariant().Contains(p.Key)).Value;

目前,这会导致字典中同时返回“汽车”和“信用卡付款”,并且我的代码像 SingleOrDefault 一样被炸毁。

我怎样才能实现我想要的?我考虑过在空格中为键添加前缀和后缀,但我必须对描述做同样的事情——我认为这会起作用,但它有点脏。有没有更好的方法?只要性能不会受到太大影响,我不反对将 Dictionary 更改为其他类型。

上面例子的必填结果:只得到“卡支付”

【问题讨论】:

  • 您期望的结果是什么?只得到"Card Payment"?
  • @D-Shih 是的
  • 你为什么不用FirstOrDefault() 而不是SingleOrDefault()
  • @Rick 好的,我知道你可以试试我的答案 :)

标签: c# linq dictionary


【解决方案1】:

您可以尝试在 where 条件之后使用 linq OrderByDescendingTake。找到最匹配的单词值。

var category = dictionary
               .Where(p => description.ToLowerInvariant().Contains(p.Key.ToLowerInvariant()))
               .OrderByDescending(x => x.Key.Length)
               .Take(1);

c# online


我会使用List<string> 来包含您的密钥,因为没有任何理由需要使用keyvalue 集合。

List<string> keys = new List<string>();
keys.Add("Car");
keys.Add("Card Payment");

string description = "Card payment to tesco";

var category = keys
        .Where(p => description.ToLowerInvariant().Contains(p.ToLowerInvariant()))
        .OrderByDescending(x => x.Length)
        .Take(1)
        .FirstOrDefault();

注意

OrderBykey values length desc 可以确定哪个key是最匹配的word value。

【讨论】:

  • 我应该解释说我在 Value 中有一个对象,稍后我会使用匹配的对象。我原来用的是List&lt;MyObject&gt;,性能很差
  • 我基本上使用了您的答案,但在查询之前我先订购了字典。这就像一个魅力。
【解决方案2】:

我在这里使用List&lt;string&gt; keysSystem.Text.RegularExpressions 找到所需的密钥。
试试看。

string description = "Card payment to tesco";
List<string> keys = new List<string> {
    {"Car" }, {"Card Payment" }
};
string desc = description.ToLowerInvariant( );
string pattern = @"([{0}]+) (\S+)";
var resp = keys.FirstOrDefault( a => {
    var regx = new Regex( string.Format( pattern, a.ToLowerInvariant( ) ) );
    return regx.Match( desc ).Success;
} );

在这里查看.NET Fiddle

【讨论】:

    【解决方案3】:

    你在滥用字典。通过扫描键,您不会从字典中获得性能提升。更糟糕的是,在这种情况下,一个简单的列表会更快。如果您通过键查找值,则字典接近恒定时间访问 (O(1))。

    if (dictionary.TryGetValue(key, out var value)) { ...
    

    为了能够利用这一优势,您需要一种更微妙的方法。主要困难是有时键可能包含多个单词。因此,我建议采用两级方法,在第一级存储单个单词键,在第二级存储组合键和值。

    示例:要存储的键值对:

    ["car"]: categoryA
    ["card payment"]: categoryB
    ["payment"]: categoryC
    

    我们将字典构建为

    var dictionary = new Dictionary<string, List<KeyValuePair<string, TValue>>> {
        ["car"] = new List<KeyValuePair<string, TValue>> {
            new KeyValuePair("car", categoryA)
        },
        ["card"] = new List<KeyValuePair<string, TValue>> {
            new KeyValuePair("card payment", categoryB)
        },
        ["payment"] = new List<KeyValuePair<string, TValue>> {
            new KeyValuePair("card payment", categoryB),
            new KeyValuePair("payment", categoryC)
        }
    };
    

    当然,实际上,我们会使用算法来做到这一点。但这里的重点是展示结构。如您所见,主键"payment" 的第三个条目包含两个条目:一个用于"card payment",另一个用于"payment"

    加值的算法是这样的:

    1. 将要输入的键拆分为单个单词。
    2. 对于每个单词,使用该单词作为主键创建一个字典条目,并将键值对存储在列表中作为字典值。这第二个键是可能由几个单词组成的原始键。

    您可以想象,第 2 步要求您测试是否已经存在具有相同主键的条目。如果是,则将新条目添加到现有列表中。否则,创建一个包含单个条目的新列表并将其插入到字典中。

    检索这样的条目:

    1. 将要输入的键拆分为单个单词。
    2. 对于每个单词,使用 true 检索现有字典条目,因此可以快速字典查找 (!) 到 List&lt;List&lt;KeyValuePair&lt;string, TValue&gt;&gt;&gt;
    3. 使用SelectMany 将此列表列表扁平化为单个List&lt;KeyValuePair&lt;string, TValue&gt;&gt;
    4. 按key长度降序排列,测试描述是否包含key。找到的第一个条目就是结果。

    您也可以将第2步和第3步结合起来,直接将单个字典条目的列表条目添加到主列表中。

    【讨论】:

      猜你喜欢
      • 2016-03-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-12
      • 2011-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-24
      相关资源
      最近更新 更多