C# Dictionary: Multiple KEYS per Value - c#

I'm looking for a way to get multiple keys with a single value. Yes, I've already used the search function, but most answers are for the opposite direction (multiple values per key), but I want the opposite.
The reasoning behind this is that I want keep multiple Item-IDs (it's for a Bot) per "main" ID, and throwing those multiple IDs into a value of the one is too slow to modify (looking for one value => looping trough all main IDs & getting each value, then checking if that ID exists).
Example
Key 1 => Value
Key 2 => Value
Key 3 => Value
Key 4 => Value
Key 5 => Value 2
Looking for Value should return: Key 1-4, not 5
So I'm looking for a way to do that easier - like I said above.
Anyone knows if that's possible and how to do it?
Thanks in advance.

Edit:
Looking at your edit, it really looks like you have designed this Dictionary backwards... your keys should be for matching values, not your values for matching keys.
You could do something like create a dictionary that maps outer-keys to inner-keys, then use the inner-key to index a second dictionary.
Example:
var outer = new Dictionary<int, Guid> {
{ 1, GuidA },
{ 2, GuidA },
{ 3, GuidA },
{ 4, GuidA },
{ 5, GuidB }
};
var inner = new Dictionary<Guid, Value> {
{ GuidA, Value1 },
{ GuidB, Value2 }
};
You would access it as: value = outer[inner[key]].

You may be overthinking your problem. Keys need to be unique in order to be useful for lookup operations. Values do not need to be unique. Multiple keys can point to the same value without causing problems.

Do the dictionary the other way around and make the value a list of items.
if for example Value is a string and Key 1-4 are ints your dictionary could look something like:
var theDictionary = new Dictionary<string, List<int>>();
retrieving Value by theDictionary["Value"] would then return a list of ints containing 1, 2, 3 and 4.
Edit - Added example:
var theDictionary = new Dictionary<string, List<string>>
{
{"Value", new List<string> {"Key 1", "Key 2", "Key 3", "Key 4", "Key 5",}},
{"Value2", new List<string> {"Key 5", "Key 2"}}
};
var oneToFour = theDictionary["Value"];

1) Servy is absolutely correct. If you're doing a search on anything but a key ... and if you're trying to retrieve anything but the corresponding value ... then something is definitely wrong. All things being equal, you probably DON'T want a dictionary.
2) Based on what you're saying, perhaps a better collection type might be a List. Specifically, a list of name/value pairs.
EXAMPLE:
List<string> NVList = new List<string>();
NVList.Add("color=blue");
...
3) Note that .Net has a specialized "NameValueCollection" class that might be IDEAL for you:
http://msdn.microsoft.com/en-us/library/system.collections.specialized.namevaluecollection.aspx

Assuming you have your initial dictionary (mapping your keys to values) already you can use some Linq to convert it into a reverse dictionary without having to create that reverse dictionary by hand.
var newDict = initialDict.Select(x=>x.Value).Distinct().ToDictionary(x=>x, x=> initialDict.Where(kvp=>kvp.Value == x).Select(kvp=>kvp.Key));
Select the distinct originalValues from your original dictionary and use those as your newKeys. Your newValues are the set of your originalKeys that mapped to each originalValue/newKey.
Example: https://dotnetfiddle.net/dhwUSC
Given an initial dictionary of
var initialDict = new Dictionary<int, string>{
{1, "Value"},
{2, "Value"},
{3, "Value"},
{4, "Value"},
{5, "Value2"}
};
the above function returns
Value: {1, 2, 3, 4}
Value2: {5}

Related

c# how to best put array or list into class that also has name?

Instead of doing my old non OOP way I am trying to do what people claim is best. I need to store about 9 different Int arrays of differing lengths. I also need to associate them with a String Name "this is called etc.." I was thinking it would make sense to store that all into a class object so I can cleanly iterate through them later on without looking to two different places using the same for loop iterator.
Example:
public class Thing
{
public List<int> SDNA {get; set;}
public string Name {get; set;}
}
List<Thing> things = new List<Thing>
{
new Thing { SDNA = {2,4,5,7,9,11},Name = "First Thing"}
}
I get a null ref exception (I am assuming its cause of the list within a class somehow) I tried creating a list this way to clear the null ref but it had some other errors.
List<Thing> things = new List<Thing>();
things.Add(new Thing() {SDNA = {2,4,5,7,9,11},Name = "The first things name"});
Errors of invalid token etc. Should I just do it with two different stored arrays, one for names and a jagged array for the Ints and then reference them each? That feels ugly to me. Why can't I store them all into one thing?
Thanks!
In the simplest case if you want just to have name to value (array) association, you can try using a simple Dictionary, e.g.
private Dictionary<string, List<int>> things = new Dictionary<string, List<int>>() {
{"First thing", new List<int>() {2, 4, 5, 7, 9, 11}},
};
then you can use it
// Add new thing
things.Add("Some other thing", new List<int>() {1, 2, 3, 4, 5});
// Try get thing
if (things.TryGetValue("First thing", out var list)) {
// "First thing" exists, list is corresponding value
}
else {
// "First thing" doesn't found
}
// Remove "Some other thing"
things.Remove("Some other thing");
// Iterate over all {Key, Value} pairs (let's print them):
foreach (var pair in things)
Console.WriteLine($"{pair.Key} :: [{string.Join(", ", pair.Value)}]");
However, if Thing is not just SDNA + Name combination (more properties, methods are expected) I suggest
private Dictionary<string, Thing> things
declaration

