Allow only a set number of characters in a string - c#

I am working on a function that will evaluate a string and only allow x instances of each character.
For example, you can have 2 allowed character so
aaaabbbbbcddddd
would be evaluated to
aabbcdd
So far I have written this:
public static string removeDuplicateCharacters(String text, int allowedDuplicates)
{
string seen="";
foreach (char c in text){
if(!seen.Contains(c)){
seen = seen + c;
} else if(seen.Contains(c)){
// while the sting contains < allowedDuplicates
// add c
}
}
return seen;
}
I can't at the moment work out how create a while condition that is also going to count through my seen string for the number of current instances of the char currently being evaluated.

Easy solution with a Dictionary to keep track of the character counts:
public static string removeDuplicateCharacters(String text, int allowedDuplicates)
{
string seen="";
Dictionary<char, int> charCount = new Dictionary<char, int>();
foreach (char c in text)
{
if(!charCount.ContainsKey(c))
{
seen += c;
charCount.Add(c, 1);
}
else if(charCount[c] < allowedDuplicates)
{
charCount[c] += 1;
seen += c;
}
else
{
//Reached max, do nothing
}
}
return seen;
}
This is your base and you can make it as nice and fancy as you want from here.
E.g.: I would suggest a StringBuilder if the strings can get long as it less taxing on memory since you don't have to allocate permanently new Strings when doing += on them.
public static string removeDuplicateCharacters(String text, int allowedDuplicates)
{
StringBuilder seen = new StringBuilder();
Dictionary<char, int> charCount = new Dictionary<char, int>();
foreach (char c in text)
{
if(!charCount.ContainsKey(c))
{
seen.Append(c);
charCount.Add(c, 1);
}
else if(charCount[c] < allowedDuplicates)
{
charCount[c] += 1;
seen.Append(c);
}
else
{
//Reached max, do nothing
}
}
return seen.ToString();
}
Another thing would be if you want lower and uppercase to be treated the same.
Then I would change the test to upper or lower case, but if you want to keep the casing of the original character in the return string you could do the following.
public static string removeDuplicateCharacters(String text, int allowedDuplicates)
{
StringBuilder seen = new StringBuilder();
Dictionary<char, int> charCount = new Dictionary<char, int>();
foreach (char c in text)
{
char upperCase = c.ToUpper();
if(!charCount.ContainsKey(upperCase))
{
seen.Append(c);
charCount.Add(upperCase , 1);
}
else if(charCount[upperCase] < allowedDuplicates)
{
charCount[upperCase ] += 1;
seen.Append(c);
}
else
{
//Reached max, do nothing
}
}
return seen.ToString();
}
Just customize from here on.

Use a dictionary to keep track of character count
public static string removeDuplicateCharacters(string text, int allowedDuplicates)
{
var frequency = new Dictionary<char, int>();
StringBuilder output = new StringBuilder();
foreach (char c in text)
{
int count = 1;
if (frequency.ContainsKey(c))
count = ++frequency[c];
else
frequency.Add(c, count);
if (count <= allowedDuplicates)
output.Append(c);
}
return output.ToString();
}

Here we keep track of the seen characters and how many times we have seen them by using a Dictionary, and we assemble the output string with a StringBuilder:
public static string RemoveDuplicateCharacters(string text, int allowedDuplicates)
{
var seen = new Dictionary<char, int>();
var sb = new StringBuilder();
foreach (char c in text)
{
int count;
if (seen.TryGetValue(c, out count))
{
count++;
} else
{
count = 1;
}
seen[c] = count;
if (count <= allowedDuplicates)
sb.Append(c);
}
return sb.ToString();
}
Test:
Console.WriteLine(RemoveDuplicateCharacters("aaaabbbbbcddddda", 3));
Output:
aaabbbcddd

This is possibly a more appropriate approach for a beginner student. It doesn't require the use of arrays or dictionaries and sticks to simple algorithmic primitives. (I'm not objecting to their use, I just have a hunch that this is a student exercise and might even be one designed to motivate their introduction.)
public static string removeDuplicateCharacters(String text, int allowedDuplicates)
{
string seen= "";
int count = 0;
foreach (char c in text) {
count = 0;
foreach (char c2 in seen) {
if (c2 == c) count++;
}
if (count < allowedDuplicates) {
seen = seen + c;
}
}
return seen;
}

