Query List Dictionary - c#

I have a list of dictionaries
var ProductItemsDictionary = new List<Dictionary<string, string>>();
Is it possible to use linq to search the list and find a dictionary based on a key inside that dictionary and retrieve the value?

Sure it is, but is it worth? See for instance the dotctor's answer. It will do the job, but is inefficient - 2 key lookups (one for checking and one for retrieving the value), compiler generated class and heap allocation (because of the specificKey variable capture), etc. Less code? More readable? What about the non linq equivalent:
static void Foo(List<Dictionary<string, string>> ProductItemsDictionary, string key)
{
string value;
foreach (var dictionary in ProductItemsDictionary)
if (dictionary.TryGetValue(key, out value)) { /* Use value */ }
// Not found
}
Zero allocations, minimum key lookups, good readability (IMO) - what else do we need? :-)

Related

Remove a single value from a NameValueCollection

My data source could have duplicate keys with values.
typeA : 1
typeB : 2
typeA : 11
I chose to use NameValueCollection as it enables entering duplicate keys.
I want to remove specific key\value pair from the collection, but NameValueCollection.Remove(key) removes all values associated with the specified key.
Is there a way to remove single key\value pair from a NameValueCollection,
OR
Is there a better collection in C# that fits my data
[EDIT 1]
First, thanks for all the answers :)
I think I should have mentioned that my data source is XML.
I used System.Xml.Linq.XDocument to query for type and also it was handy to remove a particular value.
Now, my question is, for large size data, is using XDocument a good choice considering the performance?
If not what are other alternatives (maybe back to NameValueCollection and using one of the techniques mentioned to remove data)
The idea of storing multiple values with the same key is somehow strange. But I think you can retrieve all values using GetValues then remove the one you don't need and put them back using Set and then subsequent Add methods. You can make a separate extension method method for this.
NameValueCollection doesn't really allow to have multiple entries with the same key. It merely concatenates the new values of existing keys into a comma separated list of values (see NameValueCollection.Add.
So there really is just a single value per key. You could conceivably get the value split them on ',' and remove the offending value.
Edit: #ElDog is correct, there is a GetValues method which does this for you so no need to split.
A better option I think would be to use Dictionary<string, IList<int>> or Dictionary<string, ISet<int>> to store the values as discrete erm, values
You may convert it to Hashtable
var x = new NameValueCollection();
x.Add("a", "1");
x.Add("b", "2");
x.Add("a", "1");
var y = x.AllKeys.ToDictionary(k => k, k=>x[k]);
make your own method, it works for me --
public static void Remove<TKey,TValue>(
this List<KeyValuePair<TKey,TValue>> list,
TKey key,
TValue value) {
return list.Remove(new KeyValuePair<TKey,TValue>(key,value));
}
then call it on list as --
list.Remove(key,value); //Pass the key value...
Perhaps not the best way, but....
public class SingleType
{
public string Name;
public int Value;
}
List<SingleType> typeList = new List<SingleType>();
typeList.Add (new SingleType { Name = "TypeA", Value = 1 });
typeList.Add (new SingleType { Name = "TypeA", Value = 3 });
typeList.Remove (typeList.Where (t => t.Name == "TypeA" && t.Value == 1).Single());
You can use the Dictionary collection instead:
Dictionary<string, int> dictionary = new Dictionary<string, int>();
dictionary.Add("typeA", 1);
dictionary.Add("typeB", 1);
When you try to insert type: 11 it will throw exception as Key already exists. So you can enter a new key to insert this data.
Refer this Tutorial for further help.

Fast multi-key lookup without Dictionary?

