I have the following property declared in my C# class, that we'll call MyMainClass:
private Dictionary<int, Dictionary<int, Dictionary<int, MyOtherClass>>> _oMyObjByTypeIDPeriodIDStatusID = null;
public Dictionary<int, Dictionary<int, Dictionary<int, MyOtherClass>>> oMyObjByTypeIDPeriodIDStatusID
{
get
{
return _oMyObjByTypeIDPeriodIDStatusID;
}
set
{
_oMyObjByTypeIDPeriodIDStatusID = value;
}
}
Sometimes, through reflection I'll run something like:
string propName = "oMyObjByTypeIDPeriodIDStatusID[2][4[6]";
object o = GetReflectedProperty(oMyMainClass, propName);
But that won't work because of the indexing, so instead I'll strip out the indexes and throw them in an int array. Then I run
int idxs = new int[] {2,4,6};
string propName = "oMyObjByTypeIDPeriodIDStatusID";
object o = GetReflectedProperty(oMyMainClass, propName);
That works, but now I have an object, whose value is of type
Dictionary<int, Dictionary<int, Dictionary<int, MyOtherClass>>>
And now I somehow have to traverse through each index until I finally get to the last nested Dictionary and the instance of MyOtherClass. I tried the following code:
for (int i = 0; i < idxs.Length; i++)
o = ((Dictionary<int, object>)o)[idxs[i]];
But that throws an error that it can't cast
Dictionary<int, Dictionary<int, Dictionary<int, MyOtherClass>>>
into
Dictionary<int, object>
which makes sense. My problem is, this code is generic, so I don't know what type it will be. In other words, I use it for different properties, so I don't know how many nested dictionaries there will be, so how can I cast it properly?
If you break apart going through the layers to two steps, one to go through the nested dictionaries and one to get the final value you can use the non-generic interface IDictonary and change your for loop to
for (int i = 0; i < idxs.Length - 1; i++)
o = ((IDictionary)o)[idxs[i]];
o = ((IDictionary)o)[idxs[idxs.Length - 1]];
and that should fix your casting errors.
You could just cast it to an IDictionary, if you don't know the generic type parameters at compile-time.
Just like Dictionary<TKey, TValue>, IDictionary has an indexer, so you should be able to iterate through the inner dictionaries using that interface.
The real issue here is that Dictionary<TKey,TValue> is strongly typed, so the system doesn't recognize it as Dictionary<int,object> even though TValue is derived from object. Try this instead (using Linq):
var recastDictionary = o
.Select(x => new { Key = x.Key, Value = (object)x})
.ToDictionary(x => x.Key, x => x.Value)
;
That should recast the values and give you a Dictionary<int,object> without any hassle or loss of data. (I'm sure there's more efficient ways to do it, but that should be acceptable enough, or at least give you an idea of what you need to do.)
Related
I've List Tuple with int and string in it, each time I add new position to this list, I check for the same string in already added elements. Once I see the same - the goal is to change int (to do +1). For example if we have (3, box; 1, apple; 2, PC) and we need to add "apple", so I must change existing "1, apple" to "2, apple".
But I can't do it because Item1 return error "The property has no setter".
Where my mistake?
Thanks.
string[] elements = s.Split(); // Contains all elements as strings.
List<Tuple<int, string>> elementsList = new List<Tuple<int, string>>();
var sortItems = elementsList.OrderBy(x => x.Item1);
for (int i = 0; i < elements.Length; i++)
{
foreach (var item in sortItems)
{
if (Equals(item.Item1, elements[i]))
{
item.Item1 += 1;
}
}
elementsList.Add(new Tuple<int, string>(1, elements[i]));
}
return elementsList;
According to the documentation of Tuple<T1,T2>, the properties cannot be written, which basically means that tuples are immutable. It is impossible to modify its contents in-place; it is necessary to create a new tuple.
Tuple<T1, T2> does not allow you to modify the values inside because it is immutable. Instead try using a Dictionary
string[] elements = s.Split(); // Contains all elements as strings.
IDictionary<string, int> elementsMap = new Dictionary<string, int>();
for (int i = 0; i < elements.Length; i++)
{
string name = elements[i];
if (elementsMap.ContainsKey(name))
{
elementsMap[name] += 1;
}
else
{
elementsMap.Add(name, 1);
}
}
return elementsMap;
Or through Linq(credit to Jeppe Stig Nielsen):
var elementsMap = elements.GroupBy(e => e).ToDictionary(g => g.Key, g => g.Count());
Tuples are immutable types, so basically they are not intented to be changed and have no setter. The reasons got listed up here : Why Tuple's items are ReadOnly?
Either you create a new tuple (as Codor suggested) or you just create your own implementation, like shown here: Why a Tuple, or a KeyValueItem, don't have a setter?
Tuple class is immutable which means its properties do no have public setters to overwrite the underlying value.
Tuples are not meant to be used in a key-value manner, which is probably closer to what you are trying to achieve. Tuples are more suitable for pairing - well they are could be used for more than 2 practically - two correlated pieces of data, such as coordinates for example, or width and height.
More examples in the answers of the link below :
Practical example where Tuple can be used in .Net 4.0?
When should you use a Dictionary ?
Simply when you are doing lookups by a certain unique key to read/modify the an object(which could be a collection it self) corresponding to that key. a quick google around would show you tons of uses.
Ok, I know this is impossible, but I'm wondering if anyone knows of a way to get around this.
List<int> numberList = new List<int>();
List<object> objectList = (List<object>)numberList;
This generates the following error:
Cannot convert type 'System.Collections.Generic.List{int}' to 'System.Collections.Generic.List{object}'
This is a simple example, but here is my real problem. I have a method that dynamically generates an IQueryable{IGrouping{TKey, TElement}} instance. However, since in my case TKey is dynamically generated, I can only return an IQueryable to the caller.
I want to be able to return an IQueryable{IGrouping{dynamic, TElement}} to the caller. However, the compiler complains because just like in my first example, its casting TKey as an object (for dynamic), and won't let me cast it that way. Is there any way around that?
Update:
Here is a closer example of what I'm trying to solve:
List<int> testList = new List<int>();
var qry = new EnumerableQuery<int>( testList );
var objectQry = (IQueryable<object>)qry;
While this example doesn't produce a compile-time error, is does produce a casting exception when you try to run it. For some reason, even though IQuerable is covariant (thanks Servy for your comments on that), once I introduce an implementing class such as EnumerableQuery, it fails.
Try this.
List<int> numberList = new List<int>();
List<object> objectList = numberList.Cast<object>().ToList();
Enumerable.Cast<T>
Casts the elements of an IEnumerable to the specified type.
You need to construct a copy of each new dictionary, because dictionaries as well as lists are not covariant with respect to any of their generic arguments.
(The keys are also provided in output through the Keys property, and the sequence of pairs itself, so even IDictionary<TKey, TValue> cannot be covariant with respect to TKey.)
You can use Select to map each object to a new object:
List<Dictionary<int, string>> listOfDicts = new List<Dictionary<int, string>>();
List<Dictionary<object, string>> newList = listOfDicts.Select(dic =>
dic.ToDictionary(pair => (object)pair.Key, pair => pair.Value))
.ToList();
IQueryable<T> on the other hand is covariant, as is IGrouping, so you can cast interfaces of those types up:
IQueryable<IGrouping<dynamic, int>> query = null;
IQueryable<IGrouping<object, int>> otherQuery = query;
That compiles and runs just fine.
I get an 'Unable to cast object of type 'WhereSelectEnumerableIterator'' when I try to convert one Dictionary type to another from within a Select. I don't see any obvious reasons. Please help.
void Main()
{
Dictionary<string,Dictionary<string,string>> test = new Dictionary<string,Dictionary<string,string>>();
Dictionary<string,string> inner = new Dictionary<string,string>();
for(int n = 0; n< 10; n++)
{
var data = n.ToString();
inner[data] = data;
}
for(int n2 = 0; n2 < 10; n2++)
{
test[Guid.NewGuid().ToString()] = inner;
}
// test conversion
var output = (Dictionary<Guid,Dictionary<string,string>>) test.Select(x => new KeyValuePair<Guid, Dictionary<string,string>>(new Guid(x.Key), x.Value));
}
You have an IEnumerable<Guid,Dictionary<string,string>>. You apparently want a Dictionary<Guid,Dictionary<string,string>>. When you try to assign one to the other, you get a compile time error telling you that your IEnumerable isn't a Dictionary, it's just an IEnumerable. You provided a cast, which is a way of saying, "Sorry compiler, but you're wrong, I know better than you; this IEnumerable is in fact a dictionary, under the hood." Sadly, this is not the case here. You didn't actually have a Dictionary, you had a WhereSelectEnumerableIterator, which isn't a Dictionary.
There are any number of ways you have of creating a dictionary, one of which is to use ToDictionary:
var output = test.ToDictionary(x => new Guid(x.Key), x => x.Value);
On an unrelated note, you've created just one inner dictionary, and set it as the value of every single key you create. You might be under the impression that you've copied this dictionary 10 times. You have not. There only ever is one Dictionary<string, string> here; you just have 10 different GUIDs all pointing to that one dictionary. You'll need to create a bunch of different dictionaries if you want these keys to actually point to different dictionaries.
I've a class like as below:
public class Source
{
...
...
public List<Dictionary<int, int>> blanks { get; set; }
}
I've created an object of this and a Dictionary for it. I filled 'dic' Dictionary. Then, I add this dic to the blanks list.
Source src = new Source();
Dictionary<int, int> dic = new Dictionary<int, int>();
dic.Add(30, 50);
dic.Add(40, 60);
src.blanks.Add(dic);
And I try to access these 'Key' and 'Value' elements. But, I can't.
int a = src.blanks[0].Key;
int b = src.blanks[0].Value;
What can I do to access these elements?
Thanks.
src.blanks[0] is a whole dictionary, not a single KeyValuePair<int,int>. That is why you cannot access a .Key or .Value on it - there are potentially many keys, and many values associated with them.
You can access all key-value pairs in a dictionary at position zero by enumerating them, like this:
foreach (var kvp in src.blanks[0]) {
int a = kvp.Key;
int b = kvp.Value;
Console.WriteLine("Key:{0} Value:{1}", a, b);
}
blanks[0] returns a Dictionary<int, int>, you need to specify key of your item.
src.blanks[0][key]
Or loop through your values:
foreach(var pair in src.blanks[0])
{
int currentKey = pair.Key;
int currentValue = pair.Value;
}
You are trying to access a dictionary in the list which has no Key property. The Keyvaluepairs in a dictionary have keys.
So assuming you want to look into the first dictionary in the list:
Dictionary<int, int> dict = s.blanks[0];
// lookup 30:
int value = dict[30]; // 40
to get the value you should first index the list and then index the dictionary
to get value
int b = (src.blanks[0])[0]
You want something like the following:
var listItem = src.blanks[0];
var dictionaryItem = listItem[0];
var a = dictionaryItem.Key;
var b = dictionaryItem.Value;
Nevertheless, i advice you to get rid of those "nested generics" List<Dictionary<..,..>. You won't be able to distinguish what kind of object you're dealing with if you use these nested structs.
Use other structs that better represent your business logic. For example, you could derive your own class from List
I understand why there isn't a method built in to do this, however I want a collection object that will allow Value change possibly during Enumeration.
Imagine the following:
Dictionary<string, List<string>> test = new Dictionary<string, List<string>> {{"key", null}};
Lets say I have 20 of these within a class which implements IEnumberable
I'd like to use lambda or a simple foreach to iterate through the class and find the object matching a key, then store my List<T> with the Value parameter.
You might be looking for a collection called multimap. See here for my implementation of it.
As you have discovered you can't change a DictionaryEntry through the Value property - you have to go through the Item accessor using the Key.
One option is to turn your Where results to an array then loop to get the matching Keys:
Dictionary<string, List<string>> test = new Dictionary<string, List<string>>
{{"key", null}};
test.Add("newKey",null);
var matches = test.Where(di => di.Key == "key").ToArray();
foreach(var di in matches) {
test[di.Key] = new List<string> {"one","two"};
You can just use this to modify a value in a dictionary:
public static void ChangeValue(string indexKey,List<string> newValue)
{
if (test.ContainsKey(indexKey))
keysDictionary[indexKey] = newValue;
}
You could avoid using an enumerator altogether, e.g.
var data = myEnumerable.ToArray();
for(var i = 0; i < data.Length; i++) {
// here you can manipulate data[i] to your heart's content
}