【问题标题】:Finding values in a string:string dictionary where a given filter string is a subset of the key string在字符串:字符串字典中查找值,其中给定的过滤器字符串是键字符串的子集
【发布时间】:2019-11-21 12:08:57
【问题描述】:

我有一个 Dictionary<string, string> 对象,其中存储的值如下所示:

examplePlanet                   : defaultText0
examplePlanet*                  : defaultText1
examplePlanet**                 : defaultText2
examplePlanetSpecificlocationA  : specificAText0
examplePlanetSpecificlocationA* : specificAText1
examplePlanetSpecificlocationB  : specificBText

我有一个字符串 filter 匹配这些键之一或者是键的子集。 该过滤器的形式为planetLocation,可拆分为planet和location。

我的目标是以这种方式创建过滤器匹配的值列表:如果字典中存在planetLocation,则将其值以及键匹配但具有额外*的所有值添加到列表中。 如果planetLocation 不存在,则仅添加键与过滤器的planet 部分匹配的值(可能有额外的*)。

基本上,我希望过滤器与键尽可能匹配的所有值。


例子:

examplePlanetSpecificlocationA 给[specificAText0, specificAText1]
examplePlanetSpecificlocationB 给[specificBText]
examplePlanetSpecificlocationC 给[defaultText0, defaultText1, defaultText2]


我已经尝试过(以及其他不起作用的方法):

private List<string> filteredResults;

///<summary>Filters dictionaries and returns a list of values</summary>
private List<string> GetFilteredResults(Dictionary<string, string> inputdictionary, string filter)
{
    List<string> _filteredResults = new List<string>();
    foreach (KeyValuePair<string, string> entry in inputdictionary)
    {
        if (entry.Key.Contains(filter))
        {
            _filteredResults.Add(entry.Value);
        }
    }
    return _filteredResults;
}

public void main()
{
    //stuff happens here that assigns a value to filterPlanet and filterLocation

    filteredResults = new List<string>();
    filteredResults = GetFilteredResults(exampledictionary, filterPlanet + filterLocation);
    if (filteredResults.Count == 0)
    {
        filteredResults = GetFilteredResults(exampledictionary, filterPlanet);
    }

    //do stuff with the filtered results
}

这几乎可行,但返回键包含 filterPlanet 的所有值,而不仅仅是 filterPlanet 本身加上可能的 *。我不知道如何让这个函数做我想做的事,即使它以某种方式工作,我相信还有比这更有效的过滤方式。你能帮帮我吗?

【问题讨论】:

  • 为什么“examplePlanetSpecificlocationC”匹配“examplePlanet”?为什么“examplePlanetSpecificlocationA”匹配“examplePlanetSpecificlocationA*”?
  • @canton7 examplePlanetSpecificlocationC 在字典中不存在,因此应返回默认值,该值存储在具有相同名称但没有位置位的键中。编辑:examplePlanetSpecificlocationA 始终与 examplePlanetSpecificlocationA* 匹配,因为第一个是后者的子集,仅添加了星号。字典不能有两次相同的键,这似乎是一种解决方法。
  • 对,但是“examplePlanet”不以“”结尾?为什么“examplePlanetSpecificlocationA”匹配“examplePlanetSpecificlocationA”,如果它已经在“examplePlanetSpecificlocationA”中有一个特定的匹配?
  • 如果你能详细写出你的算法,使用多级列表或流程图会更好。详细说明应该采取哪些步骤,以及在所有情况下应该发生什么。这也将帮助您实现它。
  • 我想建议使用字符串字典不是解决此问题的正确方法。它必须是一个字符串字典(例如,您是否从外部源获取此确切数据)还是可以使用不同的数据结构?

标签: c# dictionary filter


【解决方案1】:

我不会使用*'s 作为区分同一键的多个值的一种方式,我会将filterPlanet 和filterLocation 分开。这样您就可以使用简单的 O(1) 字典查找,而不是遍历所有键、进行子字符串搜索等。

public class PlanetFilterer
{
    private readonly Dictionary<string, List<string>> lookup = new Dictionary<string, List<string>>();

    public PlanetFilterer(IEnumerable<(string filter, string value)> filters)
    {
        foreach (var (filter, value) in filters)
        {
            var filterWithoutStars = filter.TrimEnd('*');
            if (!lookup.TryGetValue(filterWithoutStars, out var values))
            {
                values = new List<string>();
                lookup[filterWithoutStars] = values;
            }
            values.Add(value);
        }
    }

    public IReadOnlyList<string> Lookup(string planet, string location)
    {
        List<string> results;
        if (lookup.TryGetValue(planet + location, out results))
        {
            return results; 
        }
        if (lookup.TryGetValue(planet, out results))
        {
            return results;
        }
        return Array.Empty<string>();
    }
}

用法:

var filters = new[]
{
    ("examplePlanet", "defaultText0"),
    ("examplePlanet*", "defaultText1"),
    ("examplePlanet**", "defaultText2"),
    ("examplePlanetSpecificlocationA", "specificAText0"),
    ("examplePlanetSpecificlocationA*", "specificAText1"),
    ("examplePlanetSpecificlocationB", "specificBText"),
};
var filterer = new PlanetFilterer(filters);

Console.WriteLine(string.Join(", ", filterer.Lookup("examplePlanet", "SpecificlocationA")));
Console.WriteLine(string.Join(", ", filterer.Lookup("examplePlanet", "SpecificlocationB")));
Console.WriteLine(string.Join(", ", filterer.Lookup("examplePlanet", "SpecificlocationC")));

Try it online

【讨论】:

  • 很遗憾无法控制输入,只能reshape。 .TrimEnd 绝对是我没有想到的东西,但我确信或者 @Longoon12000 的正则表达式会起作用。
  • @asdfCYBER 请注意我的代码如何以与您的问题相同的格式接受输入,然后对其进行处理。输入完全来自您的问题,输出与您的问题相匹配。如果这还不够好,请详细说明为什么。您已经说过“我可以将输入重塑为Dictionary&lt;string, List&lt;string&gt;&gt;,是的。”
  • 哎呀,我读过界面。这肯定行得通,我现在就试试。
  • 我最终使用了我的原始代码并将if (entry.Key.Contains(filter)) 替换为if (entry.Key.TrimEnd('*') == filter)。如果您多次使用它,您的解决方案会更优雅、更快,但我只需要过滤一次即可获得所需的内容。
  • @asdfCYBER 我会担心Contains,因为它可以匹配任何地方。 StartsWith 更好,但只搜索没有位置的星球要好得多。
猜你喜欢
  • 2016-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-13
相关资源
最近更新 更多