Why is this c# Palindrome Program not working? - c#

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace Palindrome
{
class Program
{
static void Main(string[] args)
{
string filePath = #"C:\Users\Me\Desktop\Palindromes\palindromes.txt";
//This gets the file we need
var meStack = new Stack<string>();
//this creates the stack
foreach (var item in File.ReadLines(filePath))
{
meStack.Push(item.ToUpper());
}
//for every item in the file, push onto the stack and make it upper case
while (meStack.TryPop(out string Line))
{
reverseMe(Line);
}
//While every line in the stack is popped out, every line goes to the fucntion reverseMe
static bool reverseMe(string Line)
{
return
Line == Line.Reverse();
}
//return true if line is the same as the line backwards or false if its not.
}
}
}
How do I get output?
I have written comments to try and understand... but I am not getting a console output. I want the code to take in the file, put all the strings into a stack, and send every line in that stack to the reverseMe() function, which is a bool. The bool will see if the string is the same forward as it is backwards and if so it will return true or false. Basically my console is empty when I try to run this code.. What do I do?

There is a problem in the method reverseMe, The function Reverse gives you collection of char if applied on string, then you need to convert IEnumerable<char> to string by new string() or string.Concat(), like the following code:
static bool reverseMe(string Line)
{
//deleting whitespaces, tabs
Line = Regex.Replace(Line, #"\s+", "");
return Line == new string(Line.Reverse().ToArray());
//or
//return Line == string.Concat(Line.Reverse());
//or like Dmitry comment
//return Line.SequenceEqual(Line.Reverse());
}
Calling reverseMe, and output result like : word is not palindrome
while (meStack.TryPop(out string Line))
{
string isOrNotPalindrome = reverseMe(Line) ? string.Empty : "not";
Console.WriteLine($"{Line} is {isOrNotPalindrome} palindrome");
}
Demo
bool isPalindrome1 = reverseMe("madam");
bool isPalindrome2 = reverseMe("nurses run");
bool isPalindrome3 = reverseMe("AaBbbBaAp");
Result
true
true
false
I hope this will help you fix the issue

Let's start from the problem; I assume that you want to scan all file's lines and print out if the line is a palindrom.
First, we need to implement IsPalindrom method:
private static bool IsPalindrom(string value) {
if (null == value)
return false; // or true, ot throw ArgumentNullException
// We have to prepare the string: when testing for palindrom
// 1. Let's ignore white spaces (' ', '\r', '\t' etc.)
// 2. Let's ignore punctuation (':', ',' etc.)
// 3. Let's ignore cases (i.e. 'M' == 'm')
// So "Madam, I'm Adam" will be a proper palindrom
value = string.Concat(value
.Where(c => !char.IsWhiteSpace(c))
.Where(c => !char.IsPunctuation(c))
.Select(c => char.ToUpperInvariant(c)));
// Instead of Reversing we can just compare:
// [0] and [Length - 1] then [1] and [Length - 2] etc.
for (int i = 0; i < value.Length / 2; ++i)
if (value[i] != value[value.Length - 1 - i])
return false; // we have a counter example: value is NOT a palidrom
// Value has been scanned, no counter examples are found
return true;
}
Time to write Main method:
static void Main(string[] args) {
string filePath = #"C:\Users\Me\Desktop\Palindromes\palindromes.txt";
var result = File
.ReadLines(filePath)
.Where(line => !string.IsNullOrWhiteSpace(line)) // let's skip empty lines
.Select(line => $"{(IsPalindrom(line) ? "Palindrom" : "Not a palindrom")}: \"{line}\"");
// Do not forget to print result on the Console:
foreach (var record in result)
Console.WriteLine(record);
// Pause to have a look at the outcome (wait for a key to be pressed)
Console.ReadKey();
}

Uhm there isn't a Console.WriteLine() to do any actual output after getting results from the reverseMe() function.

Related

Counting duplicate chars in a C# string

I'm new to C# and trying to work out how to count the number of duplicates in a string. Example input and output would be:
"indivisibility" -> 1 # 'i' occurs six times
"Indivisibilities" -> 2 # 'i' occurs seven times and 's' occurs twice
"aA11" -> 2 # 'a' and '1'
"ABBA" -> 2 # 'A' and 'B' each occur twice
My code so far is as follows:
using System;
using System.Collections;
using System.Linq;
public class Kata
{
public static int DuplicateCount(string str)
{
Stack checkedChars = new Stack();
Stack dupChars = new Stack();
str = str.ToLower();
for (int i=1; i < str.Length; i++) {
var alreadyCounted = checkedChars.Contains(str[i]) && dupChars.Contains(str[i]);
if (!checkedChars.Contains(str[i])) {
checkedChars.Push(str[i]);
} else if (checkedChars.Contains(str[i])) {
dupChars.Push(str[i]);
} else if (alreadyCounted) {
break;
}
}
return dupChars.Count;
}
}
My approach is to loop through each character in the string. If it hasn't been seen before, to add it to a 'checkedChars' Stack (to keep track of it). If it's already been counted, add it to a 'dupChars' Stack. However, this is failing the tests. E.g:
aabbcde is the string, and the test fails with: Expected: 2 But Was: 1
Also when I console out errors, it appears that the checkedChars Stack is empty.
Can anyone spot where I have gone wrong please?
I'd suggest you use LINQ instead. It's a more suitable tool for the problem, and it results in much cleaner code:
class Program
{
static void Main(string[] args)
{
var word = "indivisibility";
Console.WriteLine($"{word} has {CountDuplicates(word)} duplicates.");
word = "Indivisibilities";
Console.WriteLine($"{word} has {CountDuplicates(word)} duplicates.");
word = "aA11";
Console.WriteLine($"{word} has {CountDuplicates(word)} duplicates.");
word = "ABBA";
Console.WriteLine($"{word} has {CountDuplicates(word)} duplicates.");
Console.ReadLine();
}
public static int CountDuplicates(string str) =>
(from c in str.ToLower()
group c by c
into grp
where grp.Count() > 1
select grp.Key).Count();
}
}
Here's the output:
indivisibility has 1 duplicates.
Indivisibilities has 2 duplicates.
aA11 has 2 duplicates.
ABBA has 2 duplicates.
Hope this helps.
You need to start the loop at int i = 0, because indexing start at 0 and not 1. So to get the first character you'll need to call str[0].
You can also remove the break as your code will never hit it, since the first 2 conditions are exactly the opposite of each other. Instead check first if alreadyCounted is true and use continue (not break as it will exit the loop entirely!) to skip to the next iteration, to avoid counting the same characters more than once.
you can use LINQ for this -
var str = "aabbcde";
var count = str.ToLower().GroupBy(x => x).Select(y => y).Where(z=>z.Count()>1).Count();
You can also use MoreLinq.CountBy:
using System;
using System.Linq;
using MoreLinq;
namespace ConsoleApp1
{
internal class Program
{
private static int CountDuplicateCharacters(string s)
{
return s?.CountBy(c => c).Where(kvp => kvp.Value > 1).Count() ?? 0;
}
private static void Main(string[] args)
{
foreach (var s in new string[] { "indivisibility", "Indivisibilities", "aA11", "ABBA" })
{
Console.WriteLine(s + ": " + CountDuplicateCharacters(s));
}
}
}
}
In case you do not want to differentiate between lower and upper case you need to supply an EqualityComparer as a second argument to CountBy.

