using TryGetValue from key and adding not working as intened - c#

I have this code to retrieve a value(integer array) out of a dictionary Then increment one of the elements inside the integer array based on the if statement it is in..
Dictionary<string, int[]> ResultDic = new Dictionary<string, int[]>();
if (TeamOnePoint > TeamTwoPoint)
{
ResultDic.TryGetValue(TeamOneResult, out OutOfDic);
OutOfDic[0]++;
OutOfDic[1]++;
////ResultDic.Remove(TeamOneResult);
////ResultDic.Add(TeamOneResult, OutOfDic);
ResultDic[TeamOneResult] = OutOfDic;;
ResultDic.TryGetValue(TeamTwoResult, out OutOfDic);
OutOfDic[0]++;
OutOfDic[2]++;
////ResultDic.Remove(TeamTwoResult);
////ResultDic.Add(TeamTwoResult, OutOfDic);
ResultDic[TeamTwoResult] = OutOfDic;
}
Now the problem I have is that evertime I read the modified OutOfDic array back into the dictionary into the value part where I specified the Key, every value in the dictionary is modified as well, and not just the key I specified.
The commented part gives the same result as the non commented part. How do I fix this problem to only add the value to the specified key?

The behaviour you've described is only possible if you've added the same array to the dictionary multiple times. Since arrays are reference types every change will affect all values in the dictionay.
So instead of doing this(for example):
Dictionary<string, int[]> ResultDic = new Dictionary<string, int[]>();
int[] array = { 1, 2, 3, 4, 5 };
ResultDic.Add("TeamOne", array);
ResultDic.Add("TeamTwo", array);
You should do this:
int[] array = { 1, 2, 3, 4, 5 };
ResultDic.Add("TeamOne", array);
array = new int[] { 1, 2, 3, 4, 5 };
ResultDic.Add("TeamTwo", array);
Note that it's not necessary to re-assign the array to the dictionary for the same reason (it's a reference type). So you can remove these lines:
ResultDic[TeamOneResult] = OutOfDic;;
// ....
ResultDic[TeamTwoResult] = OutOfDic;

Related

Why converting List to Array not referencing in c#?

An array in .Net is a reference type.
Given the two code segments above.
Question: why setting value varible "fixedItem" affects varible "data" in the first segment code, but the second segment code is does not affects
First code segment:
var data = new List<IList<int>>();
data.Add(new List<int>() { 1, 2, 3 });
data.Add(new List<int>() { 3, 8, 6,5 });
data.Add(new List<int>() { 1, 2 });
var fixedItem = data.Last();
fixedItem[1] = 8;
//Result:
//data = {{1,2,3}, {3,8,6,5}, {1,8}}
Second code segment:
var data = new List<IList<int>>();
data.Add(new List<int>() { 1, 2, 3 });
data.Add(new List<int>() { 3, 8, 6,5 });
data.Add(new List<int>() { 1, 2 });
var fixedItem = data.Last().ToArray();
fixedItem[1] = 8;
//Result:
//data = {{1,2,3}, {3,8,6,5}, {1,2}}
docs
according to documentation list.ToArray() method returns array with copies of original list
With the first version, fixedItem is the last item from the outer list, i.e. the third inner list. The same instance. Changing that list will be visible everywhere that list is referenced.
However, in the second version you use ToArray(), which creates a separate copy of the list contents, in a vector. You can do anything you like with your isolated copy - it is a different collection instance (and different collection type). Changing it will only be visible to things that reference the copy. The original list is unaffected because it is a different collection.
This is because with ToArray method, you have created a copy of the original list.
Official documentation
Copies the elements of the List to a new array.
There are many thing involve over here.
When you do ToArray in second segment , it actually create new variable and new collection.
Here you have created list of int, in this integer is value type so when it create new collection it copies those value and assign new memory location.
Now instead of int if you have created some reference type like object of class then if you change value over in one collection affect other. Here variable is new but it still reference the same memory location.
Look at the data types you've created after using Last() from Linq.
You receive two very different data types.
One would be a list while the other is just int[]. They have fairly different functionalities and ways to represent and handle data.

SortedList<> and its strange setters