Related

Delete part of string value

I want to mix 2 string in 1 randomly using foreach but I don't know how I delete the part I used on the string for the foreach like:
string s = "idontknow";
string sNew = "";
foreach(char ss in s){
s = s + ss;
ss.Delete(s); //don't exist
}
Full code here i'm trying to do:
do
{
if (state == 0)
{
for (int i = 0; random.Next(1, 5) > variable.Length; i++)
{
foreach (char ch in variable)
{
fullString = fullString + ch;
}
}
state++;
}
else if (state == 1)
{
for (int i = 0; random.Next(1, 5) > numbers.Length; i++)
{
foreach (char n in numbers)
{
fullString = fullString + n;
}
}
state--;
}
} while (variable.Length != 0 && numbers.Length != 0);
I'm pretty confident, that in your first code snippet, you are creating an infinite loop, since you are appending the used char back to the string while removing it from the first position.
Regarding your specification to shuffle two stings together, this code sample might do the job:
public static string ShuffleStrings(string s1, string s2){
List<char> charPool = new();
foreach (char c in s1) {
charPool.Add(c);
}
foreach (char c in s2) {
charPool.Add(c);
}
Random rand = new();
char[] output = new char[charPool.Count];
for(int i = 0; i < output.Length; i++) {
int randomIndex = rand.Next(0, charPool.Count);
output[i] = charPool[randomIndex];
charPool.RemoveAt(randomIndex);
}
return new string(output);
}
In case you just want to shuffle one string into another string, just use an empty string as the first or second parameter.
Example:
string shuffled = ShuffleStrings("TEST", "string");
Console.WriteLine(shuffled);
// Output:
// EgsTtSnrTi
There are possibly other solutions, which are much shorter, but I think this code is pretty easy to read and understand.
Concerning the performance, the code above should works both for small stings and large strings.
Since strings are immutable, each modify-operation on any string, e.g. "te" + "st" or "test".Replace("t", ""), will allocate and create a new string in the memory, which is - in a large scale - pretty bad.
For that very reason, I initialized a char array, which will then be filled.
Alternatively, you can use:
using System.Text;
StringBuilder sb = new();
// append each randomly picked char
sb.Append(c);
// Create a string from appended chars
sb.ToString();
And if your question was just how to remove the first char of a string:
string myStr = "Test";
foreach (char c in myStr) {
// do with c whatever you want
myStr = myStr[1..]; // assign a substring exluding first char (start at index 1)
Console.WriteLine($"c = {c}; myStr = {myStr}");
}
// Output:
// c = T; myStr = est
// c = e; myStr = st
// c = s; myStr = t
// c = t; myStr =

function which takes a string input and removes all the characters which occur a certain number of times

