C# dictionary adding to wrong key/value pair - c#

I have the following foreach loop in my program:
Dictionary<string, int[]> summaryDate_clipsEpisodesImps = new Dictionary<string, int[]>();
Dictionary<string, int[]> summaryPartner_clipsEpisodesImps = new Dictionary<string, int[]>();
foreach (DataRow row1 in dt10.Rows)
{
int[] numbers = new int[3];
numbers[0] = 0;
numbers[1] = 0;
numbers[2] = 0;
if (!dictionary1.ContainsKey(row1["value1"].ToString().Trim()))
{
dictionary1.Add(row1["value1"].ToString().Trim(), numbers);
}
if (!dictionary2.ContainsKey(row1["value2"].ToString().Trim()))
{
dictionary2.Add(row1["value2"].ToString().Trim(), numbers);
}
if (row1["yes_or_no"].ToString().Trim() == "yes")
{
dictionary1[row1["value1"].ToString().Trim()][0] = dictionary1[row1["value1"].ToString().Trim()][0] + Convert.ToInt32(row1["a_number"]);
dictionary2[row1["value2"].ToString().Trim()][0] = dictionary2[row1["value2"].ToString().Trim()][0] + Convert.ToInt32(row1["a_number"]);
}
}
Essentially, I am looping through a datatable and creating a dictionary of string/int arrays based on values that I find in each record. Then I'm trying to increment the first value in the array based on the presence of another field in the record.
My problem occurs in the if statement when I check for the yes_or_no value. When the second line incrementing dictionary2 is present in this statement, the value in dictionary 1 is incremented by the same value. I have no idea why this is the case.
Please let me know if this isn't entirely clear. Thanks in advance for the help.

This is because the values in both dictionary entries point to the same instance of numbers. This means any change to one will affect the other.
To put a new instance in each dictionary you could do something similar to:
...
if (!dictionary1.ContainsKey(row1["value1"].ToString().Trim()))
{
dictionary1.Add(row1["value1"].ToString().Trim(), GetNumbersArray());
}
if (!dictionary2.ContainsKey(row1["value2"].ToString().Trim()))
{
dictionary2.Add(row1["value2"].ToString().Trim(), GetNumbersArray());
}
...
private int[] GetNumbersArray()
{
int[] numbers = new int[3];
numbers[0] = 0;
numbers[1] = 0;
numbers[2] = 0;
return numbers;
}

You're adding a pointer to the array to each dictionary. Both dictionaries look at the same array, so when you modify the array through dictionary2, dictionary1 sees the changes.

Related

Large list size times out practice problem

I was working on a HackerRank practice problem and ran into a interesting error
when finished. This code works on every case except the ones causing it to fail
(and they are all timeout exceptions)
Practice Problem
The short version of the problem is you are given a leaderboard (int[]) and "alice's" scores (int[]) you have to find what place she got for each score in the leaderboard...View the link above for the whole problem.
My Solution
public static int[] climbingLeaderboard(int[] scores, int[] alice)
{
int[] results = new int[alice.Length]; //The array that stores alice's placements for each score
Dictionary<int, List<int>> scoresDict = new Dictionary<int, List<int>>(); //Key = x place (1st, 2nd, etc), the list is all the numbers that are at x place
for(int i = 0; i < alice.Length; i++)
{
List<int> alicePlace = scores.ToList<int>();
//Add the score to the array (converted to list for .Add)
alicePlace.Add(alice[i]);
//Sorts in reverse order to get the new score in the correct place
alicePlace = RecalculateScores(alicePlace);
//Breaks down the scores into the dictionary above
scoresDict = SeperateScores(alicePlace);
//Addes the place to the array
results[i] = GetPlace(scoresDict, alice[i]);
}
return results;
}
//Returns scores[] in reverse SORTED order
public static List<int> RecalculateScores(List<int> scores)
{
List<int> scoresRet = scores;
scoresRet.Sort();
scoresRet.Reverse();
return scoresRet;
}
//Gets the place (key) for where score is in the dict's value list
public static int GetPlace(Dictionary<int, List<int>> dict, int score)
{
foreach (int i in dict.Keys)
{
foreach (int ii in dict[i])
{
if (ii == score)
{
return i;
}
}
}
return -1;
}
//Seperates the array into a dictionary by score placement
public static Dictionary<int, List<int>> SeperateScores(List<int> scores)
{
int placeholder = scores[0];
int currentPlace = 1;
Dictionary<int, List<int>> scoresByPlace = new Dictionary<int, List<int>>();
for (int i = 0; i < scores.Count(); i++)
{
if (scores[i] == placeholder)
{
if (!scoresByPlace.Keys.Contains(currentPlace) || scoresByPlace[currentPlace] == null)
{
scoresByPlace[currentPlace] = new List<int>();
}
scoresByPlace[currentPlace].Add(scores[i]);
placeholder = scores[i];
}
else
{
currentPlace++;
if (!scoresByPlace.Keys.Contains(currentPlace) || scoresByPlace[currentPlace] == null)
{
scoresByPlace[currentPlace] = new List<int>();
}
scoresByPlace[currentPlace].Add(scores[i]);
placeholder = scores[i];
}
}
return scoresByPlace;
}
Error
Whenever it gets tested with a large array amount (2 Million for examples) it returns an timeout exception (probably HackerRank generated to make it harder)
Attempted solution
Believe it or not but I changed a lot of things on the above code. For one,the results array in the first function used to be a list but I changed to array to make it faster. I feel the dictionary/List is slowing everything down but I need them for the solution (Especially the dictionary). Any Help would be appreciated