C# Local variable won't mutate in if statement

I am quite new the C# and I have googled the answer. The closest answer I have found was this one. But it doesn't help me.
I am trying to write a function that finds the biggest number in a string using loops and splicing only. For some reason, when the condition is met, the local variable big won't mutate in the if statements. I have tried to debug it by setting big = 34 when I hit a space, but even then it won't mutate the local variable.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace parser
{
class Sub_parser
{
// function to find the greatest number in a string
public int Greatest(string uinput)
{
int len = uinput.Length;
string con1 = "";
int big = 0;
int m = 0;
// looping through the string
for (int i = 0; i < len; i++)
{
// find all the numbers
if (char.IsDigit(uinput[i]))
{
con1 = con1 + uinput[i];
}
// if we hit a space then we check the number value
else if (uinput[i].Equals(" "))
{
if (con1 != "")
{
m = int.Parse(con1);
Console.WriteLine(m);
if (m > big)
{
big = m;
}
}
con1 = "";
}
}
return big;
}
public static void Main(string[] args)
{
while (true)
{
string u_input = Console.ReadLine();
Sub_parser sp = new Sub_parser();
Console.WriteLine(sp.Greatest(u_input));
}
}
}
}
The problem comes from your check in this statement :
else if (uinput[i].Equals(" "))
uinput[i] is a char, while " " is a string : see this example
if you replace the double quotes by single quotes, it works fine...
else if (uinput[i].Equals(' '))
And, as stated by the comments, the last number will never be checked, unless your input string ends by a space. This leaves you with two options :
recheck again the value of con1 after the loop (which is not very good-looking)
Rewrite your method because you're a bit overdoing things, don't reinvent the wheel. You can do something like (using System.Linq):
public int BiggestNumberInString(string input)
{
return input.Split(null).Max(x => int.Parse(x));
}
only if you are sure of your input
When you give a number and a space in the keyboard you only read the number, no space.
So you have uinput="34".
Inside the loop, you check if the m > big only if uinput[i].Equals(" "). Which is never.
In general if you read a line, with numbers followed by space, it would ignore the last number.
One solution would be to append a " " into uinput, but i recommend splicing.
string[] numbers = uinput.Split(null);
Then iterate over the array.
Also, as said in another answer compare uinput[i].Equals(' ') because " "represents a string, and you were comparing a char with a string.
As Martin Verjans mentioned, in order to make your code work you have to edit it like in his example.
Although there is still a Problem if you input a single number. The output would then be 0.
I would go for this Method:
public static int Greatest(string uinput)
{
List<int> numbers = new List<int>();
foreach(string str in uinput.Split(' '))
{
numbers.Add(int.Parse(str));
}
return numbers.Max();
}