I have a word "angoora" here 'a' and 'o' occurs 2 time if user input is 2 then output should be "ngr" function should remove a and o because it occur 2 times in a string. if user enter 3 then output should be "angoora" because no character occur 3 times.
I am doing this but I think its not a right way because its not leading me towards my goal, any help would be highly appreciated.
public static SortedDictionary<char, int> Count(string stringToCount)
{
SortedDictionary<char, int> characterCount = new SortedDictionary<char, int>();
foreach (var character in stringToCount)
{
int counter = 0;
characterCount.TryGetValue(character, out counter);
characterCount[character] = counter + 1;
}
return characterCount;
}
You can use LINQs GroupBy to find the number of times each character occurs. Then remove the ones that occur the number of times you want. Something like this
public static string RemoveCharactersThatOccurNumberOfTimes(string s, int numberOfOccurances)
{
var charactersToBeRemoved = s.GroupBy(c => c).Where(g => g.Count() == numberOfOccurances).Select(g => g.Key);
return String.Join("", s.Where(c => !charactersToBeRemoved.Contains(c)));
}
You Can use this Function
static string Fix(string item, int count)
{
var chars = item.ToList().GroupBy(g => g).Select(s => new { Ch = s.Key.ToString(), Count = s.Count() }).Where(w => w.Count < count).ToList();
var characters = string.Join("", item.ToList().Select(s => s.ToString()).Where(wi => chars.Any(a => a.Ch == wi)).ToList());
return characters;
}
Your characterCount SortedDictionary is empty.
Currently you are doing:
public static SortedDictionary<char, int> Count(string stringToCount)
{
// Create a new empty SortedDictionary
SortedDictionary<char, int> characterCount = new SortedDictionary<char, int>();
// Loop through each character in stringToCount and see if SortedDictionary contains a key equal to this character (it doesn't as dictionary is empty).
foreach (var character in stringToCount)
{
int counter = 0;
characterCount.TryGetValue(character, out counter);
characterCount[character] = counter +1;
}
return characterCount;
}
Surely you want something like this:
public static SortedDictionary<char, int> Count(string stringToCount)
{
// Create a new empty SortedDictionary (use var keyword if defining variables)
var characterCount = new SortedDictionary<char, int>();
// Loop through each character and add to dictionary
foreach (var character in stringToCount)
{
// If character already in SortedDictionary.
if (characterCount.TryGetValue(character, out int count))
{
// Increment count value.
characterCount[character] = count + 1;
// Above line can also be: ++characterCount[character];
}
// Else, character not already in dictionary.
else
{
// Add character in dictionary and set count to 1.
characterCount.Add(character, 1);
}
}
return characterCount;
}
public static string foobar(string given, int number)
{
string result = given;
foreach (char c in result.Distinct())
{
if (given.Count(ch => c == ch) >= number) result= result.Replace(c.ToString(),"");
}
return result;
}
Distinct() will give you only unique characters.
Then you Count() the occurance of each unique character and remove if it is greater or equal the given number.

C# counting characters

I am trying to complete a brain teaser which has a bug and I cant find it. Just wondering if anyone knows the answer. My goal is to return the character that appears most often.
public string solution(string S)
{
int[] occurrences = new int[26];
foreach (char ch in S)
{
occurrences[ch - 'a']++;
}
char best_char = 'a';
int best_res = 0;
for (int i = 1; i < 26; i++)
{
if (occurrences[i] >= best_res)
{
best_char = (char)('a' + i);
best_res = occurrences[i];
}
}
return best_char.ToString();
}
You have small mistake. Your index should start from 0, not 1
for (int i = 0; i < 26; i++)
{
if (occurrences[i] >= best_res)
{
best_char = (char)('a' + i);
best_res = occurrences[i];
}
}
Another and safer version is that
public string Solution(string text)
{
string strResponse = string.Empty;
if (!string.IsNullOrEmpty(text))
{
List<KeyValuePair<char, int>> occurance = text.GroupBy(ch => ch)
.Where(grp => char.IsLetter(grp.Key))
.Select(grp => new KeyValuePair<char, int>(grp.Key, grp.Count()))
.OrderByDescending(c => c.Value)
.ToList();
if (occurance.Any())
strResponse = occurance.First().Key.ToString();
}
return strResponse;
}
There could actually be more than one character with the maximum number of occurrences, so:
private static Char[] GetMostFrequentChars(String text)
{
Dictionary<Char,Int32> rank = new Dictionary<Char,Int32>();
foreach (Char c in text.Where(c => !char.IsWhiteSpace(c)))
{
if (rank.ContainsKey(c))
rank[c]++;
else
rank.Add(c, 1);
}
return rank.Where(r => r.Value == rank.Values.Max()).Select(x => x.Key).ToArray();
}
If you don't care about special characters (like spaces), you could do this with LINQ:
public static GetMostFrequentCharacter(string value)
{
return value
.GroupBy(o => o)
.OrderByDescending(o => o.Count())
.First()
.Key
.ToString()
}
There are at least 2 problems:
as #Adem Çatamak says, for loop should start at index 0
ch - 'a' will throw an exception if the string contains any other character than a-z lowercase,
public static string solution(string S)
{
var charDict = new Dictionary<char, int>();
foreach (char c in S.Where(c => !char.IsWhiteSpace(c)))
{
if(!charDict.TryGetValue(c, out int count))
{
charDict[c] = 1;
}
charDict[c]++;
}
return charDict.OrderByDescending(kvp => kvp.Value).First().Key.ToString();
}
Using a dictionary and LINQ is going to be better I think. Don't just copy this code and paste it into what ever homework or class this is for, use it to learn otherwise its a waste of my time and yours really

