Need to remove duplicate values from C# program - c#

I need some help with a C# program that i am creating. So in this scenario i am inputting duplicate values into the program. For Example, a,b,b,c,c.
The exercise is that if there are any duplicated letters inputted (no numbers) i should get an error stating "Duplicate Value. Please Try Again!" and will not accept the duplicate value, and should show the values as a,b,c,d,e.
class Program
{
static void Main(string[] args)
{
char[] arr = new char[5];
//User input
Console.WriteLine("Please Enter 5 Letters only: ");
for (int i = 0; i < arr.Length; i++)
{
arr[i] = Convert.ToChar(Console.ReadLine());
}
//display
for(int i = 0; i<arr.Length; i++)
{
Console.WriteLine("You have entered the following inputs: ");
Console.WriteLine(arrArray[i]);
}
}
}

Choose right data structure at beginning, use HashSet instead of array since the operations are mainly looking up & inserting.

Using a hashtable (Generic Dictionary) is an efficient way to determine if an entered character has already been encountered.
Also, the Char.IsLetter method in the .NET framework is a great way to check for bad data.
static void Main(string[] args) {
Dictionary<char, bool> charsEntered = new Dictionary<char, bool>();
Console.WriteLine("Please enter 5 characters, each on a separate line.");
while (charsEntered.Count() < 5) {
Console.WriteLine("Enter a character:");
char[] resultChars = Console.ReadLine().ToCharArray();
if(resultChars.Length != 1 || !Char.IsLetter(resultChars[0])) {
Console.WriteLine("Bad Entry. Try again.");
} else {
char charEntered = resultChars[0];
if (charsEntered.ContainsKey(charEntered))
Console.WriteLine("Character already encountered. Try again.");
else
charsEntered[charEntered] = true;
}
}
Console.WriteLine("The following inputs were entered:");
Console.WriteLine(String.Join(", ", charsEntered.Keys));
Console.ReadLine();
}

Use Any linq expression to validate duplicates. char.TryParse will validates input and returns true when succeeded.
public static void Main()
{
char[] arr = new char[5];
//User input
Console.WriteLine("Please Enter 5 Letters only: ");
for (int i = 0; i < arr.Length; i++)
{
char input;
if(char.TryParse(Console.ReadLine(), out input) && !arr.Any(c=>c == input))
{
arr[i] = input;
}
else
{
Console.WriteLine( "Error : Either invalid input or a duplicate entry.");
i--;
}
}
Console.WriteLine("You have entered the following inputs: ");
//display
for(int i = 0; i<arr.Length; i++)
{
Console.WriteLine(arr[i]);
}
}
Working Code

Elaborating on Shelvin's answer of using HashSet
HashSet<char> chars = new HashSet<char>();
//User input
Console.WriteLine("Please Enter 5 Letters only: ");
for (int i = 0; i < 5; )
{
char c = Convert.ToChar(Console.ReadLine());
if(!("abcdefghijklmnopqrstuvwxyz".Contains(c.ToString().ToLower())))
{
Console.WriteLine("Please enter an alphabet");
continue;
}
else if (!chars.Contains(c))
{
chars.Add(c);
i++;
}
else
{
Console.WriteLine("Duplicate value please try again");
continue;
}
}
//display
Console.WriteLine("You have entered the following inputs: ");
foreach(char c in chars)
Console.WriteLine(c.ToString());
Console.Read();

Keep it simple, and although a HashSet is nice semantically, it's not needed for 5 elements (it's actually slower than a List in that case). Worse, it requires a parallel structure to track the characters (assuming you care about order).
Clearly none of these considerations matter for such a small example but it's good to learn them up front and don't always jump to big-O notation when actually measured performance and memory consumption should be your guide for most practical applications.
Instead you can simply do:-
List<char> chars = new List<char>(5);
while (chars.Count < 5)
{
char c = Console.ReadKey().KeyChar;
if (!char.IsLetter(c)) continue;
if (chars.Contains(char)) continue;
chars.Add(char);
}
Plus whatever error messages you want to add.