I have an Id mapping cache that's taking up a bit too much memory. It's used to house a combination of 3 different types of Id's for an object and the mappings for them are read in from a table, and cached in 6 different dictionaries for quick look-up/translation from any 1 Id type to another (performance is important for my application).
I wanted to rewrite it to something that has a smaller memory footprint so I did implement a consolidated list of the Id's and used a linq/lambda expression to pull out the values I wanted. It looks like this for now.
public struct IdMappings
{
public int Id1;
public int Id2;
public int Id3;
}
//new cache
private static List<IdMappings> AllIdMappings = null;
//current cache implementation
private static Dictionary<int, int> Id1ToId2 = null;
private static Dictionary<int, int> Id1ToId3 = null;
//etc.
public static void FillCache(DataSet data)
{
foreach (DataRow r in data.Tables[0].Rows)
{
//fill list and/or dictionaries with id's
}
}
Example lookup would then be:
public static int GetId2FromId1(int id1)
{
return AllIdMappings.FirstOrDefault(m => m.Id1 == id1).Id2;
//or
return Id1ToId2[id1];
}
This does what I need in terms of reducing memory usage, but performance for lookups has suffered as a result so I'm seeing how to implement something different. Is there a way to do multi-indexing keys, or multi-key lookup that's relatively faster than iterating through a list?
If you add these three dictionaries:
private static Dictionary<int, IdMappings> Id1Lookup = null;
private static Dictionary<int, IdMappings> Id2Lookup = null;
private static Dictionary<int, IdMappings> Id3Lookup = null;
And have the dictionary values be the same references, it should use minimally more memory but retain the same lookup speed as your original implementation.
If I'm thinking about this right, this should use half the memory of your 6 dictionary solution, but twice a List<IdMappings> type solution.
As #SWeko points out, IdMappings needs to be a class not a struct to ensure the reference pointer is used rather than copies of it.
Yes, sort the list and use binary search (List<> already implements this for you in the method Find)
Maintaining a sorted list and lookups is then done in O(logn).
One potential performance improvement could be to use a Hashset<IdMappings> instead of a List<IdMappings>, but that would mostly help for direct look-up, and not for FirstOrDefault which, more or less, iterates the list sequentially.
If your lookups are all from the ID1 -> ID2 and ID3 direction, you could use a Dictionary<int, Tuple<int, int>> for the keys, and that would eliminate a extra value of ID1 from the current dictionaries.
Anyway, cache is by definition trade of memory for lookup speed, so I don't think you can improve the memory consumption by much.
Probably your best bet is to create a mapping structure:
struct Mapping: IComparable<Mapping>
{
private readonly int FromId;
private readonly int ToId;
public Mapping(int fid, int tid);
// implement the IComparable.Compare method to compare FromId
}
Then, create a List<Mapping> for each index, and sort the list. You can then use List.Find to find the item you want.

SortedSet and SortedList fails with different enums