Determine if string has all unique characters

I'm working through an algorithm problem set which poses the following question:
"Determine if a string has all unique characters. Assume you can only use arrays".
I have a working solution, but I would like to see if there is anything better optimized in terms of time complexity. I do not want to use LINQ. Appreciate any help you can provide!
static void Main(string[] args)
{
FindDupes("crocodile");
}
static string FindDupes(string text)
{
if (text.Length == 0 || text.Length > 256)
{
Console.WriteLine("String is either empty or too long");
}
char[] str = new char[text.Length];
char[] output = new char[text.Length];
int strLength = 0;
int outputLength = 0;
foreach (char value in text)
{
bool dupe = false;
for (int i = 0; i < strLength; i++)
{
if (value == str[i])
{
dupe = true;
break;
}
}
if (!dupe)
{
str[strLength] = value;
strLength++;
output[outputLength] = value;
outputLength++;
}
}
return new string(output, 0, outputLength);
}
If time complexity is all you care about you could map the characters to int values, then have an array of bool values which remember if you've seen a particular character value previously.
Something like ... [not tested]
bool[] array = new bool[256]; // or larger for Unicode
foreach (char value in text)
if (array[(int)value])
return false;
else
array[(int)value] = true;
return true;
try this,
string RemoveDuplicateChars(string key)
{
string table = string.Empty;
string result = string.Empty;
foreach (char value in key)
{
if (table.IndexOf(value) == -1)
{
table += value;
result += value;
}
}
return result;
}
usage
Console.WriteLine(RemoveDuplicateChars("hello"));
Console.WriteLine(RemoveDuplicateChars("helo"));
Console.WriteLine(RemoveDuplicateChars("Crocodile"));
output
helo
helo
Crocdile
public boolean ifUnique(String toCheck){
String str="";
for(int i=0;i<toCheck.length();i++)
{
if(str.contains(""+toCheck.charAt(i)))
return false;
str+=toCheck.charAt(i);
}
return true;
}
EDIT:
You may also consider to omit the boundary case where toCheck is an empty string.
The following code works:
static void Main(string[] args)
{
isUniqueChart("text");
Console.ReadKey();
}
static Boolean isUniqueChart(string text)
{
if (text.Length == 0 || text.Length > 256)
{
Console.WriteLine(" The text is empty or too larg");
return false;
}
Boolean[] char_set = new Boolean[256];
for (int i = 0; i < text.Length; i++)
{
int val = text[i];//already found this char in the string
if (char_set[val])
{
Console.WriteLine(" The text is not unique");
return false;
}
char_set[val] = true;
}
Console.WriteLine(" The text is unique");
return true;
}
If the string has only lower case letters (a-z) or only upper case letters (A-Z) you can use a very optimized O(1) solution.Also O(1) space.
c++ code :
bool checkUnique(string s){
if(s.size() >26)
return false;
int unique=0;
for (int i = 0; i < s.size(); ++i) {
int j= s[i]-'a';
if(unique & (1<<j)>0)
return false;
unique=unique|(1<<j);
}
return true;
}
Remove Duplicates in entire Unicode Range
Not all characters can be represented by a single C# char. If you need to take into account combining characters and extended unicode characters, you need to:
parse the characters using StringInfo
normalize the characters
find duplicates amongst the normalized strings
Code to remove duplicate characters:
We keep track of the entropy, storing the normalized characters (each character is a string, because many characters require more than 1 C# char). In case a character (normalized) is not yet stored in the entropy, we append the character (in specified form) to the output.
public static class StringExtension
{
public static string RemoveDuplicateChars(this string text)
{
var output = new StringBuilder();
var entropy = new HashSet<string>();
var iterator = StringInfo.GetTextElementEnumerator(text);
while (iterator.MoveNext())
{
var character = iterator.GetTextElement();
if (entropy.Add(character.Normalize()))
{
output.Append(character);
}
}
return output.ToString();
}
}
Unit Test:
Let's test a string that contains variations on the letter A, including the Angstrom sign Å. The Angstrom sign has unicode codepoint u212B, but can also be constructed as the letter A with the diacritic u030A. Both represent the same character.
// ÅÅAaA
var input = "\u212BA\u030AAaA";
// ÅAa
var output = input.RemoveDuplicateChars();
Further extensions could allow for a selector function that determines how to normalize characters. For instance the selector (x) => x.ToUpperInvariant().Normalize() would allow for case-insensitive duplicate removal.
public static bool CheckUnique(string str)
{
int accumulator = 0;
foreach (int asciiCode in str)
{
int shiftedBit = 1 << (asciiCode - ' ');
if ((accumulator & shiftedBit) > 0)
return false;
accumulator |= shiftedBit;
}
return true;
}

Count length of word without using Inbuilt functions

This is a question I have come across and failed
Suppose say
string str = "wordcounter";
One can easily find the Length using str.Length
However, is it possible in C# to get the number of letters, without using any inbuilt functions like Length, SubStr etc
you could write a loop and increment a counter inside this loop:
int numberOfLetters = 0;
foreach (var c in str)
{
numberOfLetters++;
}
// at this stage numberOfLetters will contain the number of letters
// that the string contains
there is also another way:
int numberOfLetters = str.ToCharArray().Length;
there is also another, even crazier way using the SysStringByteLen function which operates on a BSTR. Strings in .NET are layed out in memory by using a 4 byte integer containing the length of the string followed by that many 2 byte UTF-16 characters representing each character. This is similar to how BSTRs are stored. So:
class Program
{
[DllImport("oleaut32.dll")]
static extern uint SysStringByteLen(IntPtr bstr);
static void Main()
{
string str = "wordcounter";
var bstr = Marshal.StringToBSTR(str);
// divide by 2 because the SysStringByteLen function returns
// number of bytes and each character takes 2 bytes (UTF-16)
var numberOfLetters = SysStringByteLen(bstr) / 2;
Console.WriteLine(numberOfLetters);
}
}
Obviously doing something like this instead of using the built-in Length function should never be done in any real production code and the code shown here should not be taken seriously.
My answer is bit late, but I would like to post the same. Though all above mentioned solutions are correct, but I believe that the IL of the foreach does knows about the length of the iterable before iterating it. Talking of a pure solution, here's mine:
private int stringLength(string str) {
int length = 0;
bool done = false;
do {
try {
char c = str[length];
length++;
} catch (IndexOutOfRangeException) {
done = true;
}
} while(!done);
return length;
}
How about?
int myOwnGetStringLength(String str)
{
int count = 0;
foreach(Char c in str)
count++;
return count;
}
not very fast but yo can always loop and count the number of caracter contained.
int counter = 0;
foreach (var caracter in str)
{
counter ++;
}
class Program
{
static void Main(string[] args)
{
string Name = "He is palying in a ground.";
char[] characters = Name.ToCharArray();
StringBuilder sb = new StringBuilder();
for (int i = Name.Length - 1; i >= 0; --i)
{
sb.Append(characters[i]);
}
Console.Write(sb.ToString());
Console.Read();
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter a string to find its lenght");
string ch = Console.ReadLine();
Program p = new Program();
int answer = p.run(ch);
Console.WriteLine(answer);
Console.ReadKey();
}
public int run(string ch)
{
int count = 0;
foreach (char c in ch)
{
count++;
}
return count;
}
}
My general solution involves without using 'foreach' or 'StringBuilder' (which are C# specific) or without catching any exception.
string str = "wordcounter";
str += '\0';
int x = 0;
while (str[x] != '\0')
x++;
Console.WriteLine(x); //Outputs 11
class Program
{
static void Main(string[] args)
{
string test = "test";
//string as char array:
//iterate through char collection
foreach (char c in test)
{
//do something
}
//access elements by index
Console.WriteLine("Contents of char array : {0}, {1}, {2}, {3}", test[0], test[1], test[2], test[3]);
Console.ReadKey();
}
}
namespace ConsoleApplication {
class Program {
static void Main(string[] args) {
string testString = "testing";
int x = 0;
foreach(char c in testString) {
x++;
}
Console.WriteLine("\nLength Of String:{0}", (x));
Console.Read();
}
}

Categories

Resources