Value lookup using key or vice versa - c#

First of all, apologies for the nasty title. I will correct it later.
I have some data like below,
"BOULEVARD","BOUL","BOULV", "BLVD"
I need a data structure that is O(1) for looking up any of this words by other. For example, if I use a dictionary I would need to store this keys/values like this, which looks odd to me,
abbr.Add("BLVD", new List<string> { "BOULEVARD","BOUL","BOULV", "BLVD" });
abbr.Add("BOUL", new List<string> { "BOULEVARD", "BOUL", "BOULV", "BLVD" });
abbr.Add("BOULV", new List<string> { "BOULEVARD", "BOUL", "BOULV", "BLVD" });
abbr.Add("BOULEVARD", new List<string> { "BOULEVARD", "BOUL", "BOULV", "BLVD" });
Which data structure to use to keep this data appropriate to my querying terms?
Thanks in advance

Create two HashMap - one maps word to a group number. And the other one maps group number to a list of words. This way you save some memory.
Map<String, Integer> - Word to Group Number
Map<Integer, List<String>> - Group Number to a list of words
You need two O(1) lookups - first to get the group number and then by it - get the list of words.

Assuming abbr is a Dictionary<String, IEnumerable<String>>, you could use the following function:
public static void IndexAbbreviations(IEnumerable<String> abbreviations) {
for (var a in abbreviations)
abbr.Add(a, abbreviations);
}
This will populate the dictionary with the provided list of abbreviations such that when any of them is looked up in the dictionary. It is slightly better than the example code you provided, because I am not creating a new object for each value.
From the documentation, "Retrieving a value by using its key is very fast, close to O(1), because the Dictionary(Of TKey, TValue) class is implemented as a hash table."

The choice of dictionary looks fine to me. As mentioned above, you should use the same list to be referenced in the dictionary. The code could go something like this:
var allAbrList = new List<List<string>>
{
new List<string> {"BOULEVARD", "BOUL", "BOULV", "BLVD"},
new List<string> {"STREET", "ST", "STR"},
// ...
};
var allAbrLookup = new Dictionary<string, List<string>>();
foreach (List<string> list in allAbrList)
{
foreach (string abbr in list)
{
allAbrLookup.Add(abbr, list);
}
}
The last part could be converted into LINQ to have less code, but this way it is easier to understand.

If you don't create a new list for each key, then a Dictionary<string, List<string>> will be fast and reasonably memory-efficient as long as the amount of data isn't enormous. You might also be able to get a little extra benefit from reusing the strings themselves, though the optimizer might take care of that for you anyway.
var abbr = new Dictionary<string, List<string>>;
var values = new List<string> { "BOULEVARD","BOUL","BOULV", "BLVD" };
foreach(var aValue in values) abbr.add(value, values);

As Petar Minchev already said, you can split your list into an list of groups and a list of keys that points to this group. To simplify this (in usage) you can write an own implementation of IDictionary and use the Add method to build those groups. I gave it a try and it seems to work. Here are the important parts of the implementation:
public class GroupedDictionary<T> : IDictionary<T,IList<T>>
{
private Dictionary<T, int> _keys;
private Dictionary<int, IList<T>> _valueGroups;
public GroupedDictionary()
{
_keys = new Dictionary<T, int>();
_valueGroups = new Dictionary<int, IList<T>>();
}
public void Add(KeyValuePair<T, IList<T>> item)
{
Add(item.Key, item.Value);
}
public void Add(T key, IList<T> value)
{
// look if some of the values already exist
int existingGroupKey = -1;
foreach (T v in value)
{
if (_keys.Keys.Contains(v))
{
existingGroupKey = _keys[v];
break;
}
}
if (existingGroupKey == -1)
{
// new group
int newGroupKey = _valueGroups.Count;
_valueGroups.Add(newGroupKey, new List<T>(value));
_valueGroups[newGroupKey].Add(key);
foreach (T v in value)
{
_keys.Add(v, newGroupKey);
}
_keys.Add(key, newGroupKey);
}
else
{
// existing group
_valueGroups[existingGroupKey].Add(key);
// add items that are new
foreach (T v in value)
{
if(!_valueGroups[existingGroupKey].Contains(v))
{
_valueGroups[existingGroupKey].Add(v);
}
}
// add new keys
_keys.Add(key, existingGroupKey);
foreach (T v in value)
{
if (!_keys.Keys.Contains(v))
{
_keys.Add(v, existingGroupKey);
}
}
}
}
public IList<T> this[T key]
{
get { return _valueGroups[_keys[key]]; }
set { throw new NotImplementedException(); }
}
}
The usage could look like this:
var groupedDictionary = new GroupedDictionary<string>();
groupedDictionary.Add("BLVD", new List<string> {"BOUL", "BOULV"}); // after that three keys exist and one list of three items
groupedDictionary.Add("BOULEVARD", new List<string> {"BLVD"}); // now there is a fourth key and the key is added to the existing list instance
var items = groupedDictionary["BOULV"]; // will give you the list with four items
Sure it is a lot of work to implement the whole interface but it will give to an encapsulated class that you don't have to worry about, after it is finished.