The whole story; I have some KeyValuePairs that I need to store in a session and my primary goal is to keep it small. Therefore I don't have the option of using many different collection. While the key is a different enum value of of a different enum type the value is always just a enum value of the same enum type. I have chosen a HashTable for this approach which content look like this (just many more):
// The Key-Value-Pairs
{ EnumTypA.ValueA1, MyEnum.ValueA },
{ EnumTypB.ValueB1, MyEnum.ValueB },
{ EnumTypC.ValueC1, MyEnum.ValueA },
{ EnumTypA.ValueA2, MyEnum.ValueC },
{ EnumTypB.ValueB1, MyEnum.ValueC }
At most I am running contains on that HashTable but for sure I also need to fetch the value at some point and I need to loop through all elements. That all works fine but now I have a new requirement to keep the order I have added them to the HashTable -> BANG
A HashTable is a map and that is not possible!
Now I thought about using a SortedList<object, MyEnum> or to go with more Data but slightly faster lookups and use a SortedSet<object> in addition to the HashTable.
Content below has been edited
The SortedList is implemented as
SortedList<Enum, MyEnum> mySortedList = new SortedList<Enum, MyEnum>();
the SortedSet is implemented as
SortedSet<Enum> mySortedSet = new SortedSet<Enum>();
The described Key - Value - Pairs are added to the sorted list with
void AddPair(Enum key, MyEnum value)
{
mySortedList.Add(key, value);
}
And for the SortedSett like this
void AddPair(Enum key)
{
mySortedSet.Add(key);
}
Both are failing with the exception:
Object must be the same type as the
enum
My question is: What goes wrong and how can I archive my goal?
Used Solution
I've decided to life with the downside
of redundant data against slower
lookups and decided to implement a
List<Enum> which will retain the
insert order parallel to my already
existing HashTable.
In my case I just have about 50-150
Elements so I decided to benchmark the
Hashtable against the
List<KeyValuePair<object,object>>
Therefore I have create me the
following helper to implement
ContainsKey() to the
List<KeyValuePair<object,object>>
static bool ContainsKey(this List<KeyValuePair<object, object>> list, object key)
{
foreach (KeyValuePair<object, object> p in list)
{
if (p.Key.Equals(key))
return true;
}
return false;
}
I inserted the same 100 Entries and
checked randomly for one of ten
different entries in a 300000 loop.
And... the difference was tiny so I
decided to go with the
List<KeyValuePair<object,object>>
I think you should store your data in an instance of List<KeyValuePair<Enum, MyEnum>> or Dictionary<Enum, MyEnum>.
SortedSet and SortedList are generic, but your keys are EnumTypeA/EnumTypeB, you need to specify the generic T with their base class(System.Enum) like:
SortedList<Enum, MyEnum> sorted = new SortedList<Enum, MyEnum>();
EDIT
Why you got this exception
SortedList and SortedSet use a comparer inside to check if two keys are equal. Comparer<Enum>.Default will be used as the comparer if you didn't specify the comparer in the constructor. Unfortunately Comparer<Enum>.Default isn't implemented as you expected. It throws the exception if the two enums are not the same type.
How to resolve the problem
If you don't want to use a List<KeyValuePair<Enum, MyEnum>> and insist using SortedLIst, you need to specify a comparer to the constructor like this:
class EnumComparer : IComparer<Enum>
{
public int Compare(Enum x, Enum y)
{
return x.GetHashCode() - y.GetHashCode();
}
}
var sorted = new SortedList<Enum, MyEnum>(new EnumComparer());
Btw, I think you need to obtain the "inserting order"? If so, List<KeyValuePair<K,V>> is a better choice, because SortedSet will prevent duplicated items.

Quick mass-updating a Dictionary

I have a Dictionary<int, int> and would like to update certain elements all at once based on their current values, e.g. changing all elements with value 10 to having value 14 or something.
I imagined this would be easy with some LINQ/lambda stuff but it doesn't appear to be as simple as I thought. My current approach is this:
List<KeyValuePair<int, int>> kvps = dictionary.Where(d => d.Value == oldValue).ToList();
foreach (KeyValuePair<int, int> kvp in kvps)
{
dictionary[KeyValuePair.Key] = newValue;
}
The problem is that dictionary is pretty big (hundreds of thousands of elements) and I'm running this code in a loop thousands of times, so it's incredibly slow. There must be a better way...
This might be the wrong data structure. You are attempting to look up dictionary entries based on their values which is the reverse of the usual pattern. Maybe you could store Sets of keys that currently map to certain values. Then you could quickly move these sets around instead of updating each entry separately.
I would consider writing your own collection type to achieve this whereby keys with the same value actually share the same value instance such that changing it in one place changes it for all keys.
Something like the following (obviously, lots of code omitted here - just for illustrative purposes):
public class SharedValueDictionary : IDictionary<int, int>
{
private List<MyValueObject> values;
private Dictionary<int, MyValueObject> keys;
// Now, when you add a new key/value pair, you actually
// look in the values collection to see if that value already
// exists. If it does, you add an entry to keys that points to that existing object
// otherwise you create a new MyValueObject to wrap the value and add entries to
// both collections.
}
This scenario would require multiple versions of Add and Remove to allow for changing all keys with the same value, changing only one key of a set to be a new value, removing all keys with the same value and removing just one key from a value set. It shouldn't be difficult to code for these scenarios as and when needed.
You need to generate a new dictionary:
d = d.ToDictionary(w => w.Key, w => w.Value == 10 ? 14 : w.Value)
I think the thing that everybody must be missing is that it is exceeeeedingly trivial:
List<int> keys = dictionary.Keys.Where(d => d == oldValue);
You are NOT looking up keys by value (as has been offered by others).
Instead, keys.SingleOrDefault() will now by definition return the single key that equals oldValue if it exists in the dictionary. So the whole code should simplify to
if (dictionary.ContainsKey(oldValue))
dictionary[key] = newValue;
That is quick. Now I'm a little concerned that this might indeed not be what the OP intended, but it is what he had written. So if the existing code does what he needs, he will now have a highly performant version of the same :)
After the edit, this seems an immediate improvement:
foreach (var kvp in dictionary.Where(d => d.Value == oldValue))
{
kvp.Value = newValue;
}
I'm pretty sure you can update the kvp directly, as long as the key isn't changed