Dictionary as key of another Dictionary / count number of equal Dictionarys

I want to use a Dictionary as the key of another Dictionary, but the comparision for equal keys shall not use the reference for comparision, but the Dictionary's content. How can I do this?
Here is what I currently have: I want to know how many equal histograms (implemented as Dictionary) I have. For that I create another Dictionary numberOfHistogramOccurences with the histograms as key, the value shall then be increased by one on each new addition of a histogram with equal content.
I am also open for hints on other ways of counting content-equal histograms/Dictionaries.
// define two histograms with equal content
Dictionary<int, int>[] histograms = new Dictionary<int, int>[2];
histograms[0] = new Dictionary<int, int>();
histograms[0][0] = 3;
histograms[0][5] = 1;
histograms[0][10] = 8;
histograms[1] = new Dictionary<int, int>();
histograms[1][0] = 3;
histograms[1][5] = 1;
histograms[1][10] = 8;
// use the equal histograms as key.
// as they are reference types, their reference is compared and not their content ):
var numberOfHistogramOccurences = new Dictionary<Dictionary<int, int>, int>();
foreach (var histogram in histograms)
{
if (!numberOfHistogramOccurences.ContainsKey(histogram))
numberOfHistogramOccurences[histogram] = 0;
numberOfHistogramOccurences[histogram]++;
}
// this gives two different keys;
// I need one key with a value of 2,
// as both histograms are equal
Debug.WriteLine("Number of different keys in numberOfHistogramOccurences: " + numberOfHistogramOccurences.Keys.Count);
Following my comment, using a custom comparer. Note that I added another histogram in order to check that it sees differences as well.
public static void Main()
{
// define two histograms with equal content
Dictionary<int, int>[] histograms = new Dictionary<int, int>[2];
histograms[0] = new Dictionary<int, int>();
histograms[0][0] = 3;
histograms[0][5] = 1;
histograms[0][10] = 8;
histograms[1] = new Dictionary<int, int>();
histograms[1][0] = 3;
histograms[1][5] = 1;
histograms[1][10] = 8;
histograms[2] = new Dictionary<int, int>();
histograms[2][0] = 1;
histograms[2][5] = 3;
histograms[2][10] = 8;
// use the equal histograms as key.
// as they are reference types, their reference is compared and not their content ):
/* change this line */
var numberOfHistogramOccurences = new Dictionary<Dictionary<int, int>, int>(new HistogramComparer());
/* ---------------- */
foreach (var histogram in histograms)
{
if (!numberOfHistogramOccurences.ContainsKey(histogram))
numberOfHistogramOccurences[histogram] = 0;
numberOfHistogramOccurences[histogram]++;
}
// this gives two different keys;
// I need one key with a value of 2,
// as both histograms are equal
Debug.WriteLine("Number of different keys in numberOfHistogramOccurences: " + numberOfHistogramOccurences.Keys.Count);
}
And the custom comparer class HistogramComparer:
public class HistogramComparer : IEqualityComparer<Dictionary<int, int>>
{
public bool Equals(Dictionary<int, int> x, Dictionary<int, int> y)
{
// check that y contains all x.Keys
foreach (var key in x.Keys)
if (!y.Keys.Contains(key))
return false;
// check that x contains all y.Keys
foreach (var key in y.Keys)
if (!x.Keys.Contains(key))
return false;
// check that keys have same values
foreach (var entry in x)
if (y[entry.Key] != entry.Value)
return false;
return true;
}
public int GetHashCode(Dictionary<int, int> obj)
{
int hash = 0;
foreach (var entry in obj)
hash ^= (entry.Key ^ entry.Value);
return hash;
}
}