Related

How to display or hide letters in a string - Hangman game

I'm a newbie in C# and I'm learning it for the past few weeks basically doing tasks and challenges to improve my knowledge. I came across making a "Hangman" console app and I'm close to finishing it. In fact, I can say that I made it but I would like to improve something and learn one new thing.
This is an example of my question if you can't understand it, sorry, English is not my main language.
You know how when you play hangman, once you guess the right letter that letter "shows itself" on its position in a word.
etc. word Head
you guess "e" and it prints _e__
you guess "d" and it prints _e_d
you guess "H" and it prints He_d
you guess "a" and it prints Head
Here is my code :
using System;
namespace Hangman
{
class Program
{
static void Main(string[] args)
{
Random random = new Random();
string word1;
Console.Write("Please enter a random word: ");
word1 = Console.ReadLine();
word1.ToLower();
Console.Clear();
int i = 0;
int j = 0;
do
{
string guess;
Console.Write("Please enter a letter: ");
guess = Console.ReadLine();
if (guess == "" +
"")
{
i += 1;
Console.WriteLine($"Not allowed. You have {word1.Length - i} tries left.\n");
}
else if(word1.Contains(guess) == true)
{
Console.WriteLine("You guessed correctly !\n");
j += 1;
}
else
{
i += 1;
Console.WriteLine($"You guessed wrongly ! You have {word1.Length - i} tries left\n");
}
/* If you guessed all the letters correctly this will print that you won */
if (j == word1.Length)
{
Console.Clear();
Console.WriteLine($"You won ! The guessing word was {word1}.\n");
}
/* If you tried too many times it will end your program and you will lose.
* If you guessed all letters in a word correctly you win and the game is done. */
if(i == word1.Length)
{
Console.Clear();
Console.WriteLine($"You lost ! The guessing word was {word1}.\n");
}
} while (i != word1.Length & j != word1.Length);
}
}
}
There are a few flows with your code, bugs if you will -
If you enter the word head and you keep guessing h you win.
When you enter your guess you can actually enter a string not just a single character.
Also the code does not consider the possibility for multiple character entries. For example if the word is alexleo - and I guess l - it will only give me a single match.
What we have to do is keeping track of the status of each letter in the word - that is whether the letter has been guessed or not.
At each iteration:
The user guesses a letter -
If the letter is contained in the word - we change the status to
guessed.
If the letter has already been guessed we warn the user.
If the letter is not contained in the word -we increase our flag
totalGuessCounts by 1.
We feedback the user and display what has been guessed so far.
The loop completes when we either win or the user has run out of guesses.
Firstly I have defined my letter setting class
public class LetterSetting
{
public char Letter { get; set; }
public bool HasBeenGuessed { get; set; }
public LetterSetting(char letter, bool hasBeenGuessed)
{
this.Letter = letter;
this.HasBeenGuessed = hasBeenGuessed;
}
}
Then I have defined the hangman word class - here is where we keep track of the letter that have been guessed, the logic to see if we have won and the display of what we have so far.
static class HangmanWord
{
static List<LetterSetting> LettersStatus = new List<LetterSetting>();
public static void Initialise(string word)
{
foreach (char letter in word)
{
LettersStatus.Add(new LetterSetting(letter, false));
}
}
public static bool SetGuessStatus(char letter)
{
List<LetterSetting> tempLettersStatus = new List<LetterSetting>();
foreach (LetterSetting item in LettersStatus)
{
if (item.Letter == letter && item.HasBeenGuessed)
{
return false;
}
tempLettersStatus.Add(item.Letter == letter ? new LetterSetting(letter, true) : item);
}
LettersStatus = tempLettersStatus;
return true;
}
public static void Display()
{
StringBuilder hangManWord = new StringBuilder();
foreach (var item in LettersStatus)
{
hangManWord.Append(!item.HasBeenGuessed ? '_' : item.Letter);
}
Console.SetCursorPosition(50, 1);
Console.Write(hangManWord.ToString());
Console.SetCursorPosition(0,1);
}
public static bool HaveWeWon()
{
return !LettersStatus.Any(letter => letter.HasBeenGuessed == false);
}
}
You main class will simply encapsulate the to retrieve the word and the game-loop:
class Program
{
static void Main(string[] args)
{
int totalGuessCounts = 0;
int maxNumberOfGuess = 0;
Console.Write("Please enter a random word: ");
string word = Console.ReadLine()?.ToLower();
while (word != null && word.Contains(" ") || string.IsNullOrWhiteSpace(word))
{
Console.Clear();
Console.WriteLine("Word must not contain spaces or be nothing ");
Console.Write("Please enter a random word: ");
word = Console.ReadLine()?.ToLower();
}
maxNumberOfGuess = word.Length;
HangmanWord.Initialise(word);
Console.Clear();
HangmanWord.Display();
do
{
Console.Write("Please enter a letter: ");
char guess = Console.ReadKey().KeyChar;
Console.WriteLine("\n");
Console.Clear();
if (!char.IsLetterOrDigit(guess))
{
continue;
}
if (word.Contains(guess))
{
Console.WriteLine(HangmanWord.SetGuessStatus(guess)
? "You guessed correctly!"
: "You already guessed that letter.");
}
else
{
totalGuessCounts++;
Console.WriteLine($"You guessed wrongly ! You have {word.Length - totalGuessCounts} tries left\n");
}
if (HangmanWord.HaveWeWon())
{
Console.WriteLine($"You won ! The guessing word was {word}.\n");
break;
}
if (totalGuessCounts == maxNumberOfGuess)
{
Console.WriteLine($"You lost ! The guessing word was {word}.\n");
}
HangmanWord.Display();
} while (totalGuessCounts != word.Length);
}
}
Here are a couple of results:
you can change the feedback layout - to your like - add a dictionary of words so you dont have to enter the word yourself - draw the hangman whilst the user guesses - Ideally you might want to move all the logic within the class HangmanWord - that is for failure , success and full feedback.
I coded something like this. This is my suggestion for solving this problem, but remember that it probably isn't the best. You can try to optimize it.
bool[] guessed = new bool[wordToGuess.Length];
int index = 0;
while ((index = wordToGuess.IndexOf(guess, index, wordToGuess.Length - index)) != -1)
{
guessed[index] = true;
index++;
}
for(int i = 0; i < wordToGuess.Length; i++)
{
if(guessed[i])
Console.Write(wordToGuess[i]);
else
Console.Write('_');
}
I used String.IndexOf(String, Int32, Int32) method for this purpose.

