Can I initialize Dictionary Values automatically? - c#

Let's say I want to store my data as a bunch of named lists.
I really like how with Dictionary, you can do:
myDictionary["SomeKey"] = new List<string>{ "SomeString" };
But unfortunately, I cannot safely do:
myDictionary["SomeKey"].Add("SomeString");
Because if the List at SomeKey has not yet been initialized, I'll get a null reference exception.
So everytime I alter a value in the dictionary, I have to do:
if (!myDictionary.ContainsKey("SomeKey")) myDictionary["SomeKey"] = new List<string>();
Before I can safely add to each list. It looks ugly and distracts from non-boilerplate code. I also duplicate code in several places (accessing SomeKey several times when all I want to say is "add this element to the list at SomeKey, regardless of whether it is empty or not").
There is basically no reason why I would want uninitialized values in my Dictionary. If it was an array of List, I would have looped through every element beforehand and initialized each member; I can't do this with Dictionary because I don't know what the keys will be in advance.
Is there a more concise syntax for saying, "access the value at this key, and if it is null, then initialize it"?

I don't think there is, but you could do it as an extension method if you wanted. Something like
public static class DictionaryExtensions
{
public static SafeAdd(this Dictionary<string,List<string>> obj, string key, string val)
{
// your example code
}
}
then you could do myDictionary.SafeAdd("somekey","somestring");

Related

What is the optimal data structure for storing objects with a string key and a bool auxiliary value?

I need a data structure like below, but I need to be able to change the bool value. Other two stay the as they were when they were initialized. What would you use for best performance?
Dictionary<string, (object, bool)> dic = new Dictionary<string, (object, bool)>();
I was thinking of hashtable. But hashtable is like a dictionary with key/value. The object and bool in my example are in concept not like a key/value, because other values of the external dictionary can have the same object (or better yet ... object type). I don't want to make someone looking at my code later on thinking that the object and bool are more related they really are.
EDIT: object in this example is just a place holder. In reality it's a complex object with other objects in it and so on. Procedure before this one makes a bunch of this objects and some of them are deepcopy of the others. They are passed to this procedure. All of the object are here named by some rules and stored in the dictionary. Names are obviously unique. Procedure that comes after will take this dictionary and set the bool value on and off based on the values in the objects themselves and on the values of other bools. Procedure will be recursive until some state is reached.
Number of objects (or dic. entries) is arbitrary but expected to be >100 && <500. Time complexity is O(n).
I am targeting .NET7 (standard).
but I need to be able to change the bool value.
You can just reassign value for the key:
var tuples = new Dictionary<string, (object Obj, bool Bool)>
{
{ "1", (new object(), true) }
};
tuples["1"] = (tuples["1"].Obj, false); // or tuples["1"] = (tuples["1"].Item1, false);
Or
if (tuples.TryGetValue("1", out var c))
{
tuples["1"] = (c.Obj, false);
}
Personally I would leave it at that, but for really high perf scenarios you can look into CollectionMarshall instead of second snippet:
ref var v = ref CollectionsMarshal.GetValueRefOrNullRef(tuples, "1");
if (!Unsafe.IsNullRef(ref v))
{
v.Bool = false;
}
A bit more info - here.
For the 'performance' aspect:
The .NET Dictionary uses hashes to look up the item you need, which is very fast (comparable to a HashTable). I don't expect much performance issues related to this, or at least nothing that can be improved on with other data structures.
Also, you shouldn't worry about performance unless you are doing things a million times in a row + it turns out (in practice) that something is taking a measurable amount of time.
For the 'changing a bool' aspect:
... that is quite a long story.
There are 2 tuple variants in .NET:
The value tuple, created by doing var x = (myObj, myBool), like you are doing.
The x is a struct, and therefore a Value Type. You can actually change x.Item1 or x.Item2 to a new value just fine.
However... if you put x into a Dictionary then you actually put a copy of x (with a copy of its values) into the dictionary, because that is the nature of value types.
When you retrieve it again from the Dictionary, yet another copy is made - which makes modifying the actual tuple inside the Dictionary impossible; any attempt to do so would only modify the last copy you got.
Side story: The .NET Compiler knows this, which is why its refuses to compile code like dic[yourKey].Item2 = newBool; because such code wouldn't do what you might hope it would do. You're basically telling the compiler to create a copy, modify the copy, and then... discard the copy. The compiler requries a variable to store the copy before the rest can even start, but we provided no variable.
The Tuple generic class, or rather a range of generic classes, an instance of which can be created using calls like var x = Tuple.Create(myObj, myBool). These classes however forbid that you change any of their properties, they are always readonly. Tuple class instances can be put in a Dictionary, but they will still be readonly.
So what options are there really to 'modify a value in a tuple' a Dictionary?
Keep using a value tuple, but accept that in order to "change" the tuple inside the Dictionary you'll have to make a new instance (either a copy, or from scratch), set it to the properties that you want, and put that instance (or actualy a copy...) into the dictionary:
// initialize it
var dict = new Dictionary<string, (object, bool)>();
var obj = new object();
dict["abc"] = (obj, true);
// change it
var tmpTuple = dict["abc"]; // get copy
tmpTuple.Item2 = false; // alter copy
dict["abc"] = tmpTuple; // store another copy
// or if you want to avoid the tmp variable
dict["abc"] = (dict["abc"].Item1, false)
Use a custom class instead of the value tuple or a Tuple class, and then put that into the Dictionary:
public class MyPair
{
public object O { get; set; }
public bool B { get; set; }
}
// initialize it
var dict = new Dictionary<string, MyPair>();
var obj = new object();
dict["abc"] = new MyPair { O = obj, B = true };
// change it
dict["abc"].B = false;
So both types of Tuples are OK for objects that you don't want to do a lot with. But both have certain limits in their usage, and sooner or later you may need to start using classes.