Remove All Indexes in String

I have dictionary of int (Dictionary<int, int>) which has index of all parenthesis in a string (key was openStartParenthesisIndex and value was closeEndParenthesisIndex)
e.g in text
stringX.stringY(())() -> stringX.stringY$($()^)^$()^
$ = openParenthesisStartIndex
^ = closeParenthesisEndIndex
Dictionary items:
key value
(openParenthesisStartIndex) --- (closeParenthesisEndIndex)
item1 15 19
item2 16 18
item3 19 21
My problem was when I loop my dictionary and try to remove it on string, next loop the index was not valid since its already change because I remove it .
string myText = "stringX.stringY(())()";
Dictionary<int, int> myIndexs = new Dictionary<int, int>();
foreach (var x in myIndexs)
{
myText = myText.Remove(item.Key, 1).Remove(item.Value-1);
}
Question: how can i remove all index in a string (from startIndex[key] to endIndex[value])?
To prevent the index from changing, one trick is to remove the occurences starting from the end:
string myText = stringX.stringY(())();
Dictionary<int, int> myIndexs = new Dictionary<int, int>();
var allIndexes = myIndexs.Keys.Concat(myIndexs.Values);
foreach (var index in allIndexes.OrderByDescending(i => i))
{
myText = myText.Remove(index, 1);
}
Note that you probably don't need a dictionary at all. Consider replacing it by a list.
StringBuilder will be more suited to your case as you are continuously changing data. StringBuilder MSDN
Ordering the keys by descending order will work as well for removing all indexes.
Another workaround could be to place an intermediary character at required index and replace all instances of that character in the end.
StringBuilder ab = new StringBuilder("ab(cd)");
ab.Remove(2, 1);
ab.Insert(2, "`");
ab.Remove(5, 1);
ab.Insert(5, "`");
ab.Replace("`", "");
System.Console.Write(ab);
Strings when you make a change to a string a new string is always created, so what you want is to create a new string without the removed parts. This code is a little bit complicated because of how it deals with the potential overlap. Maybe the better way would be to cleanup the indexes, making a list of indexes that represent the same removals in the right order without overlap.
public static string removeAtIndexes(string source)
{
var indexes = new Tuple<int, int>[]
{
new Tuple<int, int>(15, 19),
new Tuple<int, int>(16, 18),
new Tuple<int, int>(19, 21)
};
var sb = new StringBuilder();
var last = 0;
bool copying = true;
for (var i = 0; i < source.Length; i++)
{
var end = false;
foreach (var index in indexes)
{
if (copying)
{
if (index.Item1 <= i)
{
copying = false;
break;
}
}
else
{
if (index.Item2 < i)
{
end = true;
}
}
}
if (false == copying && end)
{
copying = true;
}
if(copying)
{
sb.Append(source[i]);
}
}
return sb.ToString();
}

An integer array as a key for Dictionary