Why are the contents of my array being printed twice in C#?

I do not understand why this code prints the contents of the array twice.
static void Main(string[] args)
{
Int64 userlength;
Int64 userlengthcounter;
String unencrypted;
char current;
start:
Console.WriteLine("Please enter how many characters the string you want encrypyted to be:");
userlength = Convert.ToInt64(Console.ReadLine());
Console.WriteLine("Please enter the string you want to be encrypted:");
unencrypted = Console.ReadLine();
int[] first = new int[userlength];
int[] second = new int[userlength];
if (userlength != unencrypted.Length)
{
Console.WriteLine("The string you entered was not the same length as the number of characters you specified");
goto start;
}
for (int i = 0; i < userlength; i++)
{
Console.WriteLine(unencrypted[i]);
current = unencrypted[i];
first[i] = current;
}
foreach (char item in first)
{
Console.WriteLine(item.ToString());
}
Console.ReadLine();
}
For example entering abcd would return abcdabcd and i don't understand why. Any help would be appreciated thanks.
It's because you have two loops, first you print each character in unencrypted in the for loop and store the chars in first array.
Then you loop over the array and print the chars again with foreach.
Additional Note: Using goto is almost always a bad idea because it makes your code hard to follow and unreadable. Because you have to manually track where the code jumps.
You can do the same thing with a do-while loop instead.
do {
Console.WriteLine("Please enter how many characters the string you want encrypyted to be:");
userlength = Convert.ToInt64(Console.ReadLine());
Console.WriteLine("Please enter the string you want to be encrypted:");
unencrypted = Console.ReadLine();
int[] first = new int[userlength];
int[] second = new int[userlength];
if (userlength != unencrypted.Length)
{
Console.WriteLine("The string you entered was not the same length as the number of characters you specified");
}
} while(userlength != unencrypted.Length);
you specifically build "first" then you print it in foreach, basically displaying the same content twice:
for (int i = 0; i < userlength; i++)
{
Console.WriteLine(unencrypted[i]);
current = unencrypted[i];
first[i] = current;
}
foreach (char item in first)
{
Console.WriteLine(item.ToString());
}