C# Iterating over a dictionary passed as a generic

I am having trouble trying to iterate over a dictionary passed to a function as a generic. For example, I have a function that loads data from a DB.
public T Load<T>(...)
This function can be called like so, with which I have no problems;
someclasstype data = Load<someclasstype>(...);
List<someclasstype> data = Load<List<someclasstype>>(...);
I've recently tried to extend this to be able to deal with dictionaries as well, and calling it like so:
Dictionary<long, someclasstype> data = Load<Dictionary<long, someclasstype>>(...)
I can load the data without a problem and store it in the dictionary no problem.
At this stage, the dictionary, with all its keyvaluepairs is stored in a variable called result, and I'm creating an IEnumerable with
IEnumerator resultIdx = ((IEnumerable)result).GetEnumerator();
if (!resultIdx.MoveNext())
return (T)result;
object kvp = resultIdx.Current;
So far so good. I can see the value of the key and the value of the value in a watch, or by mouseover on the kvp variable.
But I cannot figure out how to get the value part of the keyvaluepair from kvp.
// None of these work - I get compile time errors, unboxing errors, or invalid cast errors.
object item = ((KeyValuePair<TKey, TValue>)kvp).Value;
object item = ((KeyValuePair<long, object>)kvp).Value;
object item = ((T)kvp).Value // Never had a hope for this, but desperation...
Does anyone have any idea how I can do this?
try adding dynamic kvp = resultIdx.Current; . Then you can use kvp.Value
You can rewrite the function into two functions like.
public T Load<T>(...)
//Or maybe public List<T> Load<T>(...)
and
public Dictionary<long, T> LoadD<T>(...)
Then you can cast result to KeyValuePair<long, T> in LoadD. You can call Load from LoadD to minimize code rewriting.
Answer provided by Dede in comments:
"Use Reflection ?
object key kvp.GetType().GetProperty("Key").GetValue(kvp);
object value kvp.GetType().GetProperty("Value").GetValue(kvp);
Not very optimized, but can work... – Dede 24"

Is there a way to make string a reference type in a collection?