I wish to have the dictionary which uses an array of integers as keys, and if the integer array has the same value (even different object instance), they will be treated as the same key. How should I do it?
The following code does not work as b is different object instances.
int[] a = new int[] { 1, 2, 3 };
int[] b = new int[] { 1, 2, 3 };
Dictionary<int[], string> dic = new Dictionary<int[], string>();
dic.Add(a, "haha");
string output = dic[b];
You can create an IEqualityComparer to define how the dictionary should compare items. If the ordering of items is relevant, then something like this should work:
public class MyEqualityComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] x, int[] y)
{
if (x.Length != y.Length)
{
return false;
}
for (int i = 0; i < x.Length; i++)
{
if (x[i] != y[i])
{
return false;
}
}
return true;
}
public int GetHashCode(int[] obj)
{
int result = 17;
for (int i = 0; i < obj.Length; i++)
{
unchecked
{
result = result * 23 + obj[i];
}
}
return result;
}
}
Then pass it in as you create the dictionary:
Dictionary<int[], string> dic
= new Dictionary<int[], string>(new MyEqualityComparer());
Note: calculation of hash code obtained here:
What is the best algorithm for an overridden System.Object.GetHashCode?
Maybe you should consider using a Tuple
var myDictionary = new Dictionary<Tuple<int,int>, string>();
myDictionary.Add(new Tuple<int,int>(3, 3), "haha1");
myDictionary.Add(new Tuple<int,int>(5, 5), "haha2");
According to MSDN , Tuple objects Equals method will use the values of the two Tuple objects
The easiest way if you don't care about actual hashing may just be to convert the array into a string. Adding a space to avoid numbers joining.
dic.Add(String.Join(" ",a), "haha");

Adding values to a C# array

