1

I am experienced in C/C++ but pretty much a newbie in C#. My question is pretty simple. Say we have a hash table with integer keys and values and we want to increment all the values in the hash table by 1. We prefer to accomplish this with O(1) extra memory.

Below is one solution, which, to my opinion, is somehow ugly. Is there other way to make it looks more decent?

        Dictionary<int, int> dict = new Dictionary<int, int>();
        for (int i = 0; i < dict.Count; ++i)
        {
            dict[dict.Keys.ElementAt(i)]++;
        } 

PS: I heard that foreach is read-only in C#. But, is there any way like for(auto it& : dict) it.second++ in C++ that I can use to still accomplish this task in C#?

Chen Chen
  • 358
  • 4
  • 15
  • Note that the code that you have, in addition to being O(n^2) in time, doesn't actually work. The keys are not guaranteed to stay in the same order, so what you're *actually* doing is incrementing N random keys, not necessarily incrementing each key exactly once. – Servy Nov 17 '16 at 15:14
  • 1
    `dict.Keys.ToList().ForEach(x=>dict[x]++);` – Damith Nov 17 '16 at 15:19
  • @Servy I got the idea you said about keys not being in the same order. However, I have done several tests and didn't see the problem. Can you show me a test that it break? I am not sure how Keys is actually ordered. I hope it is by address. Regarding O(n^2) in time, is that due to `ElementAt` being O(n) operation for non `IList` – Chen Chen Nov 17 '16 at 15:24
  • @Servy Could you please paste the link of that documentation? Still not getting used to MSDN's documentation style. – Chen Chen Nov 17 '16 at 15:34
  • @ChenChen https://www.google.com/search?q=C%23%20Dictionary – Servy Nov 17 '16 at 15:35
  • you can cheat a bit for O(1) time and space by using extension method that returns the value + 1 or make custom Dictionary http://stackoverflow.com/questions/963068/want-to-create-a-custom-class-of-type-dictionaryint-t to confuse anyone else working on the code :] – Slai Nov 17 '16 at 15:38
  • `using System.Runtime.CompilerServices; ... Dictionary> dict = new Dictionary>(); foreach(StrongBox v in dict.Values) { ++v.Value; }` – user4003407 Nov 17 '16 at 15:56

4 Answers4

4

Dictionary<,> itself doesn't provide a good way of doing this - because updating the value associated with a key counts as a change that invalidates any iterator. ConcurrentDictionary<,> does allow this though, and even has an AddOrUpdate method which will help you:

using System;
using System.Linq;
using System.Collections.Concurrent;

class Test
{
    static void Main()
    {
        var dict = new ConcurrentDictionary<int, int>
        {
            [10] = 15,
            [20] = 5,
            [30] = 10
        };
        foreach (var key in dict.Keys)
        {
            dict.AddOrUpdate(key, 0, (k, v) => v + 1);
        }
        Console.WriteLine(string.Join("\r\n", dict.Select(kp => $"{kp.Key}={kp.Value}")));
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • This is still `O(N)` Jon isn't it ? – Jim Nov 17 '16 at 15:20
  • 1
    @Jim In time, yes. It's O(1) in memory (which is what the question asks for). It's impossible for it to be any less than O(n) in time; it's updating n items. – Servy Nov 17 '16 at 15:26
  • @Jon Skeet Is ConcurrentDictionary more expensive than Dictionary? – Chen Chen Nov 17 '16 at 16:03
  • @ChenChen: I suspect so due to its features, but I don't know how *much* more expensive or where. Best to benchmark your specific usage. – Jon Skeet Nov 17 '16 at 16:16
2

You could use foreach in this instance, but we'd need to do it in a way that doesn't use the dictionary, since updating it changes the internal iterator (which is how foreach keeps up with where it is while iterating, check out this SO for more details: https://stackoverflow.com/a/398996/3874503) In this implementation we'll iterate over a list of the keys and then update each key's value as we iterate.

 Dictionary<int, int> dict = new Dictionary<int, int>();
 foreach (var i in dict.Keys.ToList())
 {
     dict[i]++;
 }

Update as Servy pointed out, I failed to mention that this solution is not O(1), but is instead O(N).

Community
  • 1
  • 1
davidallyoung
  • 1,302
  • 11
  • 15
  • The question specifically says that he's looking to do this in O(1) memory. This is O(N) in memory. – Servy Nov 17 '16 at 15:26
  • I realize this isn't O(1), but I'm not sure of an impl that would actually yield that in this given instance. This is better in terms of the original solution as it is not looking up the key back in the dictionary based on index and will reference all keys at a specific point in time. I should have made note of that in the answer though, thanks! – davidallyoung Nov 17 '16 at 15:33
-2

Why is this disallowed? Trying to assigning to it probably wouldn't do what you want - it wouldn't modify the contents of the original collection. This is because the variable x is not a reference to the elements in the list - it is a copy.

Because KeyValuePair have Read-Only Propreties for the Key and the Value to do this in a foreach you Would need a second Dictionnary to add these items and dispose the original one

        Dictionary<int, int> dict = new Dictionary<int, int>();
        dict.Add(0, 1);
        dict.Add(1, 2);
        dict.Add(2, 3);

        Dictionary<int, int> dictOutput = new Dictionary<int, int>();
        foreach (KeyValuePair<int,int> item in dict)
        {
            dictOutput.Add(item.Key, item.Value + 1);
        }
        dict.Dispose();
Danys Chalifour
  • 322
  • 1
  • 5
  • 12
-2

You don't need the dict.Keys.ElementAt(i) index in your loop. You can treat it like an array. Dictionarys are the C# equivalent to a Map in C++. All you need to do to increment your values is

for (int i = 0; i < dict.Count; i ++)
    dict[i]++;

To add to that, if you're using a Dictionary<int, int>, you can simply use

int[] dict = new int[count];

since your key is the index anyway; But I would really just recommend using a List<int>. This way you can adjust the size dynamically without having to do all the extra work.

This way, you can create your List<int> like so:

List<int> lstDict = new List<int>;
int value; /*assign to whatever value you need to insert*/
int count; /*however many elements you need*/

for (int i = 0; i < count; i ++) 
    lstDict.Add(value);

//
//  Whatever else you need to do
//

for (int i = 0; i < count; i ++)
    lstDict[i]++;

There's tons of way to do what you're trying to accomplish. You can use any of these options to achieve the same thing. If you want to stick with Dictionarys, just use that first snippet I put up there. I just, personally, wouldn't use them unless I needed to use two different data types, like a Dictionary<string, int>.

Uchiha Itachi
  • 1,251
  • 1
  • 16
  • 42
  • This will compile, but not work, given that the keys of the dictionary, while `int` values, are not necessarily all of the integers between `0` and `Count-1`. You'll just get key not found errors doing this. – Servy Nov 17 '16 at 15:30
  • It should also be noted that a C# `Dictionary` is more like the C++ `unordered_map`, not the `map`. The C++ `map` structure has O(log n) lookup time because it also sorts entries by their key value. Its behavior is more like the C# `SortedDictionary`. – RogerN Nov 17 '16 at 15:35
  • Thanks @RogerN. I didn't know that. – Uchiha Itachi Nov 17 '16 at 15:37