user2699298
user2699298

Reputation: 1476

Changing one element of string in hashtable in C#

I have to write a program which use hashtable and the keys/values are input by the user. In the program, I have to output all the keys, however if any key starts with small 'a', I have to make it start with a big 'A'. I have a problem in the last step.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Hashtable hashtable = new Hashtable();
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("Vnesete kluc");
                string kluc = Console.ReadLine();
                Console.WriteLine("Vnesete podatok");
                string podatok = Console.ReadLine();
                hashtable.Add(kluc, podatok);
            }

            foreach (string klucevi in hashtable.Keys)
            {
                if (klucevi[0] == 'a')
                {
                    klucevi[0] = 'A';
                }
                Console.WriteLine(klucevi);
            }

        }
    }
}

I'm getting an error on the line where I'm converting the first element of the string if it's 'a' to 'A'.

Upvotes: 0

Views: 264

Answers (2)

Kris Vandermotten
Kris Vandermotten

Reputation: 10201

Your problem has nothing to do with hash tables. You have a compilation error, because in .NET strings are immutable.

Secondly, and this is unrelated, a foreach loop variable cannot be assigned to.

So, instead of

foreach (string klucevi in *whatever*)
{
    if (klucevi[0] == 'a')
    {
        klucevi[0] = 'A';
    }
    Console.WriteLine(klucevi);
}

use

foreach (string klucevi in *whatever*)
{
    var temp = klucevi;

    if (temp[0] == 'a')
    {
        StringBuilder sb = new StringBuilder(temp);

        sb[0] = 'A';

        temp = sb.ToString();
    }
    Console.WriteLine(temp);
}

don't forget to include a using System.Text; declaration.

UPDATE: The aswer above shows you a generic way to modify a string in .NET, not just to replace one character.

Furthermore, some people have raised concerns about the efficiency of the approach. They are mistaken. More information in this execelent article on Strings Undocumented.

UPDATE 2: I like being challenged. While it is totally irrelevant for the question at hand, a discussion has emmerged about the efiiciency of using a StringBuilder, compared to using "A" + temp.Substring(1). Because I like facts, and I assume some readers would agree, I ran a little benchmark.

I ran the tests on a 64 bit Windows 7 box with .NET 4.5 as both a 32 and a 64 bit process. It turns out that the StringBuilder approach is always faster than the alternative, by about 20%. Memory usage is approximately the same. YMMV.

For those who care to repeat the test, here's the source code:

using System;
using System.Diagnostics;
using System.Text;

static class Program
{
    static void Main(string[] args)
    {
        for (int length = 50; length <= 51200; length = length * 2)
        {
            string input = new string(' ', length);

            // warm up
            PerformTest(input, 1);

            // actual test
            PerformTest(input, 100000);
        }
    }

    static void PerformTest(string input, int iterations)
    {
        GC.Collect();
        GC.WaitForFullGCComplete();

        int gcRuns = GC.CollectionCount(0);

        Stopwatch sw = Stopwatch.StartNew();

        for (int i = iterations; i > 0; i--)
        {
            StringBuilder sb = new StringBuilder(input);

            sb[0] = 'A';

            input = sb.ToString();
        }

        long ticksWithStringBuilder = sw.ElapsedTicks;
        int gcRunsWithStringBuilder = GC.CollectionCount(0) - gcRuns;

        GC.Collect();
        GC.WaitForFullGCComplete();

        gcRuns = GC.CollectionCount(0);

        sw = Stopwatch.StartNew();

        for (int i = iterations; i > 0; i--)
        {
            input = "A" + input.Substring(1);
        }

        long ticksWithConcatSubstring = sw.ElapsedTicks;
        int gcRunsWithConcatSubstring = GC.CollectionCount(0) - gcRuns;

        if (iterations > 1)
        {
            Console.WriteLine(
                "String length: {0, 5} With StringBuilder {1, 9} ticks {2, 5} GC runs, alternative {3, 9} ticks {4, 5} GC Runs, speed ratio {5:0.00}", 
                input.Length, 
                ticksWithStringBuilder, gcRunsWithStringBuilder, 
                ticksWithConcatSubstring, gcRunsWithConcatSubstring, 
                ((double)ticksWithStringBuilder) / ((double)ticksWithConcatSubstring));
        }
    }
}

Upvotes: 1

Shadow Wizard
Shadow Wizard

Reputation: 66389

You can't dynamically change keys. Most simple approach is check the key before you add to the collection:

for (int i = 0; i < 10; i++)
{
    Console.WriteLine("Vnesete kluc");
    string kluc = Console.ReadLine();
    if (kluc.StartsWith("a"))
        kluc = "A" + kluc.Substring(1);
    Console.WriteLine("Vnesete podatok");
    string podatok = Console.ReadLine();
    hashtable.Add(kluc, podatok);
}

Upvotes: 3

Related Questions