How to merge 2 Dictionaries into a third that consists of {Key=Value(Dict1), Value=Value(Dict2)}

I am trying to merge 2 Dictionaries.
The first one consists of an ID and a Title, so for instance: {2, Error}.
The second one consists of an ID and the frequency in which the ID occurs: {2, 3}.
I want to create a dictionary that looks like this: {Error, 3}.
Is this possible?
My code thus far works only if there are no duplicates ... I want to just add the frequency number of the duplicate ID to the already saved frequency of the previous occurrence of the same ID. So e.g. if in dict2 for some reason there exists {2, 3} and {2, 5} the result in dict3 should be {Error, 8}.
foreach (var item in dict2)
{
foreach(var entry in dict1)
{
if (item.Key.Equals(entry.Key))
{
dict3.Add(entry.Value.ToString(), (int)item.Value);
}
}
}
You can use LINQ:
var combinedDictQry =
from entry1 in dictionary1
from entry2 in dictionary2
where entry1.Key == entry2.Key
select new { Key = entry1.Value, Value = entry2.Value };
var combinedDict = combinedDictQry.ToDictonary(entry => entry.Key, entry => entry.Value);
This will serve you well for smaller dictionaries (ie. for most practical purposes). If you need to process larger amounts of data, you might get performance issues and you will have to access the values of the second dictionary with TryGetValue to make use of the dictionary's better access performance.
If you expect duplicate values in dictionary1, .ToDictionary will not work, because duplicate keys are not allowed in a dictionary. You can however create a lookup. Duplicate keys will be merged and the values can be enumerated:
var combinedLookup = combinedDictQry.ToLookup(entry => entry.Key, entry => entry.Value);

Select item by a maximum value and duplicates of this value present in List<Tuple>

Print top value and its duplicate values present in List
Suppose List is ordered list.
List<Tuple<int, string, string>> result = new List<Tuple<int, string, string>> {
{4, "am", "I am the One"}
{4, "am" , "I am human"}
{2, "one", "zero plus one is one"}
{1, "is", "this is list"}
};
Selected output should Top int value and its duplicate if present.
Top according to int values is : 4. Duplicates are 1st and 2nd entries of list.
Output:
4, am, I am the One
4, am , I am human
Can anyone code to get max value and its duplicate?
I think this will do it:
List<Tuple<int, string, string>> maxTuples =
result.Where(r => r.Item1 == (result.Max(m => m.Item1))).ToList();
You are getting the maximum value of Item1 (the int) and then finding all records that have the same value.
HTH, like mentioned in the comments previously good to post what you have tried in future.

Good way to extract a subset of key-value pairs from a dictionary, based on a value in a key value pair

