Reputation: 669
I have a Dictionary<string, string>
object where values are stored that look like this:
examplePlanet : defaultText0
examplePlanet* : defaultText1
examplePlanet** : defaultText2
examplePlanetSpecificlocationA : specificAText0
examplePlanetSpecificlocationA* : specificAText1
examplePlanetSpecificlocationB : specificBText
And I have a string filter
that matches one of these keys or is a subset of a key.
This filter has the form planetLocation
, which can be split into planet
and location
.
My goal is to create a list of values where the filter is matched in this way: if planetLocation exists in the dictionary, add its value, and all values where the key matches but has extra *'s, to a list.
If planetLocation does not exist, only add values where the key matches the planet
part of the filter (with possible extra *'s).
Basically, I want all values where the filter matches the key as much as possible.
Examples:
examplePlanetSpecificlocationA
gives [specificAText0, specificAText1]
examplePlanetSpecificlocationB
gives [specificBText]
examplePlanetSpecificlocationC
gives [defaultText0, defaultText1, defaultText2]
I have already tried (among other things that didn't work) this:
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
}
This almost worked, but returns all values where the key contains filterPlanet and not just filterPlanet itself plus possible *'s. I'm not sure how to make this function do what I want, and even if it somehow works I'm sure there is a more efficient way of filtering than this. Could you please help me here?
Upvotes: 1
Views: 447
Reputation: 42245
I wouldn't use the *
's as a way of differentiating multiple values for the same key, and I'd keep filterPlanet
and filterLocation
separate. That way you can use a simple O(1) dictionary lookup, rather than iterating across all keys, doing substring searching, etc.
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>();
}
}
Usage:
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")));
Upvotes: 3