Not able to change the value of a Dictionary's key - c#

I wanted to count the number of repeated characters in a text file..
I wrote this code
foreach(char c in File.ReadAllText(path))
{
if(dic.ContainsKey(c))
{
int i=dic[c];
dic[c]=i++;
}
else
{
dic.Add(c,1);
}
}
It's adding all the unique words but it's showing value for all keys as 1 even if there are repeated characters!

I think you want:
dic[c] = i + 1;
Or possibly, although IMHO this just adds complexity since you don't use i after:
dic[c] = ++i;
Explanation:
i++ is a post-increment operation. This means it assigns the current value of i to dic[c] and then increments i. So in summary, you're always reading in i=1, putting the i=1 back into the dictionary, then incrementing i to 2 before sending it to the void.
Addendum:
You don't really need to go through a temporary variable at all. You can simply read and assign the value back in one operation with dic[c] += 1; or even increment it with dic[c]++;.

i++ will add one to the value of i but return the value of i before the increment. You don't want to do that. You just want to return the value of i incremented by one. To do this, just write:
dic[c] = i+1;
On a side note, you could do the whole thing using LINQ instead:
var dic = File.ReadAllText(path).GroupBy(c => c)
.ToDictionary(group => group.Key, group => group.Count());

You want dic[c] = i + 1; or dic[c] += 1 or dic[c]++. In your code the post increment operator is incrementing i after assignment takes place so it has no effect on the value of dic[c].

dic[c]=i++; translates to
dic[c] = i;
i = i++;
i isn't a reference value and thus the value of i placed inside the dictionary will not change after you increment it outside the dictionary.
Use dic[c]++; instead.

This is because i gets incremented after being affected to dict[c]. Try this instead :
if(dic.ContainsKey(c))
{
dic[c] += 1;
}