Unexpected results for checking if a character is a symbol

I am creating a string extension to check if a string is all symbols or not however it is not working as I would expect it to, so far I have the following:
// Class for: String extensions
public static class StringExtension
{
// Method for: Determining if a string contains only symbols
public static bool ContainsOnlySymbols(this String inputString)
{
// Identifiers used are:
bool containsMore = false;
// Go through the characters of the input string checking for symbols
foreach (char character in inputString.ToCharArray())
{
// This line needs || Char.IsPunctuation(character) also
// Credit: #asantaballa
containsMore = Char.IsSymbol(character) ? false : true;
if (containsMore)
{
return containsMore;
}
}
// Return the results
return containsMore; // Edited after answer: <-- mistake here
}
}
Now if I use this extension on the following two strings I get the opposite of what I expect to see:
string testString = "!=";
I expect this to be all symbols, but
I expect: testString.ContainsOnlySymbols() => true
I get: testString.ContainsOnlySymbols() => false
Now if I use the next test string:
string testString = "Starts with";
I expect this to have no symbols
I expect: testString.ContainsOnlySymbols() => false
I get: testString.ContainsOnlySymbols() => true
A couple problems:
In your loop, you are really only getting the option related to the last character. And or clause should take care of it.
containsMore = containsMore || !(Char.IsSymbol(character) || Char.IsPunctuation(character));
Then, you need a not at the end. If it doesn't contain more, then its only symbols
return ! containsMore;
You might want a special case for how to handle empty strings too. Not sure how you want to handle that. That will be your choice if an empty string should return true or false.
You can accomplish this with a one-liner. See these examples.
string x = "##=";
string z = "1234";
string w = "1234#";
bool b = Array.TrueForAll(x.ToCharArray(), y => (Char.IsSymbol(y) || Char.IsPunctuation(y))); // true
bool c = Array.TrueForAll(z.ToCharArray(), y => (Char.IsSymbol(y) || Char.IsPunctuation(y))); // false
bool e = Array.TrueForAll(w.ToCharArray(), y => (Char.IsSymbol(y) || Char.IsPunctuation(y))); // false
Checking all chars if all isSymbol or Punctuation. we return true here.
public static bool ContainsOnlySymbols(this String inputString)
{
return inputString.ToCharArray().All(x => Char.IsSymbol(x) || Char.IsPunctuation(x));
}
Test:
string testString = "Starts with"; // => false
string testString = "!="; // => true
string testString = "##"; // => true
string testString = "!Starts with"; // => false
I believe the IsSymbol method checks for a very specific set of character. You may want to do:
containsMore = (Char.IsSymbol(character) || Char.IsPunctuation(character)) ? false : true;
Wrote a quick program to show results for character and does show symptom. Might even be that all you need for your app is IsPunctuation.
33/!: IsSymbol=False, IsPunctuation=True
Program
using System;
namespace csharptestchis
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i <= 255; i++)
{
char ch = (char)i;
bool isSymbol = Char.IsSymbol(ch);
bool isPunctuation = Char.IsPunctuation(ch);
Console.WriteLine($"{i}/{ch}: IsSymbol={isSymbol}, IsPunctuation={isPunctuation} ");
}
}
}
}
Firstly, the idea is simple: you loop your string, if you meet a character non-symbol, return false. Until the end of string and you don't meet a character non-symbol. VoilĂ , return true.
public static bool ContainsOnlySymbols(string inputString)
{
// Identifiers used are:
bool containsMore = false;
// Go through the characters of the input string checking for symbols
foreach (char character in inputString)
{
containsMore = Char.IsSymbol(character) ? false : true;
if(!containsMore)
return false;
}
// Return the results
return true;
}
Secondly, there is a problem with your code, IsSymbol returns true only if your character is in these groups
MathSymbol, CurrencySymbol, ModifierSymbol, and OtherSymbol.
And fortunately, ! don't be in these groups. That means "!=" returns false.
So you must include others conditions like:
public static bool ContainsOnlySymbols(string inputString)
{
// Go through the characters of the input string checking for symbols
return inputString.All(c => Char.IsSymbol(c) || Char.IsPunctuation(c));
}
Or you have to write your own method to determine what symbol is acceptable and what is not.
Or if a string doesn't contain digit and letter, it can be considered symbol. You can do
public static bool ContainsOnlySymbols(string inputString)
{
// Go through the characters of the input string checking for symbols
return !inputString.Any(c => Char.IsLetterOrDigit(c));
}