I want to modify some strings that are contained in an object like say an array, or maybe the nodes in an XDocument (XText)XNode.Value.
I want to gather a subset of strings from these objects and modify them, but I don't know at runtime from what object type they come from.
Put another way, let's say I have objects like this:
List<string> fruits = new List<string>() {"apple", "banana", "cantelope"};
XDocument _xmlObject;
I want to be able to add a subset of values from the original collections to new lists like this:
List<ref string> myStrings1 = new List<ref string>();
myStrings1.Add(ref fruits[1]);
myStrings1.Add(ref fruits[2]);
List<ref string> myStrings2 = new List<ref string>();
IEnumerable<XNode> xTextNodes = getTargetTextNodes(targetPath); //some function returns a series of XNodes in the XDocument
foreach (XNode node in xTextNodes)
{
myStrings2.Add(((XText)node).Value);
}
Then change the values using a general purpose method like this:
public void Modify(List<ref string> mystrings){
foreach (ref string item in mystrings)
{
item = "new string";
}
}
Such that I can pass that method any string collection, and modify the strings in the original object without having to deal with the original object itself.
static void Main(string[] args)
{
Modify(myStrings1);
Modify(myStrings2);
}
The important part here is the mystrings collection. That can be special. But I need to be able to use a variety of different kinds of strings and string collections as the originals source data to go in that collection.
Of course, the above code doesn't work, and neither does any variation I've tried. Is this even possible in c#?
What you want is possible with C#... but only if you can fix every possible source for your strings. That would allow you to use pointers to the original strings... at a terrible cost, however, in terms of memory management and unsafe code throughout your application.
I encourage you to pursue a different direction for this.
Based on your edits, it looks like you're always working with an entire collection, and always modifying the entire collection at once. Also, this might not even be a string collection at the outset. I don't think you'll be able to get the exact result you want, because of the base XDocument type you're working with. But one possible direction to explore might look like this:
public IEnumerable<string> Modify(IEnumerable<string> items)
{
foreach(string item in items)
{
yield return "blah";
}
}
You can use a projection to get strings from any collection type, and get your modified text back:
fruits = Modify(fruits).ToList();
var nodes = Modify( xTextNodes.Select(n => (XText)n.Value));
And once you understand how to make a projection, you may find that the existing .Select() method already does everything you need.
What I really suggest, though, is that rather than working with an entire collection, think about working in terms of one record at a time. Create a common object type that all of your data sources understand. Create a projection from each data source into the common object type. Loop through each of the objects in your projection and make your adjustment. Then have another projection back to the original record type. This will not be the original collection. It will be a new collection. Write your new collection back to disk.
Used appropriately, this also has the potential for much greater performance than your original approach. This is because working with one record at a time, using these linq projections, opens the door to streaming the data, such that only one the one current record is ever held in memory at a time. You can open a stream from the original and a stream for the output, and write to the output just as fast as you can read from the original.
The easiest way to achieve this is by doing the looping outside of the method. This allows you to pass the strings by reference which will replace the existing reference with the new one (don't forget that strings are immutable).
And example of this:
void Main()
{
string[] arr = new[] {"lala", "lolo"};
arr.Dump();
for(var i = 0; i < arr.Length; i++)
{
ModifyStrings(ref arr[i]);
}
arr.Dump();
}
public void ModifyStrings(ref string item)
{
item = "blah";
}

Something similar to C# .NET Generic List in java

How would be a c# .net generic list in java?
somthing like that:
public class ClientList : List<Client> { }
the answer from Nikil was perfect, I just want to add to whoever wants to create a class from the List:
public class ClientList extends ArrayList<Client>
Java's List interface (java.util.List) can be generified. In other words, instances of List can be given a type, so only instances of that type can be inserted and read from that List. Here is an example:
List<String> list = new ArrayList<String>();
This list is now targeted at only String instances, meaning only String instances can be put into this list. If you try to put something else into this List, the compiler will complain.
The generic type checks only exists at compile time. At runtime it is possible to tweak your code so that a String List has other objects that String's inserted. This is a bad idea, though.
Accessing a Generic List
You can get and insert the elements of a generic List like this:
List<String> list = new ArrayList<String>();
String string1 = "a string";
list.add(string1);
String string2 = list.get(0);
Notice how it is not necessary to cast the object obtained from the List.get() method call, as is normally necessary. The compiler knows that this List can only contain String instances, so casts are not necessary.
Iterating a Generic List
You can iterate a generic List using an iterator, like this:
List<String> list = new ArrayList<String>();
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String aString = iterator.next();
}
Notice how it is not necessary to cast the object returned from the iterator.next() next call. Because the List is generified (has a type), the compiler knows that it contains String instances. Therefore it is not necessary to cast the objects obtained from it, even if it comes from its Iterator.
You can also use the new for-loop, like this:
List<String> list = new ArrayList<String>();
for(String aString : list) {
System.out.println(aString);
}
Notice how a String variable is declared inside the parantheses of the for-loop. For each iteration (each element in the List) this variable contains the current element (current String).

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.

Categories

Resources