Dictionary<char, int> LetterCount(string textPath)
{
var dic = new Dictionary<char, int>();
foreach (char c in System.IO.File.ReadAllText(textPath))
{
if (dic.ContainsKey(c))
dic[c]++;
else
dic.Add(c, 1);
}
return dic;
}
Then use like this:
var letters = LetterCount(#"C:\Text.txt");
// letters will contain the result

Related

Why does post-increment fail in .Aggregate(...) but pre-increment succeeds?

I was fiddling with one of my project euler answers to try and make it a bit shorter/cleaner/succinct.
I came up with this:
Sequences.FibonacciBig() // infinite fib sequence of type BigInteger
.TakeWhile(f => f.ToString().Length < 1000)
.Aggregate(1, (i, _) => i++);
My test failed as the actual was 1, which seemed odd. I first thought that the lazy enumerable wasn't being evaluated or something like that. I replaced with i += 1 and it worked, test passed. Then I replaced with ++i and it still worked.
I'm confused as to why the statement seems to not be evaluated at all when using the post-increment operator. At worst, I expected some kind of off-by-one error, but not have the aggregate function effectively do nothing.
Can someone explain?
Look at following code:
private int value = 0;
public int GetValue()
{
return value++;
}
would you expect it to return 1 when called for the first time? It doesn't. It returns current value of value and than increments it. The same happens to your lambda expression.
.Aggregate(1, (i, _) => i++);
It returns current value of i, and than increments it (which is pointless at that point as you're not holding reference to it anywhere else).
pre-increment and += work because they increment the value before returning it.
i++ increments i as a side effect, but the value of the i++ expression will be the value before i was incremented, unlike with ++i where the value will be the value of i after the increment.
In other words:
var i = 3;
var a = i++;
Console.WriteLine("a = {0}, i = {1}", a, i); // a = 3, i = 4
Compare this to:
var i = 3;
var a = ++i;
Console.WriteLine("a = {0}, i = {1}", a, i); // a = 4, i = 4
But anyway this doesn't really matter here since you shouldn't be incrementing i anyway in your code. You could just write:
.Aggregate(1, (i, _) => i + 1)
because i is a parameter, so it's just a local variable that you don't reuse later.
But, actually, why don't you just write .Count() + 1 instead? Because that's exactly what your Aggregate call does...

C# Loop Through An Array

I am completely new to C#. I am trying to loop through a short array, where the string elements in the array are placed at the end of a website search. The code:
int n = 1;
string[] s = {"firstitem","seconditem","thirditem"}
int x = s.Max(); // note, from my research this should return the maximum value in the array, but this is the first error
x = x + 1
while (n < x)
{
System.Diagnostics.Process.Start("www.website.com/" + b[0]);
b[]++; // this also generates an error "identifier expected"
}
My coding, logic or both are wrong. Based on what I've read, I should be able to get the maximum value in an array (as an int), then add to the arrays value while a WHILE loop adds each value in the array at the end of the website (and then stops). Note, that on the first error, I tried coding it differently, like the below:
int x = Convert.ToInt32(s.Max);
However, it generates an overload error. If I'm reading things correctly, MAX should find the maximum value in a sequence.
foreach(var str in s)
{
System.Diagnostics.Process.Start("www.website.com/" + str);
}
You have a collection of strings. The largest string is still a string, not an int. Since s.Max() is a string, and you're assinging it to a variable of type int: int x = s.Max(); the compiler (correctly) informs you that the types do not match. You need to convert that string to an int. Since, looking at your data, they aren't integers, and I see no sensible way of converting those strings into integers, I see no reasonable solution. What integer should "firstitem" be?
If you just want to execute some code for each item in the array then use one of these patterns:
foreach(string item in s)
{
System.Diagnostics.Process.Start("www.website.com/" + item);
}
or
for(int i = 0; i < s.Length; i++)
{
System.Diagnostics.Process.Start("www.website.com/" + s[i]);
}
You're missing a couple of semi-colons
x should presumably be the Length of the array, not the largest value in it
You need to increment x inside of your loop - at the end of it, not outside of it
You should actually be incrementing n, not x
n should be starting at 0, not at 1
Inside the loop you're using b[0] where you probably want to use b[n]
I'm no C++ guru, but I have no idea what b[]++ might mean
As other answers have mentioned, you may want to use a for or foreach instead of a while.
Make an effort to go through some introductory tutorials. Trial and error can be a useful tool, but there's no need to fall back on that when learning the very basics
Following is an image to point out what are the errors of your code:
After the correction, it would be:
int n=1;
string[] s= { "firstitem", "seconditem", "thirditem" };
int x=s.Length;
while(n<x) {
System.Diagnostics.Process.Start("www.website.com/"+s[n]);
n++; // or ++n
}
And we can make it more semantic:
var items=new[] { "firstitem", "seconditem", "thirditem" };
for(int index=1, count=items.Length; index<count; ++index)
Process.Start("www.website.com/"+items[index]);
If the starting order doesn't matter, and we can use foreach instead, and we can use Linq to make the code even simpler:
var list=(new[] { "firstitem", "seconditem", "thirditem" }).ToList();
list.ForEach(item => Process.Start("www.website.com/"+item));
and we might quite often write in another form:
foreach(var item in new[] { "firstitem", "seconditem", "thirditem" })
Process.Start("www.website.com/"+item);
from the sample
var processList = (new string[]{"firstitem","seconditem","thirditem"})
.Select(s => Process.Start("www.website.com/" + s))
.ToList();
and here is a test version that outputs to console
(new string[] { "firstitem", "seconditem", "thirditem" })
.Select(s => { Console.WriteLine(#"www.website.com/" + s); return s; })
.ToList();
note: Select requires a return type and the .ToList() enforces evaluation.

C# - Compile variable as name/code

Since I couldn't explain very good in my last question and I didn't get an answer that could satisfy me, I decided to open a new one. Straight to the point, what I'm basically trying to do is compiling a variable (the value it holds) as a part of code (and specificly in my case referencing another variable)
Say I have:
int var_1, var_2, var_3 ... var_10;
for (int i = 1; i <= 10; i++)
{
var_%i%=20; //if i is 1, then var_1's value will be set to 20, if i is 2, then var_2's value will be set to 20. So basically this loop sets the value of var_1 .. var_10 to 20
}
I can explain in an even simpler way, if in any case the latter is not clear.
int var_5;
int SomeOtherVar = 5;
var_%SomeOtherVar% = 10; // so var_5 (where 5 is the value of SomeOtherVar) is set to 10
Is this doable and if it is, what's the approach?
No you can't do that, why dont you use an array?
int[] array = new int[3];
for (int i = 0; i < array.Length; ++i)
{
array[i] = 20;
}
Hope it helps.
It's not doable. Use an array instead. The type is int[] but I suggest you go read a tutorial about arrays to understand how to create and use them.
I can't think of a situation where you'd need to do this. If you wish to store values against a consecutive list of numbers, use an array. Otherwise you could use a Dictionary. For example to store "var1" = 20, "var2" = 20 as in your question, you could do this:
Dictionary<string, int> dict = new Dictionary<string, int>();
for (int i = 1; i <= 10; i++)
{
dict.Add("var" + i.ToString(), 20);
}
Some examples of usage:
dict["var1"] // 20
dict["var2"] // 20
dict.ContainsKey("var3") // true
dict.ContainsKey("var99") // false
Note: I'm concatenating the string "var" with the int from the loop just to demonstrate that you can use arbitary strings as keys to store / lookup the values. In this case it's a bit of a strange thing to do, and you'd probably be best sticking to a normal array, but my example shows how a dictionary could work with more complex keys.
If you want to bypass static type checking and you feel like creating some coding horror, you can use ExpandoObject combined with the dynamic keyword. Won't let you set variables in your scope, but will technically let you declare your own ones. Note that in my example I cast it to IDictionary<string, object> because I create its members' names at runtime from a string. What the following method does is create twenty members and assign their values from 0 to 19.
static dynamic SetVariables(IEnumerable<int> range)
{
const string variableName = "var_";
var expandoDictionary = new ExpandoObject() as IDictionary<string, object>;
foreach (var i in range)
expandoDictionary[variableName + i] = i;
return expandoDictionary;
}
You can then access the members easily this way:
var container = SetVariables(Enumerable.Range(0, 20));
var value13 = container.var_13;
Please note that I do not recommend this usage, and I'd stay away from dynamic as much as I can. However, for the sake of problem solving, this can be seen as one unsafe but partial solution.

Incrementing a numerical value in a dictionary

I'm using the code below to either increment or insert a value in a dictionary. If the key I'm incrementing doesn't exist I'd like to set its value to 1.
public void IncrementCount(Dictionary<int, int> someDictionary, int id)
{
int currentCount;
if (someDictionary.TryGetValue(id, out currentCount))
{
someDictionary[id] = currentCount + 1;
}
else
{
someDictionary[id] = 1;
}
}
Is this an appropriate way of doing so?
Your code is fine. But here's a way to simplify in a way that doesn't require branching in your code:
int currentCount;
// currentCount will be zero if the key id doesn't exist..
someDictionary.TryGetValue(id, out currentCount);
someDictionary[id] = currentCount + 1;
This relies on the fact that the TryGetValue method sets value to the default value of its type if the key doesn't exist. In your case, the default value of int is 0, which is exactly what you want.
UPD. Starting from C# 7.0 this snippet can be shortened using out variables:
// declare variable right where it's passed
someDictionary.TryGetValue(id, out var currentCount);
someDictionary[id] = currentCount + 1;
As it turns out it made sense to use the ConcurrentDictionary which has the handy upsert method: AddOrUpdate.
So, I just used:
someDictionary.AddOrUpdate(id, 1, (id, count) => count + 1);
Here's a nice extension method:
public static void Increment<T>(this Dictionary<T, int> dictionary, T key)
{
int count;
dictionary.TryGetValue(key, out count);
dictionary[key] = count + 1;
}
Usage:
var dictionary = new Dictionary<string, int>();
dictionary.Increment("hello");
dictionary.Increment("hello");
dictionary.Increment("world");
Assert.AreEqual(2, dictionary["hello"]);
Assert.AreEqual(1, dictionary["world"]);
It is readable and the intent is clear. I think this is fine. No need to invent some smarter or shorter code; if it doesn't keep the intent just as clear as your initial version :-)
That being said, here is a slightly shorter version:
public void IncrementCount(Dictionary<int, int> someDictionary, int id)
{
if (!someDictionary.ContainsKey(id))
someDictionary[id] = 0;
someDictionary[id]++;
}
If you have concurrent access to the dictionary, remember to synchronize access to it.
Just some measurements on .NET 4 for integer keys.
It's not quite an answer to your question, but for the sake of completeness I've measured the behavior of various classes useful for incrementing integers based on integer keys: simple Array, Dictionary (#Ani's approach), Dictionary (simple approach), SortedDictionary (#Ani's approach) and ConcurrentDictionary.TryAddOrUpdate.
Here is the results, adjusted by 2.5 ns for wrapping with classes instead of direct usage:
Array 2.5 ns/inc
Dictionary (#Ani) 27.5 ns/inc
Dictionary (Simple) 37.4 ns/inc
SortedDictionary 192.5 ns/inc
ConcurrentDictionary 79.7 ns/inc
And that's the code.
Note that ConcurrentDictionary.TryAddOrUpdate is three times slower than Dictionary's TryGetValue + indexer's setter. And the latter is ten times slower than Array.
So I would use an array if I know the range of keys is small and a combined approach otherwise.
Here is a handy unit test for you to play with concerning ConcurrentDictionary and how to keep the values threadsafe:
ConcurrentDictionary<string, int> TestDict = new ConcurrentDictionary<string,int>();
[TestMethod]
public void WorkingWithConcurrentDictionary()
{
//If Test doesn't exist in the dictionary it will be added with a value of 0
TestDict.AddOrUpdate("Test", 0, (OldKey, OldValue) => OldValue+1);
//This will increment the test key value by 1
TestDict.AddOrUpdate("Test", 0, (OldKey, OldValue) => OldValue+1);
Assert.IsTrue(TestDict["Test"] == 1);
//This will increment it again
TestDict.AddOrUpdate("Test", 0, (OldKey, OldValue) => OldValue+1);
Assert.IsTrue(TestDict["Test"] == 2);
//This is a handy way of getting a value from the dictionary in a thread safe manner
//It would set the Test key to 0 if it didn't already exist in the dictionary
Assert.IsTrue(TestDict.GetOrAdd("Test", 0) == 2);
//This will decriment the Test Key by one
TestDict.AddOrUpdate("Test", 0, (OldKey, OldValue) => OldValue-1);
Assert.IsTrue(TestDict["Test"] == 1);
}

How to get the size of arrays packed as value in a Dictionary<K,V> in C#?

I have a Dictionary<K,V> object already filled with data:
Dictionary<string, double[]> ltableData;
Every double[] has the same size (number of elements).
I do not know in advance neither any 'string' key nor the size of the 'double[]' values.
Now I have to make one cycle with a nested one as follows:
for(int i = 0; i< number_elements;i++)
{
foreach (var array in ltableData.Values)
{
ltempList.Add(array[i]);
//other instructions
}
}
How can I assign number_elements? I can of course use this solution:
var lrowCount;
foreach(var item in ltableData.Values)
{
lrowCount = item.Count();
}
that works but it is of course ugly both to see and execute. Is there any cleaner and more effective method to extract the count? Thanks
Francesco
var lrowCount = ltableData.Values.First().Length;
// or
var lrowCount = ltableData.First().Value.Length;
foreach (var array in ltableData.Values)
{
for (int i = 0; i < array.Length; i++)
{
//other instructions
}
}
or, even more readable,
foreach (var array in ltableData.Values)
{
foreach (var val in array)
{
//other instructions
}
}
You can take its length from any item:
int number_elements = ltableData.Values.Select(v => v.Length).FirstOrDefault();
Though I think if you have such structure then you should create a class for it which will have a Length on top level and which will also assert and guarantee that all inner items have the same length.
If you know that itableData does contain values you can do the following.
ltableData.Values.First().Count();
I'm not going to say this is much cleaner, but you could do
var number_elements = ltableData.Values.First().Count();
This is ONLY if you must need the length. Otherwise, a foreach loop would be just fine.
Now that you've clarified what you're doing as per the comments on your original post, it's clear that you actually want
using System.Linq;
var ltempList = ltableData.SelectMany(kvp => kvp.Value);
This will give an IEnumerable<double> containing all of the values from all different entries in the dictionary, aggregated together.
you can use this to get the total number of values
var number_elements = ltableData.Select(p => p.Value.Count()).Sum();

Categories

Resources