I initially had some code, which when simplified, looks like this:
var planets = new List<Planet>
{
new Planet {Id = 1, Name = "Mercury"},
new Planet {Id = 2, Name = "Venus"},
};
I got into a scenario where the list was being populated all at once, but the reads weren't fast enough. And so, I changed this to use a SortedList instead.
I later realized that I could rewrite it like this
var planets = new SortedList<int, Planet>
{
{1, new Planet {Id = 1, Name = "Mercury"}},
{2, new Planet {Id = 2, Name = "Venus"}},
//in my actual code, i am reading the ids from a db
};
But before I got to this approach, I had the code written like this
var planets = new SortedList<int, Planet>
{
Keys = {1, 2},
Values =
{
new Planet {Id = 1, Name = "Mercury"},
new Planet {Id = 2, Name = "Venus"},
}
};
which gives me this exception
System.NotSupportedException: This operation is not supported on SortedList
nested types because they require modifying the original SortedList.
at System.ThrowHelper.ThrowNotSupportedException(ExceptionResource resource)
at System.Collections.Generic.SortedList`2.KeyList.Add(TKey key)
which I found to be very strange, coz IMHO, I wasn't really modifying the "original SortedList" as it claims, and what "nested types" is it talking about? Is it the list of keys internal to the SortedList?
I see then that the Keys and Values properties in SortedList don't actually have setters. They are read-only properties, and yet, I don't get a compile-time error. I am allowed to make a set call, as I can see in the stack trace with KeyList.Add. I feel the only reason why this fails is because of an explicit check within SortedList, which seems bizarre to me!
For instance
var str = new String {Length = 0}; gives me a compile-time error as expected, since Length is a read-only property, as does planets.Keys = null;
Someone please tell me - what simple fact am I overlooking here?
The code that you've written is comparable to this:
var planets = new SortedList<int, Planet>();
planets.Keys.Add(1);
planets.Keys.Add(2);
planets.Values.Add(new Planet { Id = 1, Name = "Mercury" });
planets.Values.Add(new Planet { Id = 2, Name = "Venus" });
SortedList requires that you add the value and key at the same time via SortedList<TKey, TValue>.Add(TKey key, TValue value) method, so that it can sort the value by the key. The implementation of the IList<T> which is used for Keys and Values internally does not support adding a respective key or value independently via the IList<T>.Add(T value) method.
You should be able to reproduce this error by calling Keys.Add(...) or Values.Add(...)
My initial query about the SortedList has now minimized to this concern about array, collection & object initializers, and the way the compiler interprets them differently. Thanks to #Haney again for the first answer to guide me towards this point of view, and to ILSpy for these insights.
Here are some array and collection initializers:
int[] a = { 1, 2, 3 };
int[] b = new int[] { 1, 2, 3 };
IList<int> c = { 1, 2, 3 };
IList<int> d = new int[] { 1, 2, 3 };
They all look kind of similar. Here, the compiler produces the exact same output for a & b. For c, we will get this compile-time error:
Can only use array initializer expressions to assign to array types.
Try using a new expression instead.
which makes sense since we shouldn't use array initializers for collections. But then, d produces the exact same result as a & b. And I thought that was an array initializer as well. Apparently not.
Now consider this class
class MyCollectionContainer
{
public int[] MyIntArray { get; set; }
public IList<int> MyList { get; set; }
}
and this code that operates on it
var containerA = new MyCollectionContainer { MyIntArray = { 1, 2, 3 } };
var containerB = new MyCollectionContainer { MyIntArray = new int[]{ 1, 2, 3 } };
var containerC = new MyCollectionContainer { MyList = { 1, 2, 3 } };
var containerD = new MyCollectionContainer { MyList = new int[]{ 1, 2, 3 } };
containerA gives this compile-time error:
Cannot initialize object of type 'int[]' with a collection initializer
For containerB, the compiler effectively converts it into this code:
MyCollectionContainer myCollectionContainer = new MyCollectionContainer();
myCollectionContainer.MyIntArray = new int[] {1, 2, 3};
For containerD, its pretty much the same, barring the fact that its another property that gets initialized:
MyCollectionContainer myCollectionContainer = new MyCollectionContainer();
myCollectionContainer.MyList = new int[] {1, 2, 3};
For containerC, the compiler morphs it into:
MyCollectionContainer myCollectionContainer = new MyCollectionContainer();
myCollectionContainer.MyList.Add(1);
myCollectionContainer.MyList.Add(2);
myCollectionContainer.MyList.Add(3);
This results in a run-time NullReferenceException since MyList is not initialized.
This means the only valid ways to initialize the collection container object here is containerB and containerD. To me, this clearly shows that object initializers are different when compared to array & collection initializers, in the way the compiler interprets them.

Sorted ObservableCollection with KeyValuePair and duplicated keys

I have a foreach loop. Each loop I get a value (long) and name (string) of an item.
Now I want to create an ObservableCollection<KeyValuePair<Int32, String>>() but not with the value, but with a new index starting with 0. The collection should be sorted by the value. If there is a duplicate value, both items should be added.
Example:
var tempList= new SortedList<long, String>();
foreach (INetworkItem item in ListOfItems)
{
long value = item.ticks;
string name = item.name;
// tried:
// tempList.Add(value , name );
...
}
Content ListOfItems:
3444423423, "aaaaaa"
9999457567, "bbbbbb"
1111100065, "cccccc"
3444423423, "aaaaaa"
After the foreach-loop I need an ObservableCollection<KeyValuePair<Int32, String>>() which looks like:
0, "cccccc"
1, "aaaaaa"
2, "aaaaaa"
3, "bbbbbb"
How can I do that?
First you need to sort the ListOfItems by ticks.
var items = ListOfItems.OrderBy(x => x.ticks);
Then you should convert them to the format you want
0, "cccccc"
1, "aaaaaa"
2, "aaaaaa"
3, "bbbbbb"
You can do this by Select
items = items.Select((x, i) => new KeyValuePair<int,string>(i, x.name));
Then initialize your collection with it.
var collection = new ObservableCollection<KeyValuePair<Int32, String>>(items);

C# Dictionary: Multiple KEYS per Value

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}

Sorting an array related to another array

I have two arrays, x and y, where y is the value of the tens of every element in x. Now, I want to sort y. But, the order of y will be different of x's. So, I can't tell after sorting which element in y was related to, for instance, x[0].
I want a "double sorting" maybe.
Array.Sort has an overload that accepts two arrays; one for the keys, and one for the items. The items of both are sorted according to the keys array:
int[] keys = { 1, 4, 3, 2, 5 };
string[] items = { "abc", "def", "ghi", "jkl", "mno" };
Array.Sort(keys, items);
foreach (int key in keys) {
Console.WriteLine(key); // 1, 2, 3, 4, 5
}
foreach (string item in items) {
Console.WriteLine(item); // abc, jkl, ghi, def, mno
}
So in your case, it sounds like you want:
Array.Sort(y,x); // or Sort(x,y); - it isn't 100% clear
How about?
var selectedArr = new int[] { 1, 3, 5, 7, 9 };
var unorderArr = new int[] { 9, 7, 5, 3, 1 };
var orderedArr = unorderArr.OrderBy(o => selectedArr.IndexOf(o));
If we have two arrays of complex objects and want to sort them according to one of the two arrays then we can use the next approach:
// We want to sort "people" array by "Name" and
// accordingly to it reorder "countries" array.
Person[] people = new Person[]
{
new Person {Name = "Fill"},
new Person {Name = "Will"},
new Person {Name = "Bill"},
};
Country[] countries = new Country[]
{
new Country {Name = "Canada"},
new Country {Name = "UK"},
new Country {Name = "USA"}
};
// Here we sort "people" array, but together with each "Person"
// in sorted array we store its "index" in unsorted array. Then we
// will use this "index" to reorder items in "countries" array.
var sorted = people
.Select((person, index) => new {person, index})
.OrderBy(x => x.person.Name)
.ToArray();
// Here "people" array is sorted by "Name", and
// "contries" array is reordered accordingly to it.
people = sorted.Select(x => x.person).ToArray();
countries = sorted.Select(x => countries[x.index]).ToArray();
Another approach is to use overload of the method Array.Sort with IComparer. At first we should implement IComparer:
private class PeopleComparer : IComparer<Person>
{
public int Compare(Person x, Person y)
{
return x.Name.CompareTo(y.Name);
}
}
And then we can sort two our arrays:
Array.Sort(people, countries, new PeopleComparer());
Here is complete sample that demonstrates these two approaches.
If y is always the tens value of x, y probably shouldn't exist - you should probably just calculate it's value directly off of x when needed.
In general, sorting parallel arrays is only possible (without hand rolling a sort algorithm) when the sort algorithm takes a custom "swap" function, which you can implement in terms of swapping elements in both arrays simultaneously. std::sort in C++ and qsort in C don't allow this.
Also in the general case, consider a single array where the element is a pair of items, rather than a parallel array for each item. This makes using "standard" algorithms easier.

Categories