I have a table represented in a Dictionary, where each key corresponds to the column name, and the (value) List holds the values for that column's rows. So the index of the list items is my row index.
Trying to figure out a linq C# implementation that would give me the indices of a value in a column, and thereby derive its adjacent column values for the other keys. For example, if my table representation looks like so:
Key1 AA BB JX TR FT
Key2 AX BC SF XO ST
Key3 22 22 22 21 21
Key4 AW BM ND NC PO
Key5 AZ BN SD DS ZX
I'd like to find the value with most occurrences in Key3, say 22, and extract a subset of key-value pairs for just adjacent values for Key1 and Key2. I've seen examples of how to get indices of duplicates, and from that I could derive the subset dictionary by iterating, but ideally I'd like it to be one statement that does it.
Currently using from an example from this forum for the criteria: number
var result = Enumerable.Range(0, table["Key3"].Count)
.Where(i => table["Key3"][i] == number)
.ToList();
Hoping someone can suggest a way to add the extraction of the subset dictionary to this statement.
First of all, i'm not sure i understand well what you want to achieve...
Second of all, as Marc Gravell mentioned in the comment to the question, Dictionary object has no defined ordering and indexes, unless it is OrderedDictionary.
Third of all, the best option in your case is to use for loop, because this is always(*) faster then any Linq solution.
*-usually ;)
My best guess is:
Dictionary<string, List<string>> oDict = new Dictionary<string, List<string>>();
oDict.Add("Key1", new List<string>{"AA", "BB", "JX", "TR", "FT"});
oDict.Add("Key2", new List<string>{"AX", "BC", "SF", "XO", "ST"});
oDict.Add("Key3", new List<string>{"22", "22", "22", "21", "21"});
oDict.Add("Key4", new List<string>{"AW", "BM", "ND", "NC", "PO"});
oDict.Add("Key5", new List<string>{"AZ", "BN", "SD", "DS", "ZX"});
//key to find
string searchedKey = "Key3";
//value to find
string searchedValue = "22";
//get index of Key
var keyIndex = oDict.Keys.ToList().IndexOf(searchedKey); //if not found, returns -1
//returns 2
//get the list of indexes where the value has been found
var valIndexes = Enumerable.Range(0, oDict[searchedKey].Count)
.Where(i => oDict[searchedKey][i] == searchedValue)
.ToList();
//returns: {0, 1, 2}
//get distination resultset
var resultSubset = oDict
.Take(keyIndex+1) //take the no. of Keys
.ToDictionary(a=>a.Key,
a=>a.Value.Where((x,z) => valIndexes
.Any(i=> i==z))
.Select(x=>x).ToList());
Above code returns: Dictionary<<string>, List<string>>():
Key1, {AA, BB, JX}
Key2, {AX, BC, SF}
Key3, {22, 22, 22}

Extracting unique keys from key/value pairs, and grouping the values in an array

Let's say I have a List<NameValuePair>, where NameValuePair is a simple object that has a Name property and a Value property, both strings.
The list is populated with values like this:
name = "name1", value = "value1"
name = "name1", value = "value2"
name = "name2", value = "value3"
name = "name3", value = "value4"
Note that there are two instances of the "name1" key. There can be any number of keys (since this is a List).
I want to turn this List into a new list, which has just unique keys, and groups any values with the same key name as an array/list of that key.
So the above should become:
name = "name1", value = "value1", "value2" // value is a string array or list
name = "name2", value = "value3"
name = "name3", value = "value4"
What is the easiest way to accomplish this?
The easiest way is with a ILookup, which is essentially like a dictionary but can have more than one value for each key.
You can do something like this to create your lookup:
var lookup = list.ToLookup(pair => pair.name,
pair => pair.value);
Then you could print the name/value pairs like this:
foreach (var nameGroup in lookup)
{
var name = nameGroup.Key;
foreach (string value in nameGroup)
Console.WriteLine("Name: {0}, Value: {1}", name, value);
}
Maybe with a Dictionary<string,List<string>> you could do something like
for (var kv in mylistofnamed) {
if (!dict.ContainsKey(kv.Key))
dict[kv.Key] = new List<string>();
dict[kv.Key].Add(kv.Value);
}
?
If you only need a read-only collection then Lookup will do the trick, as in Meta-Knight's answer.
If you need to modify the collection after its initial creation/population, then you probably need something like Dictionary<string, List<string>>. You can create and populate such a dictionary from your original list using LINQ:
var dict = list
.GroupBy(x => x.Name)
.ToDictionary(x => x.Key, y => y.Select(z => z.Value).ToList());
One way is with a dictionary:
http://arcanecode.com/2007/03/05/dictionaries-in-c-the-hashtable/
A HashTable can do what you need with unique keys. You will have to supply a List as the value however, since you will be storing multiple values per key.
Here's another easy example:
http://dotnetperls.com/hashtable-keys
You will need to iterate over each KeyValuePair in your list to populate the HashTable by storing the name as the key and the values as the value. Because you may have a name that points to multiple values, you will need your values in the HashTable to be Lists.
Check for the existence of the name in the HashTable, if it's not there, create a new list for that name and add the value to the list. If the key already exists, access that element in the HashTable and add the new value to the list that maps to the key.
All classes that implement the IDictionary interface or the Generic IDictionary Interface enforce uniqueness checks for the Keys. You could use any of the classes, though I confess my preference for the Generic Dictionary<TKey, TValue> class.
When adding values, you can just check if the Dictionary object already contains the supplied Key. If not, then you can add the item into the Dictionary.

Categories