C# - Dealing with contradictions in string.replace

I'm getting started with C# and programming in general and I've been playing with the "if" statements, arrays and generally getting to grips with things. However, one thing that has stumped me is how you would go about performing an replace operation which is inherently contradictory.
IE: I have string "AAABBB" but I want to search through my text and replace all "A"s with "B"s and vice-versa. So my intended output would be "BBBAAA".
I'm currently trying to use string.replace and if statements but it's not working (it follows the order of the statements, so in the above examples I'd get all "A" or all "B".
Code examples:
if (string.Contains("a"));
{
string = string.Replace("a", "b");
}
if (string.Contains("b"));
{
string = string.Replace("b", "a");
}
Any help would be super welcome!
If you're always replacing one character with another, it's probably simplest to convert it to a char[] and go through it one character at a time, fixing each one appropriately - rather than doing "all the As" and then "all the Bs".
public static string PerformReplacements(string text)
{
char[] chars = text.ToCharArray();
for (int i = 0; i < chars.Length; i++)
{
switch (chars[i])
{
case 'A':
chars[i] = 'B';
break;
case 'B':
chars[i] = 'A';
break;
}
}
return new string(chars);
}
Consider using Linq:
s = new string(s.Select(x => x == 'A' ? 'B' : x == 'B' ? 'A' : x).ToArray());
The reason why this fails is because all A's are first replaced by B's but then back to A's.
A generic way to solve this is the following:
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
public class Foo {
public static string ParallelReplace (string text, char[] fromc, char[] toc) {
Contract.Requires(text != null);
Contract.Requires(fromc != null);
Contract.Requires(toc != null)
Contract.Requires(fromc.Length == toc.Length);
Contract.Ensures(Contract.Result<string>().Length == text.Length);
Array.Sort(fromc,toc);
StringBuilder sb = new StringBuilder();
foreach(char c in text) {
int i = Array.BinarySearch(fromc,c);
if(i >= 0) {
sb.Append(toc[i]);
} else {
sb.Append(c);
}
}
return sb.ToString();
}
}
Demo with csharp interactive shell:
csharp> Foo.ParallelReplace("ABasdsadsadaABABB",new char[] {'b','a','s'},new char[] {'f','s','a'});
"ABsadasdasdsABABB"
This represents a mapping {b->f,a->s,s->a}. The method works in O(s*log(n)+n*log(n)), with s the length of the string and n the number of rules.
The Contract's are not necessary, but can help if one uses a static code analysis tool to prevent making errors.

Using the value found through '.contains'

This is a console unit conversion program which takes in a measurement unit (such as pounds, ounces) specified by the user, followed by a second measurement unit, and finally a value to convert from one unit to the other.
string userUnit1, userUnit2 = "";
int userValue = 0;
The units that can be used are specified in a text file, the contents of which are split into a string array (called 'units') in the program. The text file also contains the 'conversion value' between the two units. In the text file, they appear like so: ounce,gram,28.0 (since there's 28 grams in one ounce, this value is also put into the array).
The units that the user enters are checked with the following snippet of code:
double result = 0;
if (units.Contains(userUnit1) && units.Contains(userUnit2))
{
//??? Something like:
//result = userValue */ value in the array element;
}
The basic IF statement above checks the array for the units that the user enters, and what I want to do is use the conversion value in that array element to convert the number that the user entered. How would I go about this?
EDIT: This is the code for storing the split text into the array.
using (read = new StreamReader("C:/Users/Sam Smith/Desktop/convert.txt"))
{
while (!read.EndOfStream)
{
lineFromFile = read.ReadLine();
lineFromFile.Split(' ');
foreach (int i in lineFromFile)
{
count++;
if (count % 3 == 0)
{
units.Add(lineFromFile);
}
}
}
}
Something like this?
String path = #"C:\Users\Sam Smith\Desktop\convert.txt";
var lines = System.IO.File.ReadLines(path)
.Where(l => l.Contains(userUnit1) && l.Contains(userUnit2));
if(lines.Any())
{
var unit = lines.First();
var parts = unit.Split(new char[]{','}, StringSplitOptions.RemoveEmptyEntries);
var result = userValue * double.Parse(parts[2]);
}
You need a reference to the System.Linq namespace for Enumerable.Where. You can use File.ReadLines to retrieve all lines in a file as IEnumerable<string>.
Use the IndexOf method into unit1Index and unit2Index and compare to -1 to assure they were found, or use the FirstOrDefault extension method and compare to null.
It also sounds like you should make a dictionay out of it, which would simplify this with TryGetValue.
var conversionsDic = File.ReadAllLines("conversions.txt")
.Select(l => l.Split(' '))
.ToDictionary(ls => ls[0] + ls[1], ls => double.Parse(ls[2]));
double conversion;
if (conversionsDic.TryGetValue(userUnit1 + userUnit2, out conversion))
{
// use 'conversion' to do whatever
var result = userValue * conversion;
}

Categories

Resources