I have a string aaaaabbbbbccccc I have a dictionary that has certain rules
Dictionary<string, string> rules = new Dictionary<string, string>();
rules.Add("abc", "aab");
rules.Add("ac", "ba");
rules.Add("cb", "cc");
This means if string has abc it will be replaced with aab but string doesn't have any rules that match. So, I am creating a new string out of the old string based on these rules. For instance, if I rearrange the old string to abcabcabcabcabc then rule can be applied. But I am stuck at rearranging. I tried using IndexOf and Remove functions but I didn't get the positive output. This is my code
string s;
s = "aaaaabbbbbccccc";
string newString = "";
int ia, ib, ic;
//Formulating rule 1
if (s.Contains("a") && s.Contains("b") && s.Contains("c"))
{
ia = s.IndexOf("a");
ib = s.IndexOf("b");
ic = s.IndexOf("c");
if (ia < ib && ib < ic)
{
newString += "abc";
s.Remove(ia, 1);
s.Remove(ib, 1);
s.Remove(ic, 1);
}
}
Console.WriteLine("New String " + newString);
Console.WriteLine("Old String " + s);
I am getting
New String abc
Old String aaaaabbbbbccccc //Which is wrong.
Can anyone help what I am doing wrong or is there any better way?
Your explanation does not match your code.
In your explanation, you say that aaaaabbbbbccccc does not match any rule (e.g. the "abc" rule). However, in your code, you are not checking if it contains "abc", but rather that it contain "a" and "b" and "c", not necessarily as a single chunk:
if (s.Contains("a") && s.Contains("b") && s.Contains("c"))
This is an important difference:
aaaaabbbbbccccc does not contain abc (rule does not apply)
aaaaabbbbbccccc does contain a, b and c (rule does apply)
You're contradicting yourself. Which is correct here, the code or your explanation? Does the rule check for the exact string, or does it check for all characters separately?
Awkward string manipulation.
Based on your code; I infer that you're not experienced with some very common string operations (no offense intended). Unless you contradict me, I'm going to assume that your explanation is correct and your code is not.
Checking if a string contains a substring:
As I explained previously, there is an important difference between checking is a string contains a substring:
s.Contains("abc") //1
and checking if a string contains each individual character of a substring:
s.Contains("a") && s.Contains("b") && s.Contains("c") //2
As a more practical example; does my username (Flater) contain the substring "Fertla"?
If you use the logic in //1, the answer is no.
If you use the logic in //2, the answer is yes.
Based on your explanation, you should be using //1
Replacing a string:
This means if string has abc it will be replaced with aab
There is a very simple method for this:
s.Replace("abc", "aab");
Some examples:
abcdef becomes aabdef
abcabcabc becomes aabaabaab (it replaces all occurrences)
uvwxyz becomes uvwxyz (if it doesn't occur, nothing gets replaced)
Take note of the second and third bullet point.
String.Replace() will replace all occurrences, in case the substring occurs more than once. Based on your explanation, I assume this is what you want (if it's possible for a substring to occur more than once, to begin with).
If the substring is not found, String.Replace() will give you the same output as its input. Nothing will be changed. This means that you can execute your rule on your string without needing to check if the substring exists:
If it does exist, then your value will be replaced; just like you want it to happen.
If it does not exist, then nothing will happen; which is also what you want to happen.
You can dramatically simplify your code!
Create your dictionary;
Dictionary<string, string> rules = new Dictionary<string, string>();
rules.Add("abc", "aab");
rules.Add("ac", "ba");
rules.Add("cb", "cc");
Define your string:
string s = "aaaaaaabbbbbbccccccc";
And the rest is easy enough:
foreach(var rule in rules)
{
s = s.Replace(rule.Key, rule.Value);
}
This will try to perform a replace for every rule that you've defined. If it finds rules that are applicable; then it will replace the values.
Note
This code assumes that your replace values do not collide. If you do want to avoid collisions, you will have to check if substrings of all defined rules exist (Contains()) before actually replacing a value.
I have a really hard time understanding you requierment, but here is a solution may you tell me if this is even close to what you want?
private static string WeirdArrangement (string input)
{
//string input = "aabbcc[aczç_eyvur]";
string validChars = "abc";
string pattern = "abc"; // Must be a composition of all valid char
var invalidChars = input.Where(c => !validChars.Contains(c));
var validOccurences = input.Where(c => validChars.Contains(c))
.GroupBy(c => c)
.Select(c => new { Char = c.Key, Count = c.Count() });
var minPattern = validOccurences.Min(o => o.Count);
// Build time
StringBuilder builder = new StringBuilder();
//new StringBuilder(pattern.Length * minPattern + invalidChars.Count() + leftoverCount);
// X time partern
for (int i = 0; i < minPattern; i++) builder.Append(pattern);
//Rest of the validOccurences
foreach (var charOccurency in validOccurences)
{
for (int i = minPattern; i < charOccurency.Count; i++) builder.Append(charOccurency.Char);
}
//Invalid char
foreach (var invalidChar in invalidChars)
{
builder.Append(invalidChar);
};
return builder.ToString();
}
Related
Why comparing characters with .Equals always returns false?
char letter = 'a';
Console.WriteLine(letter.Equals("a")); // false
Overall I'm trying to write an English - Morse Code translator. I run into a problem comparing char values which shown above. I began with a foreach to analyze all the characters from a ReadLine() input, by using the WriteLine() method, all the characters were transposed fine, but when trying to compare them using the .Equals() method, no matter what I did, it always output false when trying to compare chars.
I have used the .Equals() method with other strings successfully, but it seems to not work with my chars.
using System;
public class MorseCode {
public static void Main (string[] args) {
Console.WriteLine ("Hello, write anything to convert it to morse code!");
var input = Console.ReadLine();
foreach (char letter in input) {
if(letter.Equals("a")) {
Console.WriteLine("Its A - live");
}
Console.WriteLine(letter);
}
var morseTranslation = "";
foreach (char letter in input) {
if(letter.Equals("a")) {
morseTranslation += ". _ - ";
}
if(letter.Equals("b")) {
morseTranslation += "_ . . . - ";
}
if(letter.Equals("c")) {
morseTranslation += "_ . _ . - ";
}
...
}
}
Console.WriteLine("In morse code, " + input + " is '"morseTranslation + "'");
}
}
At the beginning, I wrote the foreach to test if it recognized and ran the correct output, but in the end, when I wrote "sample" into the ReadLine(), it gave me :
Hello, write anything to convert it to morse code!
sample
s
a
m
p
l
e
When you do this:
var c = 'x';
var isEqual = c.Equals("x");
the result (isEqual) will always be false because it's comparing a string to a char. This would return true:
var isEqual = c.Equals('x');
The difference is that "x" is a string literal and 'x' is a char literal.
Part of what makes this confusing is that when you use an object's Equals method, it allows you to compare any type to any other type. So you could do this:
var x = 0;
var y = "y";
var isEqual = x.Equals(y);
...and the compiler will allow it, even though the comparison between int and string won't work. It will give you this warning:
When comparing value types like int or char with other values of the same type, we usually use ==, like
if (someChar == someOtherChar)
Then if you tried to do this:
if(someChar == "a")
It wouldn't compile. It would tell you that you're comparing a char to a string, and then it's easier because instead of running the program and looking for the error it just won't compile at all and it will tell you exactly where the problem is.
Just for the fun of it, here's another implementation.
public static class MorseCodeConverter
{
private static readonly Dictionary<char, string> Codes
= CreateMorseCodeDictionary();
public static string Convert(string input)
{
var lowerCase = input.ToLower();
var result = new StringBuilder();
foreach (var character in input)
{
if (Codes.ContainsKey(character))
result.Append(Codes[character]);
}
return result.ToString();
}
static Dictionary<char, string> CreateMorseCodeDictionary()
{
var result = new Dictionary<char, string>();
result.Add('a', ". _ - ");
result.Add('b', "_ . . . - ");
// add all the rest
return result;
}
}
One difference is that it's a class by itself without the console app. Then you can use it in a console app. Read the input from the keyboard and then call
MorseCodeConverter.Convert(input);
to get the result, and then you can print it to the console.a
Putting all of the characters in a dictionary means that instead of repeating the if/then you can just check to see if each character is in the dictionary.
It's important to remember that whilst the char and string keywords look reminiscant of eachother when looking at printed values you should note that they are not accomodated for in exactly the same way.
When you check a string you can use:
string s = "A";
if(s.Equals("A"))
{
//Do Something
}
However, the above will not work with a char. The difference between chars (value types) and strings (reference types) on a surface level is the use of access - single quote (apostrophe) vs quote.
To compare a char you can do this:
char s = 'A';
if(s.Equals('A'))
{
//Do Something
}
On a point relevant to your specific case however, morse code will only requre you to use a single case alphabet and as such when you try to compare against 'A' and 'a' you can call input.ToLower() to reduce your var (string) to all lower case so you don't need to cater for both upper and lower case alphabets.
It's good that you're aware of string comparissons and are not using direct value comparisson as this:
if (letter == 'a')
{
Console.WriteLine("Its A - live");
}
Would've allowed you to compare the char but it's bad practice as it may lead to lazy comparisson of strings in the same way and this:
if (letter == "a")
{
Console.WriteLine("Its A - live");
}
Is a non-representitive method of comparison for the purpose of comparing strings as it evaluates the reference not the direct value, see here
For char comparison you have to use single quote ' character not " this.
By the way it writes sample in decending order beacuse in your first foreach loop you write all letters in new line. SO below code will work for you:
using System;
public class MorseCode {
public static void Main (string[] args) {
Console.WriteLine ("Hello, write anything to convert it to morse code!");
var input = Console.ReadLine();
/*foreach (char letter in input) {
if(letter.Equals("a")) {
Console.WriteLine("Its A - live");
}
Console.WriteLine(letter);
}*/
var morseTranslation = "";
foreach (char letter in input) {
if(letter.Equals('a')) {
morseTranslation += ". _ - ";
}
if(letter.Equals('b')) {
morseTranslation += "_ . . . - ";
}
if(letter.Equals('c')) {
morseTranslation += "_ . _ . - ";
}
...
}
}
Console.WriteLine("In morse code, " + input + " is '"morseTranslation + "'");
}
}
In C#, you can compare strings like integers, that is with == operator. Equals is a method inherited from the object class, and normally implementations would make some type checks. char letter is (obviously) a character, while "a" is a single lettered string.
That's why it returns false.
You could use if (letter.Equals('a')) { ... }, or simpler if (letter == 'a') { ... }
Even simpler than that would be switch (letter) { case 'a': ...; break; ... }.
Or something that is more elegant but maybe too advanced yet for a beginner, using LINQ:
var validCharacters = "ABCDE...";
var codes = new string[] {
".-", "-...", "-.-.", "-..", ".", ...
};
var codes = input.ToUpper() // make uppercase
.ToCharArray() // explode string into single characters
.Select(validCharaters.IndexOf) // foreach element (i. e. character), get the result of "validCharacters.IndexOf",
// which equals the index of the morse code in the array "codes"
.Where(i => i > -1) // only take the indexes of characters that were found in "validCharacters"
.Select(i => codes[i]); // retrieve the matching entry from "codes" by index
// "codes" is now an IEnumerable<string>, a structure saying
// "I am a list of strings over which you can iterate,
// and I know how to generate the elements as you request them."
// Now concatenate all single codes to one long result string
var result = string.Join(" ", codes);
I'm searching for a solution to this case:
I have a Method inside a DLL that receive a string that contains some words as "placeholders/parameters" that will be replaced by a result of another specific method (inside dll too)
Too simplificate: It's a query string received as an argument to be on a method inside a DLL, where X word that matchs a specifc case, will be replaced.
My method receive a string that could be like this:
(on .exe app)
string str = "INSERT INTO mydb.mytable (id_field, description, complex_number) VALUES ('#GEN_COMPLEX_ID#','A complex solution', '#GEN_COMPLEX_ID#');"
MyDLLClass.MyMethod(str);
So, the problem is: if i replace the #GEN_COMPLEX_ID# on this string, wanting that a different should be on each match, it not will happen because the replaced executes the function in a single shot (not step by step). So, i wanna help to implement this: a step by step replace of any text (like Find some word, replace, than next ... replace ... next... etc.
Could you help me?
Thanks!
This works pretty well for me:
string yourOriginalString = "ab cd ab cd ab cd";
string pattern = "ab";
string yourNewDescription = "123";
int startingPositionOffset = 0;
int yourOriginalStringLength = yourOriginalString.Length;
MatchCollection match = Regex.Matches(yourOriginalString, pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);
foreach (Match m in match)
{
yourOriginalString = yourOriginalString.Substring(0, m.Index+startingPositionOffset) + yourNewDescription + yourOriginalString.Substring(m.Index + startingPositionOffset+ m.Length);
startingPositionOffset = yourOriginalString.Length - yourOriginalStringLength;
}
If what you're asking is how to replace each placeholder with a different value, you can do it using the Regex.Replace overload which accepts a MatchEvaluator delegate, and executes it for each match:
// conceptually, something like this (note that it's not checking if there are
// enough values in the replacementValues array)
static string ReplaceMultiple(
string input, string placeholder, IEnumerable<string> replacementValues)
{
var enumerator = replacementValues.GetEnumerator();
return Regex.Replace(input, placeholder,
m => { enumerator.MoveNext(); return enumerator.Current; });
}
This is, of course, presuming that all placeholders look the same.
Pseudo-code
var split = source.Split(placeholder); // create array of items without placeholders
var result = split[0]; // copy first item
for(int i = 1; i < result.Length; i++)
{
bool replace = ... // ask user
result += replace ? replacement : placeholder; // to put replacement or not to put
result += split[i]; // copy next item
}
you should use the split method like this
string [] placeholder = {"#Placeholder#"} ;
string[] request = cd.Split(placeholder, StringSplitOptions.RemoveEmptyEntries);
StringBuilder requetBuilding = new StringBuilder();
requetBuilding.Append(request[0]);
int index = 1;
requetBuilding.Append("Your place holder replacement");
requetBuilding.Append(request[index]);
index++; //next replacement
// requetBuilding.Append("Your next place holder replacement");
// requetBuilding.Append(request[index]);
I've been using C# String.Format for formatting numbers before like this (in this example I simply want to insert a space):
String.Format("{0:### ###}", 123456);
output:
"123 456"
In this particular case, the number is a string. My first thought was to simply parse it to a number, but it makes no sense in the context, and there must be a prettier way.
Following does not work, as ## looks for numbers
String.Format("{0:### ###}", "123456");
output:
"123456"
What is the string equivalent to # when formatting? The awesomeness of String.Format is still fairly new to me.
You have to parse the string to a number first.
int number = int.Parse("123456");
String.Format("{0:### ###}", number);
of course you could also use string methods but that's not as reliable and less safe:
string strNumber = "123456";
String.Format("{0} {1}", strNumber.Remove(3), strNumber.Substring(3));
As Heinzi pointed out, you can not have format specifier for string arguments.
So, instead of String.Format, you may use following:
string myNum="123456";
myNum=myNum.Insert(3," ");
Not very beautiful, and the extra work might outweigh the gains, but if the input is a string on that format, you could do:
var str = "123456";
var result = String.Format("{0} {1}", str.Substring(0,3), str.Substring(3));
string is not a IFormattable
Console.WriteLine("123456" is IFormattable); // False
Console.WriteLine(21321 is IFormattable); // True
No point to supply a format if the argument is not IFormattable only way is to convert your string to int or long
We're doing string manipulation, so we could always use a regex.
Adapted slightly from here:
class MyClass
{
static void Main(string[] args)
{
string sInput, sRegex;
// The string to search.
sInput = "123456789";
// The regular expression.
sRegex = "[0-9][0-9][0-9]";
Regex r = new Regex(sRegex);
MyClass c = new MyClass();
// Assign the replace method to the MatchEvaluator delegate.
MatchEvaluator myEvaluator = new MatchEvaluator(c.ReplaceNums);
// Replace matched characters using the delegate method.
sInput = r.Replace(sInput, myEvaluator);
// Write out the modified string.
Console.WriteLine(sInput);
}
public string ReplaceNums(Match m)
// Replace each Regex match with match + " "
{
return m.ToString()+" ";
}
}
How's that?
It's been ages since I used C# and I can't test, but this may work as a one-liner which may be "neater" if you only need it once:
sInput = Regex("[0-9][0-9][0-9]").Replace(sInput,MatchEvaluator(Match m => m.ToString()+" "));
There is no way to do what you want unless you parse the string first.
Based on your comments, you only really need a simple formatting so you are better off just implementing a small helper method and thats it. (IMHO it's not really a good idea to parse the string if it isn't logically a number; you can't really be sure that in the future the input string might not be a number at all.
I'd go for something similar to:
public static string Group(this string s, int groupSize = 3, char groupSeparator = ' ')
{
var formattedIdentifierBuilder = new StringBuilder();
for (int i = 0; i < s.Length; i++)
{
if (i != 0 && (s.Length - i) % groupSize == 0)
{
formattedIdentifierBuilder.Append(groupSeparator);
}
formattedIdentifierBuilder.Append(s[i]);
}
return formattedIdentifierBuilder.ToString();
}
EDIT: Generalized to generic grouping size and group separator.
The problem is that # is a Digit placeholder and it is specific to numeric formatting only. Hence, you can't use this on strings.
Either parse the string to a numeric, so the formatting rules apply, or use other methods to split the string in two.
string.Format("{0:### ###}", int.Parse("123456"));
So what I am trying to do is as follows :
example of a string is A4PC
I am trying to replace for example any occurance of "A" with "[A4]" so I would get and similar any occurance of "4" with "[A4]"
"[A4][A4]PC"
I tried doing a normal Replace on the string but found out I got
"[A[A4]]PC"
string badWordAllVariants =
restriction.Value.Replace("A", "[A4]").Replace("4", "[A4]")
since I have two A's in a row causing an issue.
So I was thinking it would be better rather than the replace on the string I need to do it on a character per character basis and then build up a string again.
Is there anyway in Linq or so to do something like this ?
You don't need any LINQ here - String.Replace works just fine:
string input = "AAPC";
string result = input.Replace("A", "[A4]"); // "[A4][A4]PC"
UPDATE: For your updated requirements I suggest to use regular expression replace
string input = "A4PC";
var result = Regex.Replace(input, "A|4", "[A4]"); // "[A4][A4]PC"
This works well for me:
string x = "AAPC";
string replace = x.Replace("A", "[A4]");
EDIT:
Based on the updated question, the issue is the second replacement. In order to replace multiple strings you will want to do this sequentially:
var original = "AAPC";
// add arbitrary room to allow for more new characters
StringBuilder resultString = new StringBuilder(original.Length + 10);
foreach (char currentChar in original.ToCharArray())
{
if (currentChar == 'A') resultString.Append("[A4]");
else if (currentChar == '4') resultString.Append("[A4]");
else resultString.Append(currentChar);
}
string result = resultString.ToString();
You can run this routine with any replacements you want to make (in this case the letters 'A' and '4' and it should work. If you would want to replace strings the code would be similar in structure but you would need to "look ahead" and probably use a for loop. Hopefully this helps!
By the way - you want to use a string builder here and not strings because strings are static which means space gets allocated every time you loop. (Not good!)
I think this should do the trick
string str = "AA4PC";
string result = Regex.Replace(str, #"(?<Before>[^A4]?)(?<Value>A|4)(?<After>[^A4]?)", (m) =>
{
string before = m.Groups["Before"].Value;
string after = m.Groups["After"].Value;
string value = m.Groups["Value"].Value;
if (before != "[" || after != "]")
{
return "[A4]";
}
return m.ToString();
});
It is going to replace A and 4 that hasn't been replaced yet for [A4].
I have the following main string which contains link Name and link URL. The name and url is combined with #;. I want to get the string of each link (name and url i.e. My web#?http://www.google.com), see example below
string teststring = "My web#;http://www.google.com My Web2#;http://www.bing.se Handbooks#;http://www.books.se/";
and I want to get three different strings using any string function:
My web#?http://www.google.com
My Web2#?http://www.bing.se
Handbooks#?http://www.books.de
So this looks like you want to split on the space after a #;, instead of splitting at #; itself. C# provides arbitrary length lookbehinds, which makes that quite easy. In fact, you should probably do the replacement of #; with #? first:
string teststring = "My web#;http://www.google.com My Web2#;http://www.bing.se Handbooks#;http://www.books.se/";
teststring = Regex.Replace(teststring, #"#;", "#?");
string[] substrings = Regex.Split(teststring, #"(?<=#\?\S*)\s+");
That's it:
foreach(var s in substrings)
Console.WriteLine(s);
Output:
My web#?http://www.google.com
My Web2#?http://www.bing.se
Handbooks#?http://www.books.se/
If you are worried that your input might already contain other #? that you don't want to split on, you can of course do the splitting first (using #; in the pattern) and then loop over substrings and do the replacement call inside the loop.
If these are constant strings, you can just use String.Substring. This will require you to count letters, which is a nuisance, in order to provide the right parameters, but it will work.
string string1 = teststring.Substring(0, 26).Replace(";","?");
If they aren't, things get complicated. You could almost do a split with " " as the delimiter, except that your site name has a space. Do any of the substrings in your data have constant features, such as domain endings (i.e. first .com, then .de, etc.) or something like that?
If you have any control on the input format, you may want to change it to be easy to parse, for example by using another separator between items, other than space.
If this format can't be changed, why not just implement the split in code? It's not as short as using a RegEx, but it might be actually easier for a reader to understand since the logic is straight forward.
This will almost definitely will be faster and cheaper in terms of memory usage.
An example for code that solves this would be:
static void Main(string[] args)
{
var testString = "My web#;http://www.google.com My Web2#;http://www.bing.se Handbooks#;http://www.books.se/";
foreach(var x in SplitAndFormatUrls(testString))
{
Console.WriteLine(x);
}
}
private static IEnumerable<string> SplitAndFormatUrls(string input)
{
var length = input.Length;
var last = 0;
var seenSeparator = false;
var previousChar = ' ';
for (var index = 0; index < length; index++)
{
var currentChar = input[index];
if ((currentChar == ' ' || index == length - 1) && seenSeparator)
{
var currentUrl = input.Substring(last, index - last);
yield return currentUrl.Replace("#;", "#?");
last = index + 1;
seenSeparator = false;
previousChar = ' ';
continue;
}
if (currentChar == ';' && previousChar == '#')
{
seenSeparator = true;
}
previousChar = currentChar;
}
}