How to Find Item in Dictionary Collection?

I have declared and populated the following collection.
protected static Dictionary<string, string> _tags;
Now I want to look locate a particular entry in the collection. I tried the following.
thisTag = _tags.FirstOrDefault(t => t.Key == tag);
if (thisTag != default(KeyValuePair<string, string>))
...
And I get the error:
Operator '!=' cannot be applied to operands of type 'System.Collections.Generic.KeyValuePair' and ''
Initially I attempted to compare the result to null, and I guess that's not supported with structs.
I would've thought that finding an item within a collection is a very trivial task. So how the heck to I determine if the item I'm looking for was found?
(Note: I'm using Dictionary because I want fast lookups. I know I can use Contains() to determine if the item is there. But that means a total of two lookups, which sort of defeats the purpose of having a fast lookup. I'll happily using a different collection if it can quickly lookup an item and I have a way to determine if it was successful.)
thisTag = _tags.FirstOrDefault(t => t.Key == tag);
is an inefficient and a little bit strange way to find something by key in a dictionary. Looking things up for a Key is the basic function of a Dictionary.
The basic solution would be:
if (_tags.Containskey(tag)) { string myValue = _tags[tag]; ... }
But that requires 2 lookups.
TryGetValue(key, out value) is more concise and efficient, it only does 1 lookup. And that answers the last part of your question, the best way to do a lookup is:
string myValue;
if (_tags.TryGetValue(tag, out myValue)) { /* use myValue */ }
VS 2017 update, for C# 7 and beyond we can declare the result variable inline:
if (_tags.TryGetValue(tag, out string myValue))
{
// use myValue;
}
// use myValue, still in scope, null if not found
Sometimes you still need to use FirstOrDefault if you have to do different tests.
If the Key component of your dictionnary is nullable, you can do this:
thisTag = _tags.FirstOrDefault(t => t.Key.SubString(1,1) == 'a');
if(thisTag.Key != null) { ... }
Using FirstOrDefault, the returned KeyValuePair's key and value will both be null if no match is found.
It's possible to find the element in Dictionary collection by using ContainsKey or TryGetValue as follows:
class Program
{
protected static Dictionary<string, string> _tags = new Dictionary<string,string>();
static void Main(string[] args)
{
string strValue;
_tags.Add("101", "C#");
_tags.Add("102", "ASP.NET");
if (_tags.ContainsKey("101"))
{
strValue = _tags["101"];
Console.WriteLine(strValue);
}
if (_tags.TryGetValue("101", out strValue))
{
Console.WriteLine(strValue);
}
}
}
Of course, if you want to make sure it's in there otherwise fail then this works:
thisTag = _tags[key];
NOTE: This will fail if the key,value pair does not exists but sometimes that is exactly what you want.
This way you can catch it and do something about the error.
I would only do this if I am certain that the key,value pair is or should be in the dictionary and if not I want it to know about it via the throw.

Categories

Resources