ImNotReallyThatCSharp
ImNotReallyThatCSharp

Reputation: 21

Using two parameters (string, int) to define a max number of specific characters in string output

I am not very experienced with C# and have got a task to develop a simple program that can take two parameters; one string and one integer. The string is to be returned, and the integer is supposed to define the max number of each specific character that is returned in the string, i.e.:

input: "aaabbbbc", "2" output: aabbc

input: "acc", "1" output: ac

I've tried looking at different collections like IEnumerator to help make the code easier to write, but as I'm not very experienced I can't sort out how to utilize them.

This is the code that I've written so far:

public static string TwoParameters()
{
    Console.Write("Write some characters (i.e. 'cccaabbb'): ");
    string myString = Console.ReadLine();
    return myString;
    Console.Write("Write a number - ");
    int max = Convert.ToInt32(Console.Read());
}

public static void Counter(string myString, int max)
{
    string myString = TwoParameters(myString);
    foreach (char a in myString)
    {
        var occurences = myString.Count(x => x == 'a');
        if (occurences > max)
            max = occurences;
    }
}

Errors I get when running:

CS0136: Local or parameter 'myString' cannot be declared in scope because of enclosing local scope.

CS1501: No overload for method 'TwoParameters' takes 1 arg.

CS1061: 'string' does not contain a definition for count.

CS0165: Use of unassigned local variable 'myString'.

CS7036: There is no argument given that corresponds to the required formal parameter 'myString' of 'Program.Counter(string, int)'

Any kind of pointers to what I'm doing wrong, suggestions to how I can improve my code and/or finish it up for the program to make the output will be hugely appreciated.

Upvotes: 1

Views: 287

Answers (3)

Patrick Artner
Patrick Artner

Reputation: 51643

Pointers to what you are doing wrong:

  • you reset your given max to the counts in your string
  • you only handle "a"
  • your TwoParameters function has unreachable code
  • you try to declare a variable name again already providing it to the function as parameter
  • you do not build a string to output

Using Linq is probably somewhat overkill for your level of knowledge. This is a simpler Version of Oliver's answer - respecting order of letters as well:

public static void Main()
{
    var input = "abbaaabcbcccd";
    var max = 2;

    // stores count of characters that we added already
    var occurences = new Dictionary<char,int>();

    var sb = new StringBuilder();
    foreach (var c in input)
    {
        // add with a count of 0 if not yet encountered
        if (!occurences.ContainsKey(c))
        {
            // if you want only "consecutive" letters not repeated over max:
            // uncomment the following line that resets your dict - you might
            // want to use a single integer and character instead in that case
            // occurences.Clear(); // better use a single int-counter instead
            occurences[c] = 0;
        }


        // add character if less then max occurences
        if (occurences[c] <  max)
        {
            sb.Append(c);           
            occurences[c]+=1;
        }
    }

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

Output:

abbaccd

Upvotes: 0

Oliver
Oliver

Reputation: 45101

This function would also respect the order within the string, so aabcabc, 2 would result into aabcbc:

static string ReturnMaxOccurences(string source, int count)
{
    return source.Aggregate(new Accumulator(), (acc, c) =>
    {
        acc.Histogram.TryGetValue(c, out int charCount);

        if (charCount < count)
            acc.Result.Append(c);

        acc.Histogram[c] = ++charCount;

        return acc;
    }, acc => acc.Result.ToString());
}

But you need also this little helper class:

public class Accumulator
{
    public Dictionary<char, int> Histogram { get; } = new Dictionary<char, int>();
    public StringBuilder Result { get; } = new StringBuilder();

}

This method iterates the whole string and save within a histogram the occurences of each character. If the value is lower than the desired max value it will be added to the result string, otherwise it simply steps over the character and continues with the next.

Upvotes: 1

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131374

A string can be treated as an IEnumerable<char>. You can use LINQ to first group the characters then take only 2 from each group, eg :

var input="aaabbbbc";
var max=2;
var chars=input.GroupBy(c=>c)
               .SelectMany(g=>g.Take(2))
               .ToArray();
var result=new String(chars);

This produces

aabbc

This query groups the characters together with GroupBy and then takes only max from each group with Take. SelectMany flattens all the IEnumerable<char> returned from Take into a single IEnumerable<char> that can be used to create a string

Upvotes: 3

Related Questions