I don't see a reason to define the value part of your dictionary as a List<string> object, but perhaps that is your requirement. This answer assumes that you just want to know whether the word essentially means "Boulevard".
I would pick one value as the "official" value and map all of the other values to it, like this:
var abbr = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
abbr.Add("BLVD", "BLVD"); // this line may be optional
abbr.Add("BOUL", "BLVD");
abbr.Add("BOULV", "BLVD");
abbr.Add("BOULEVARD", "BLVD");
Alternatively, you could define an enum for the value part of the dictionary, as shown below:
enum AddressLine1Suffix
{
Road,
Street,
Avenue,
Boulevard,
}
var abbr = new Dictionary<string, AddressLine1Suffix>(StringComparer.CurrentCultureIgnoreCase);
abbr.Add("BLVD", AddressLine1Suffix.Boulevard);
abbr.Add("BOUL", AddressLine1Suffix.Boulevard);
abbr.Add("BOULV", AddressLine1Suffix.Boulevard);
abbr.Add("BOULEVARD", AddressLine1Suffix.Boulevard);

Related

How to use dictionaries with lists as value c# unity

I am using a dictionary system to store my data and each thing I want to add to my dictionary will need to have 8 definitions. I want to know if anything like that is possible and if yes, I want to learn the codes for adding new items to the dictionary and read those items (if possible read the spesific things from the 8 items inside each list using index)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dictionary_matches : MonoBehaviour
{
/*public static Dictionary<string, List<String>> Matches_dic = new Dictionary<string, List<String>>();
dict.Add("key1", new List<int> { 1, 2, 3 });*/
public static Dictionary<string, string> myDict = new Dictionary<string, string>();
myDict.Add("Australia", "Canberra");
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Any help would be appreciated.
It seems, that you are looking for something like this:
public class Dictionary_matches : MonoBehaviour {
public static readonly Dictionary<string, List<string>> Matches_dic =
new Dictionary<string, List<string>>() {
{"match_1", new List<string>() {"a", "b", "c"}},
{"match_2", new List<string>() {"abracadabra"}},
};
public void Update() {
// You can use in any method of the class, e.g.
Matches_dic.Add("more_match", new List<string>() {"p", "q"});
}
// Other staff
}
here we declare the dictionary and with two entries (match_1 and match_2)
When you declare such a dictionary its keys and values are empty.
Let's say I create one such dictionary
var myDict = new Dictionary<string, List<string>>()
Now I want to add a value. But remember, my value is a List<T>.
So you need to check if your key is already present, and if it is already have an instanciated list as a value (e.g. a value != null)
if(myDict.ContainsKey("myKey") && myDict["myKey"] != null)
{
// In this case you could do
mydict["myKey"].Add("myValue");
// Because you already have a list.
// And to read the first item from the list
var firstValue = mydict["myKey"][0]; // or using Linq replace [0] by First()
}
// Now if the key does not exist you need to add the key, and ideally an instantiated list.
if(!myDict.ContainsKey["myKey"])
{
// In your case we know the expected length of the list, so let's specify it.
myDict.Add("myKey", new List<string>(8))
}
You'll notice I didn't write the case where the key would exist, but the value is null.
I'll leave to your consideration if handling that particular case is needed, and if it is, you should have all necessary information in the example above.

Finding values in a string:string dictionary where a given filter string is a subset of the key string

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?
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")));
Try it online

Find matching KVP from Dictionary<List<enum>,string> where search key is List<enum> and return reverse partial matches

I have a Dictionary where the key is a list of enum values, and the value is a simple string.
What I need to do is using another list of enum values find the match KVP.
The curveball and reason for posting here is I also need it to return KVP if the list from my test or search list contains all the items (or enum objects) in any key in the dictionary.
example excerpt of code:
public enum fruit{ apple , orange , banana , grapes };
public class MyClass
{
public Dictionary<List<fruit>, string> FruitBaskets = new Dictionary<List<fruit>, string>;
FruitBaskets.Add(new List<fruit>{apple,orange},"Basket 1");
List<fruit> SearchList = new List<fruit>{orange,apple,grapes};
}
I need to search the dictionary for SearchList and return "Basket 1".
Note that the matching may be backwards than what you would expect for such an example as I need the key to match agains the search list and not vice versa, so extra items in the search list that are not in the key are ok.
I know I could simply iterate the dict and check one by one but I also need this to be as fast as possible as it resides in a loop that is running fairly fast.
What I am currently using is;
public Dictionary<List<fruit>, string> SearchResults;
foreach (KeyValuePair<List<fruit>, string> FruitBasket in FruitBaskets)
{
if (FruitBasket.Key.Except(SearchList).Count() == 0)
SearchResults.Add(FruitBasket);
}
Wondering if there is a better/faster way.
You need to rethink about you choice of Keys in dictionary. There are some major problem with List keys, such as:
You can't use O(1) key lookup with List
Your keys aren't immutable
You can have identical lists as keys without receiving errors, for example you can have:
var a = new[] { fruit.organge }.ToList();
var b = new[] { fruit.organge }.ToList();
fruitBasket.Add(a, "1");
fruitBasket.Add(b, "2");
But is this dictionary valid? I guess not but it depends on your requirements.
You can change Dictionary keys!
For this reasons, you need to change your dictionary key type. You can use combined Enum values instead of using a List with bitwise operators. For this to work, you need to assign powers of 2 to each enum value:
[Flags]
public Enum Fruit
{
Orange = 1,
Apple = 2,
Banana = 4,
Grape = 8
}
You have to combine these enum values to get the desired multi-value enum dictionary key effect:
For [Fruit.Orange, Fruit.Apple] you use Fruit.Orange | Fruit.Apple.
Here's a sample code for combining and decomposing values:
private static fruit GetKey(IEnumerable<fruit> fruits)
{
return fruits.Aggregate((x, y) => x |= y);
}
private static IEnumerable<fruit> GetFruits(fruit combo)
{
return Enum.GetValues(typeof(fruit)).Cast<int>().Where(x => ((int)combo & x) > 0).Cast<fruit>();
}
Now you need a function to get all combinaions (power set) of the SearchList:
private static IEnumerable<fruit> GetCombinations(IEnumerable<fruit> fruits)
{
return Enumerable.Range(0, 1 << fruits.Count())
.Select(mask => fruits.Where((x, i) => (mask & (1 << i)) > 0))
.Where(x=>x.Any())
.Select(x=> GetKey(x));
}
Using these combinations, you can lookup values from dictionary using O(1) time.
var fruitBaskets = new Dictionary<fruit, string>();
fruitBaskets.Add(GetKey(new List<fruit> { fruit.apple, fruit.orange }), "Basket 1");
List<fruit> SearchList = new List<fruit> { fruit.orange, fruit.apple, fruit.grapes };
foreach (var f in GetCombinations(SearchList))
{
if (fruitBaskets.ContainsKey(f))
Console.WriteLine(fruitBaskets[f]);
}
Consider storing your data in a different way:
var FruitBaskets = Dictionary<fruit, List<string>>();
Each entry contains elements that match at least one fruit. Conversion from your structure is as follows:
foreach (var kvp in WobblesFruitBaskets)
{
foreach (var f in kvp.Key)
{
List<string> value;
if (!FruitBaskets.TryGetValue(f, out value))
{
value = new List<string>();
FruitBaskets.Add(f, value);
}
value.Add(kvp.Value);
}
}
Now, the search would look like this: For a composed key searchList you first calculate results for single keys:
var partialResults = new Dictionary<fruit, List<string>>();
foreach (var key in searchList)
{
List<string> r;
if (FruitBaskets.TryGetValue(key, out r))
{
partialResults.Add(key, r);
}
}
Now, what is left is to compose all possible search results. This is the hardest part, which I believe is inherent to your approach: for a key with n elements you have 2n - 1 possible subkeys. You can use one of subset generating approaches from answers to this question and generate your final result:
var finalResults = new Dictionary<List<fruit>, List<string>>();
foreach (var subkey in GetAllSubsetsOf(searchList))
{
if (!subkey.Any())
{
continue; //I assume you don't want results for an empty key (hence "-1" above)
}
var conjunction = new HashSet<string>(partialResults[subkey.First()]);
foreach (var e in subkey.Skip(1))
{
conjunction.IntersectWith(partialResults[e]);
}
finalResults.Add(subkey, conjunction.ToList());
}
I've changed string to List<string> in result's value part. If there is some invariant in your approach that guarantees there will be always only one result, then it should be easy to fix that.
if you create a Dictionary from a Reference Type, you stored just the Reference (Not value), then you can't use simply FruitBaskets[XXX] (except you use the same key that you create the node of dictionary), you must iterate whole of Keys in your dictionary.
I think this function is easy and good for you:
bool Contain(List<fruit> KEY)
{
foreach (var item in FruitBaskets.Keys)
{
if (Enumerable.SequenceEqual<fruit>(KEY,item))
return true;
}
return false;
}
and this,
bool B = Contain(new List<fruit> { fruit.apple, fruit.orange }); //this is True
But if you want to consider the permutation of members, you can use this function:
bool Contain(List<fruit> KEY)
{
foreach (var item in FruitBaskets.Keys)
{
HashSet<fruit> Hkey= new HashSet<fruit>(KEY);
if (Hkey.SetEquals(item))
return true;
}
return false;
}
and here's the output:
bool B1 = Contain(new List<fruit> { fruit.orange, fruit.grapes }); // = False
bool B2 = Contain(new List<fruit> { fruit.orange, fruit.apple }); // = True
bool B3 = Contain(new List<fruit> { fruit.apple, fruit.orange }); // = True

Check if an item exist in the dictionary and remove it from the dictionary in C# [duplicate]

This question already has answers here:
Remove Item in Dictionary based on Value
(6 answers)
Closed 9 years ago.
The question should be clear from the title itself. I need to check if an item exist in the dictionary and remove it from the dictionary in C#. The only catch is that i have to do this using only the value item and not the key.
The declaration is as below:
IDictionary<string, myCustomClassObject> clients = new IDictionary<string, myCustomClassObject>();
Now i fill in the dictionary by:
clients["key"] = myCustomClassObject1;
Now how can i find and remove this item myCustomClassObject1 from my Dictionary. I only want to use the value item and not the key
Is this doabale...if so please guide...
regards
Edit: Thank you all....got valuable comments...probably have some thinking to do ...thanks
It depends on how you need it to perform. If you can accept O(N) performance, you could just do something like:
foreach(var pair in clients) {
if(pair.Value == expected) {
clients.Remove(pair.Key);
break;
}
}
However, if you need faster you would need two dictionaries - one the reverse of the other (i.e. keyed by the instances). So when adding, you would do:
clientsByKey.Add(key, value);
clientsByValue.Add(value, key);
so you can do (to remove-by-value):
string key;
if(clientsByValue.TryGetValue(value, out key)) {
clientsByValue.Remove(value);
clientsByKey.Remove(key);
}
or similarly (to remove-by-key):
Foo value;
if(clientsByKey.TryGetValue(key, out value)) {
clientsByValue.Remove(value);
clientsByKey.Remove(key);
}
It's not very efficient to search a dictionary by it's values. However, you can use Linq to find all entries with a given value.
IEnumerable<KeyValuePair<string, myCustomClassObject>> pairs = clients
.Where(entry => entry.Value.Equals(myCustomClassObject1)).ToList();
foreach (KeyValuePair<string, myCustomClassObject> kv in pairs)
clients.Remove(kv.Key);
This should do it. It removes all clients having a given value.
while (clients.ContainsValue(myCustomClassObject1))
clients.Remove(clients.Where(x => x.Value == myCustomClassObject1).FirstOrDefault().Key);
Or create a new dictionary without the values you want removed
clients = clients.Where(x => x.Value != myCustomClassObject1).ToDictionary(k => k.Key, v => v.Value);
If the collection only contains one item with the value to be removed then you can use one of the other answers here, which will work just fine.
However, if your collection can have multiple items with the same value then you need to be careful.
You cannot modify a collection while iterating over it, so you will need to find the keys of all the items that you want to remove in one loop and put them in a list, and then iterate over that list in a separate loop to delete the items.
For example:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
void run()
{
var dict = new Dictionary<string, int>
{
{"Key1", 1},
{"Key2", 2},
{"Key3", 3},
{"Key4", 2},
{"Key5", 4}
};
int valueToRemove = 2;
var keysToRemove = (from element in dict
where element.Value == valueToRemove
select element.Key).ToList();
foreach (var key in keysToRemove)
dict.Remove(key);
foreach (var element in dict)
Console.WriteLine("Key = {0}, Value = {1}", element.Key, element.Value);
}
static void Main(string[] args)
{
new Program().run();
}
}
}
Use,
Following will remove only first matching value
client newClient = new client();
foreach(KeyValuePair<string, client> client in clients) {
if(client.value.equals(newClient)) {
clients.remove(client.key);
break;
}
}
Or if you want to remove all matching values,
foreach(var client in clients.Where(kvp => kvp.Value == newClient).ToList()) {
clients.Remove(client.Key);
}