Very basic bingo game

I'm new to C# and programming as a whole and i have a quick question to ask you guys, I've searched a bit but only found far too complicated examples for me to implement in my work so here goes:
int[] newArray = new int[7];
Console.WriteLine("Hello! Please enter 7 numbers between 1-25, press ENTER after each number. ");
for (int i = 0; i < newArray.Length; i++)
bool loop = true;
do
{
try
{
newArray[i] = Convert.ToInt32(Console.ReadLine());
loop = false;
}
catch
{
Console.WriteLine("You may only enter numbers!");
}
} while (loop);
Console.Write("You entered the following numbers: ");
for (int i = 0; i < newArray.Length; i++)
{
Console.WriteLine(newArray[i]);
}
}
This is the first part of a bingogame im trying to write, but i can't understand why the names loop and i don't exist, should i make something static? Move some brackets around? Please help.
You need to wrap the entire for statement in braces, otherwise it will only execute the next line of code, which is just bool loop = true;.
for (int i = 0; i < newArray.Length; i++)
{ // <-- Add this
bool loop = true;
do
{
try
{
newArray[i] = Convert.ToInt32(Console.ReadLine());
loop = false;
}
catch
{
Console.WriteLine("You may only enter numbers!");
}
} while (loop);
Console.Write("You entered the following numbers: ");
}
It's worth to mention about string.Join method to print all elements of the list.
Console.WriteLine("You entered the following numbers: ");
Console.WriteLine(string.Join(", ", newArray));
After using Parse/TryParse method, you don't need to use Convert.ToInt32 any more.
To validate number and be able to reenter it, it is much better to do 2 IF statements instead of using Constains method of Enumerable class.
while (!int.TryParse(Console.ReadLine(), out number) || number < 1 || number > 25)
{
Console.WriteLine("You may only enter numbers from range 1-25!");
}
Make one single bracket right after your for cycle.
You're missing an open brace. This looks like homework so I'm not going to rewrite it for you. Take a closer look and work on the formatting and indentations. That will give you a clue as to were that missing brace should be.
Here is a nicer way to test for number input without the try/catch
var newArray = new int[7];
Console.WriteLine("Hello! Please enter 7 numbers between 1-25, press ENTER after each number. ");
for (var i = 0; i <= newArray.Length - 1; i++)
{
int number;
while (!int.TryParse(Console.ReadLine(), out number))
{
Console.WriteLine("You may only enter numbers!");
}
newArray[i] = Convert.ToInt32(number);
}
Console.WriteLine("You entered the following numbers: ");
foreach (var t in newArray)
{
Console.WriteLine(t);
}