Probably a really simple one this - I'm starting out with C# and need to add values to an array, for example:
int[] terms;
for(int runs = 0; runs < 400; runs++)
{
terms[] = runs;
}
For those who have used PHP, here's what I'm trying to do in C#:
$arr = array();
for ($i = 0; $i < 10; $i++) {
$arr[] = $i;
}
You can do this way -
int[] terms = new int[400];
for (int runs = 0; runs < 400; runs++)
{
terms[runs] = value;
}
Alternatively, you can use Lists - the advantage with lists being, you don't need to know the array size when instantiating the list.
List<int> termsList = new List<int>();
for (int runs = 0; runs < 400; runs++)
{
termsList.Add(value);
}
// You can convert it back to an array if you would like to
int[] terms = termsList.ToArray();
Edit: a) for loops on List<T> are a bit more than 2 times cheaper than foreach loops on List<T>, b) Looping on array is around 2 times cheaper than looping on List<T>, c) looping on array using for is 5 times cheaper than looping on List<T> using foreach (which most of us do).
Using Linq's method Concat makes this simple
int[] array = new int[] { 3, 4 };
array = array.Concat(new int[] { 2 }).ToArray();
result
3,4,2
If you're writing in C# 3, you can do it with a one-liner:
int[] terms = Enumerable.Range(0, 400).ToArray();
This code snippet assumes that you have a using directive for System.Linq at the top of your file.
On the other hand, if you're looking for something that can be dynamically resized, as it appears is the case for PHP (I've never actually learned it), then you may want to use a List instead of an int[]. Here's what that code would look like:
List<int> terms = Enumerable.Range(0, 400).ToList();
Note, however, that you cannot simply add a 401st element by setting terms[400] to a value. You'd instead need to call Add() like this:
terms.Add(1337);
By 2019 you can use Append, Prepend using LinQ in just one line
using System.Linq;
and then in NET 6.0:
terms = terms.Append(21);
or versions lower than NET 6.0
terms = terms.Append(21).ToArray();
Answers on how to do it using an array are provided here.
However, C# has a very handy thing called System.Collections
Collections are fancy alternatives to using an array, though many of them use an array internally.
For example, C# has a collection called List that functions very similar to the PHP array.
using System.Collections.Generic;
// Create a List, and it can only contain integers.
List<int> list = new List<int>();
for (int i = 0; i < 400; i++)
{
list.Add(i);
}
Using a List as an intermediary is the easiest way, as others have described, but since your input is an array and you don't just want to keep the data in a List, I presume you might be concerned about performance.
The most efficient method is likely allocating a new array and then using Array.Copy or Array.CopyTo. This is not hard if you just want to add an item to the end of the list:
public static T[] Add<T>(this T[] target, T item)
{
if (target == null)
{
//TODO: Return null or throw ArgumentNullException;
}
T[] result = new T[target.Length + 1];
target.CopyTo(result, 0);
result[target.Length] = item;
return result;
}
I can also post code for an Insert extension method that takes a destination index as input, if desired. It's a little more complicated and uses the static method Array.Copy 1-2 times.
Based on the answer of Thracx (I don't have enough points to answer):
public static T[] Add<T>(this T[] target, params T[] items)
{
// Validate the parameters
if (target == null) {
target = new T[] { };
}
if (items== null) {
items = new T[] { };
}
// Join the arrays
T[] result = new T[target.Length + items.Length];
target.CopyTo(result, 0);
items.CopyTo(result, target.Length);
return result;
}
This allows to add more than just one item to the array, or just pass an array as a parameter to join two arrays.
You have to allocate the array first:
int [] terms = new int[400]; // allocate an array of 400 ints
for(int runs = 0; runs < terms.Length; runs++) // Use Length property rather than the 400 magic number again
{
terms[runs] = value;
}
int ArraySize = 400;
int[] terms = new int[ArraySize];
for(int runs = 0; runs < ArraySize; runs++)
{
terms[runs] = runs;
}
That would be how I'd code it.
C# arrays are fixed length and always indexed. Go with Motti's solution:
int [] terms = new int[400];
for(int runs = 0; runs < 400; runs++)
{
terms[runs] = value;
}
Note that this array is a dense array, a contiguous block of 400 bytes where you can drop things. If you want a dynamically sized array, use a List<int>.
List<int> terms = new List<int>();
for(int runs = 0; runs < 400; runs ++)
{
terms.Add(runs);
}
Neither int[] nor List<int> is an associative array -- that would be a Dictionary<> in C#. Both arrays and lists are dense.
You can't just add an element to an array easily. You can set the element at a given position as fallen888 outlined, but I recommend to use a List<int> or a Collection<int> instead, and use ToArray() if you need it converted into an array.
If you really need an array the following is probly the simplest:
using System.Collections.Generic;
// Create a List, and it can only contain integers.
List<int> list = new List<int>();
for (int i = 0; i < 400; i++)
{
list.Add(i);
}
int [] terms = list.ToArray();
one approach is to fill an array via LINQ
if you want to fill an array with one element
you can simply write
string[] arrayToBeFilled;
arrayToBeFilled= arrayToBeFilled.Append("str").ToArray();
furthermore, If you want to fill an array with multiple elements you can use the
previous code in a loop
//the array you want to fill values in
string[] arrayToBeFilled;
//list of values that you want to fill inside an array
List<string> listToFill = new List<string> { "a1", "a2", "a3" };
//looping through list to start filling the array
foreach (string str in listToFill){
// here are the LINQ extensions
arrayToBeFilled= arrayToBeFilled.Append(str).ToArray();
}
Array Push Example
public void ArrayPush<T>(ref T[] table, object value)
{
Array.Resize(ref table, table.Length + 1); // Resizing the array for the cloned length (+-) (+1)
table.SetValue(value, table.Length - 1); // Setting the value for the new element
}
int[] terms = new int[10]; //create 10 empty index in array terms
//fill value = 400 for every index (run) in the array
//terms.Length is the total length of the array, it is equal to 10 in this case
for (int run = 0; run < terms.Length; run++)
{
terms[run] = 400;
}
//print value from each of the index
for (int run = 0; run < terms.Length; run++)
{
Console.WriteLine("Value in index {0}:\t{1}",run, terms[run]);
}
Console.ReadLine();
/*Output:
Value in index 0: 400
Value in index 1: 400
Value in index 2: 400
Value in index 3: 400
Value in index 4: 400
Value in index 5: 400
Value in index 6: 400
Value in index 7: 400
Value in index 8: 400
Value in index 9: 400
*/
If you don't know the size of the Array or already have an existing array that you are adding to. You can go about this in two ways. The first is using a generic List<T>:
To do this you will want convert the array to a var termsList = terms.ToList(); and use the Add method. Then when done use the var terms = termsList.ToArray(); method to convert back to an array.
var terms = default(int[]);
var termsList = terms == null ? new List<int>() : terms.ToList();
for(var i = 0; i < 400; i++)
termsList.Add(i);
terms = termsList.ToArray();
The second way is resizing the current array:
var terms = default(int[]);
for(var i = 0; i < 400; i++)
{
if(terms == null)
terms = new int[1];
else
Array.Resize<int>(ref terms, terms.Length + 1);
terms[terms.Length - 1] = i;
}
If you are using .NET 3.5 Array.Add(...);
Both of these will allow you to do it dynamically. If you will be adding lots of items then just use a List<T>. If it's just a couple of items then it will have better performance resizing the array. This is because you take more of a hit for creating the List<T> object.
Times in ticks:
3 items
Array Resize Time: 6
List Add Time: 16
400 items
Array Resize Time: 305
List Add Time: 20
I will add this for a another variant. I prefer this type of functional coding lines more.
Enumerable.Range(0, 400).Select(x => x).ToArray();
You can't do this directly. However, you can use Linq to do this:
List<int> termsLst=new List<int>();
for (int runs = 0; runs < 400; runs++)
{
termsLst.Add(runs);
}
int[] terms = termsLst.ToArray();
If the array terms wasn't empty in the beginning, you can convert it to List first then do your stuf. Like:
List<int> termsLst = terms.ToList();
for (int runs = 0; runs < 400; runs++)
{
termsLst.Add(runs);
}
terms = termsLst.ToArray();
Note: don't miss adding 'using System.Linq;' at the begaining of the file.
This seems like a lot less trouble to me:
var usageList = usageArray.ToList();
usageList.Add("newstuff");
usageArray = usageList.ToArray();
Just a different approach:
int runs = 0;
bool batting = true;
string scorecard;
while (batting = runs < 400)
scorecard += "!" + runs++;
return scorecard.Split("!");
int[] terms = new int[400];
for(int runs = 0; runs < 400; runs++)
{
terms[runs] = value;
}
static void Main(string[] args)
{
int[] arrayname = new int[5];/*arrayname is an array of 5 integer [5] mean in array [0],[1],[2],[3],[4],[5] because array starts with zero*/
int i, j;
/*initialize elements of array arrayname*/
for (i = 0; i < 5; i++)
{
arrayname[i] = i + 100;
}
/*output each array element value*/
for (j = 0; j < 5; j++)
{
Console.WriteLine("Element and output value [{0}]={1}",j,arrayname[j]);
}
Console.ReadKey();/*Obtains the next character or function key pressed by the user.
The pressed key is displayed in the console window.*/
}
/*arrayname is an array of 5 integer*/
int[] arrayname = new int[5];
int i, j;
/*initialize elements of array arrayname*/
for (i = 0; i < 5; i++)
{
arrayname[i] = i + 100;
}
To add the list values to string array using C# without using ToArray() method
List<string> list = new List<string>();
list.Add("one");
list.Add("two");
list.Add("three");
list.Add("four");
list.Add("five");
string[] values = new string[list.Count];//assigning the count for array
for(int i=0;i<list.Count;i++)
{
values[i] = list[i].ToString();
}
Output of the value array contains:
one
two
three
four
five
You can do this is with a list. here is how
List<string> info = new List<string>();
info.Add("finally worked");
and if you need to return this array do
return info.ToArray();
Here is one way how to deal with adding new numbers and strings to Array:
int[] ids = new int[10];
ids[0] = 1;
string[] names = new string[10];
do
{
for (int i = 0; i < names.Length; i++)
{
Console.WriteLine("Enter Name");
names[i] = Convert.ToString(Console.ReadLine());
Console.WriteLine($"The Name is: {names[i]}");
Console.WriteLine($"the index of name is: {i}");
Console.WriteLine("Enter ID");
ids[i] = Convert.ToInt32(Console.ReadLine());
Console.WriteLine($"The number is: {ids[i]}");
Console.WriteLine($"the index is: {i}");
}
} while (names.Length <= 10);

Categories

Resources