c# How to sort a sorted list by its value column

i have a generic sorted list "results" with key = some filename and value = boolean.
I would like to sort the list by the boolean entry or value column. does anyone know how i can do this?
Thanks!
SortedList is optimized so that inertions occur in an ordered fashion, such that enumeration occurs in a sorted order at minimal cost. Anything else requires a re-sort. Thus:
SortedList<string,bool> l = new SortedList<string, bool>();
l.Add("a", true);
l.Add("b", false);
l.Add("c", true);
l.Add("d", false);
var orderByVal = l.OrderBy(kvp => kvp.Value);
but this enumeration will be significantly slower to calculate, and be performed up-front, requiring extra storage to do so.
Depending on your situation it might be cheaper to maintain 2 SortedList instances with the key/value reversed.
In .NET 2.0, you could add your items to a SortedList:
public static List<MyObject> SortedObjects(IEnumerable<MyObject> myList) {
SortedList<string, MyObject> sortedList = new SortedList<string, MyObject>();
foreach (MyObject object in myList) {
sortedList.Add(object.ValueIWantToSort, object);
}
return new List<MyObject>(sortedList.Values);
}
For descending all list items
list.OrderByDescending();
or
var list = list.OrderByDescending(x => x.Product.Name)
.ThenBy(x => x.Product.Price).ToList();
Normally that sorted by the first key on the list so if you swap the key and value on the add, then match that on the binding
that sample example i use and work fine
public static SortedList<string, string> GetCountries(string conn)
{
var dict = new SortedList<string, string>();
dict.Add("","Select One");
var sql = "SELECT [CountryID] ,[Descr] FROM [dbo].[Countries] Order By CountryID ";
using (var rd = GetDataReader(conn, sql))
{
while (rd.Read())
{
dict.Add(rd["Descr"].ToString(), rd["CountryID"].ToString());
}
}
return dict;
}
Dim List As SortedList(Of String, String) = VDB.CoreLib.DbUtils.GetCountries(connDB)
ddlBankCountry.DataSource = List
ddlBankCountry.DataTextField = "Key"
ddlBankCountry.DataValueField = "Value"
ddlBankCountry.DataBind()

Categories