(C#) How to list all user-inputted integers in an array?

I'm working on an application that has the user choose how many integers to enter into an array, and then has them input the numbers to be added to the array. After each number is inputted, it shows all non-duplicate integers entered by the user up to that point in a vertical list. If it isn't unique, it informs the user that it has already been inputted.
I'm not sure how to make the application list every integer entered, rather than just the most recent one.
Here's my code:
static void Main(string[] args)
{
//checks how many numbers will be entered
int manyNumbers;
Console.WriteLine("How many numbers will you enter?");
manyNumbers = Convert.ToInt32(Console.ReadLine());
int[] array = new int[manyNumbers];
//starts asking for numbers
for (int i = 0; i < manyNumbers;)
{
Console.Write("\nEnter number: ");
string entered = Console.ReadLine();
int val;
//checks to see if valid number
if (!Int32.TryParse(entered, out val))
{
Console.Write("Invalid number '{0}'", entered);
array[i++] = val;
}
//checks to see if already entered
else if (i > 0 && array.Take(i).Contains(val))
{
Console.Write("{0} has already been entered", val);
array[i++] = val;
}
//prints inputted integer
else {
array[i++] = val;
Console.WriteLine("{0}", val);
}
}
}
Just loop over the array so far printing each.
Forgive my mobile crafted code, but more or less this:
//prints inputted integer
else {
array[i++] = val;
for(int j=0 ; j<i;j++) {
Console.WriteLine("{0}", array[j]);
}
}
You may use foreach loop
foreach(int num in array)
{
Console.WriteLine("{0}", num);
}
Very Basic approach, try one of the following:
for(var x=0;x<array.length;x++)
or
foreach(var i in array)
But your use case, use HashSet data structure
Mathematically, Set is unique list of things.
try this code, it uses a Dictionary to keep the list in memory and to search to see if the an integer has been added,
using System.Collections.Generic;
static void Main(string[] args)
{
//checks how many numbers will be entered
int manyNumbers;
Console.WriteLine("How many numbers will you enter?");
manyNumbers = Convert.ToInt32(Console.ReadLine());
Dictionary<int, int> array = new Dictionary<int, int>();
//starts asking for numbers
for (int i = 0; i < manyNumbers; )
{
Console.Write("\nEnter number: ");
string entered = Console.ReadLine();
int val;
//checks to see if valid number
if (!Int32.TryParse(entered, out val))
{
Console.Write("Invalid number '{0}'", entered);
}
//checks to see if already entered
else
if (i > 0 && array.ContainsKey(val))
{
Console.Write("{0} has already been entered", val);
//array[i++] = val;
}
else
{
//* add the new integer to list
array.Add(val, 0);
//prints the complete list
List<int> keys = new List<int>(array.Keys);
Console.WriteLine();
for(int j=0; j<keys.Count; j++) Console.WriteLine(keys[j]);
}
}
}

Reading in values into Array

This program asks the user for an array size then asks the user to enter in the array values. The problem I am having is that the for loop to read in the array values doesn't work properly. No matter what is the value for nit will sitll ask for a lot more inputs.
int n;
Console.WriteLine("Enter how many values you are entering in");
n = Convert.ToInt32(Console.Read());
int[] arr = new int[n];
Console.WriteLine("Enter your values in");
for (int i = 0; i < n; i++)
{
arr[i] = Convert.ToInt32(Console.Read());
}
Quick and easy fix:
Use int.Parse(Console.ReadLine()) in place of your Convert.ToInt32(Console.Read())
Explanation:
You were getting the ASCII value the way you were doing it. For example, if you would type in the number 2 your n variable would have actually been set to 50.
for (int i = 0; i < n; i++)
{
string value = Console.ReadLine();
int result;
if (int.TryParse(value, out result)) arr[i] = result;
else
{
Console.WriteLine("Invalid value try again.");
i--;
}
}
Use Console.ReadLine() instead of Console.Read().
As documented here, Console.Read() returns only a single character. You probably want to read in the entire line and parse it for the integer value.
This code should do what you want:
int n;
Console.WriteLine("Enter how many values you are entering in");
//n = Convert.ToInt32(Console.Read());
n = Int32.Parse(Console.ReadLine());
int[] arr = new int[n];
Console.WriteLine("Enter your values in");
for (int i = 0; i < n; i++)
{
arr[i] = Int32.Parse(Console.ReadLine());
}
This works in the input case of n = 10 as well.
here is a version with some more robust error checking.
Note: (entering this directly without running, so I apologise if there are minor syntax errors)
int count;
bool attemptedSetCount = false;
do
{
if (attemptedSetCount)
{
Console.WriteLine("Please enter a valid integer");
}
Console.WriteLine("Enter how many values you are entering in");
string countString = Console.ReadLine();
attemptedSetCount = true;
}
while (!Int32.TryParse(countString, out count));
int[] arr = new int[n];
Console.WriteLine("Enter your values in");
for (int i = 0; i < count; i++)
{
string valString = (Console.ReadLine());
int val;
if(!Int32.TryParse(valString, out val))
{
Console.WriteLine("Please enter a valid integer");
i--;
}
else
{
arr[i] = val;
}
}
The Console.Read function reads the next single character of input and returns it as as int value. The code then passes it to the Convert.ToInt32 API which will essentially return the exact value that was passed in. This means that numbers like 1 which have the character value 49 will be processed as 49. This is why your code is reading in so many values
To properly read a number from the Console use the ReadLine method which returns a String. This can then be passed to Int32.Parse and converted to an int
Int32.Parse(Console.ReadLine());
Or better yet, just abstract this out to a method
static int ReadInt() {
return Int32.Parse(Console.ReadLine());
}
Use ReadLine() to get the number, then use ReadKey() not Read() or ReadLine() when entering the chararcters
int n;
Console.WriteLine("Enter how many values you are entering in");
n = Convert.ToInt32(Console.ReadLine());
int[] arr = new int[n];
Console.WriteLine("Enter your values in");
for (int i = 0; i < n; i++)
{
char c = Console.ReadKey().KeyChar;
arr[i] = Convert.ToInt32(c.ToString());
}
Have you read the documentation?
The Read method blocks its return while you type input characters;
it terminates when you press the Enter key. Pressing Enter appends a
platform-dependent line termination sequence to your input (for example,
Windows appends a carriage return-linefeed sequence). Subsequent calls to the
Read method retrieve your input one character at a time. After the final
character is retrieved, Read blocks its return again and the cycle repeats.
Note that you will not get a property value of -1 unless you perform one of the
following actions: simultaneously press the Control modifier key and Z console
key (Ctrl+Z), which signals the end-of-file condition; press an equivalent key
that signals the end-of-file condition, such as the F6 function key in Windows;
or redirect the input stream to a source, such as a text file, that has an actual
end-of-file character.
The ReadLine method, or the KeyAvailable property and ReadKey method are
preferable to using the Read method.
If I execute this code:
Console.Write("? ") ;
int input = Console.Read() ;
Console.WriteLine("You entered {0}.", input ) ;
Console.WriteLine( "{0} is the decimal code point for the character whose glyph is '{1}.'" , input , (char)input ) ;
And, if I, at the ? prompt, enter the characters 123 followed by the return key:
? 123<return>
I'll see this output:
You entered 49.
49 is the decimal code point for the character whose glyph is '1'.
[Note that in Windows, you can generate a '1' at the command prompt by holding down the <ALT> key, typing '0049and releasing the` key.]
Assuming that the intent is for the user to specify a number of values to be entered and to then prompt them for that many input values, you want code that looks something like this:
static void Main()
{
int n = ReadIntegerFromConsole( "How many values do you want to enter?" ) ;
int[] values = new int[n] ;
for ( int i = 0 ; i < values.Length ; ++i )
{
string prompt = string.Format( "{0}/{1}?" , i , n ) ;
values[i] = ReadIntegerFromConsole(prompt) ;
}
Console.WriteLine( "You entered: {0}" , string.Join(", ",values) ) ;
return ;
}
static int ReadIntegerFromConsole( string prompt )
{
int value ;
bool isValid ;
do
{
Console.Write( prompt) ;
Console.Write( ' ' );
string text = Console.ReadLine() ;
isValid = int.TryParse(text, out value ) ;
prompt = "That's not an integer. Try again:" ;
} while (!isValid) ;
return value ;
}

Categories

Resources