Validate user's input against a range of acceptable values - c#

I have a situation where users can assign locations to items.
locations range from A1 to GG17
I guess it is like a row / column format.. Columns Range from A - GG. After Z will be STRICTLY AA, BB, CC, DD, EE, FF, GG.
And rows 1-17
i.e. users can assign location A1, A2, A3, B4, AA1, BB2... GG17
I want to validate the text the user enters when they assigning a location to stop them from adding HH1 or A20 for example..
I am able to achieve this from A - Z, 1-17, but am stumbling when the validation has to go past Z (AA, BB, CC...)
this is what I have for A-Z and 1-17 so far that works
List<char> allowedColumns = new List<char>();
List<int> allowedRows = new List<int>();
char column ='A';
int row = 1;
if (txtAssignLocation.Text != "")
{
while (column <= 'Z')
{
allowedColumns.Add(column);
column++;
}
while (row <= 17)
{
allowedRows.Add(row);
row++;
}
string enteredText = txtAssignLocation.Text;
string enteredColumn = enteredText.Substring(0, 1);
string enteredRow = enteredText.Substring(1);
if (!allowedColumns.Contains(Convert.ToChar(enteredColumn)) || !allowedRows.Contains(Convert.ToInt32(enteredRow)))
{
lblValidationError.Text = "Entered Location does not exist";
}
}
I am at a bit of a loss since Char cannot be more than one character and it and ++ cannot be applied to string

new Regex("^[A-Z][A-G]?[1-9][0-7]?$") validates most of the combinations. Preventing the numbers from 20 to 97 is a bit more tricky. But play around with regular expressions or just split the string and to an int.TryParse and make sure that the number part is <= 17.
EDIT
Yes, it was a bit quick'n'dirty. This one should do the trick:
#"^([A-Z]|([A-G])\2)([1-9]|1[0-7])$"
Example usage:
Regex regex = new Regex(#"^([A-Z]|([A-G])\2)([1-9]|1[0-7])$");
string userInput = "AG20";
bool ok = regex.Match(userInput).Success;

Based on what David said in the comments why not simply have a "hard coded" list of acceptable values since there aren't that many and simply check any entered text against this list?
// This will be a list of all valid locations from 'A1' to 'GG17'
var locations = new HashSet<string>();
// Add all values from 'A1' to 'Z17'
for (char c = 'A'; c <= 'Z'; c++)
{
for (int i = 1; i <= 17; i++)
{
locations.Add($"{c}{i}");
}
}
// Add the values for 'AA1' to 'GG17'
for (char c = 'A'; c <= 'G'; c++)
{
for (int i = 1; i <= 17; i++)
{
locations.Add($"{c}{c}{i}");
}
}
Now you can simply check against this list for validation:
locations.Contains("A1"); // true
locations.Contains("BB10"); // true
locations.Contains("AF7"); // false
locations.Contains("GA10"); // false

You can do this with just providing the maxColumn and maxRow in your method (or passed in as variables) instead of an array or other hardcoding of logic. You could enhance this with LINQ, but providing answer as a loop for clarity:
public static bool ValidateInput(string input)
{
//set maxColumn and maxRow sizes
const string maxColumn = "BG";
const int maxRow = 155;
//initialize input parse variables
string inputColumn = "";
int inputRow = int.MaxValue;
//use only upper-case (to aid in comparison)
input = input.ToUpper();
//parse input into inputColumn and inputRow
for (int i = 0; i < input.Length; i++)
{
if (char.IsDigit(input[i]))
{
inputRow = int.Parse(input.Substring(i));
break;
}
inputColumn += input[i];
}
//make sure the length is at least as long as maxColumn (for comparing single letter column to double-letter column, for example)
inputColumn.PadLeft(maxColumn.Length, ' ');
//return comparison result (inclusive to maxColum and maxRow)
return string.Compare(inputColumn, maxColumn) <= 0 && inputRow <= maxRow;
}
UPDATE: If you are interested in the LINQ version, here it is:
public static bool ValidateInput(string input)
{
//set maxColumn and maxRow sizes
const string maxColumn = "BG";
const int maxRow = 155;
//parse input into inputColumn and inputRow
string inputColumn = new string(input.TakeWhile(char.IsLetter).Select(char.ToUpper).ToArray());
int inputRow = int.Parse(new string(input.Skip(inputColumn.Length).ToArray()));
//make sure the length is at least as long as maxColumn (for comparing single letter column to double-letter column, for example)
inputColumn.PadLeft(maxColumn.Length, ' ');
//return comparison result (inclusive to maxColum and maxRow)
return string.Compare(inputColumn, maxColumn) <= 0 && inputRow <= maxRow;
}

Related

Xor operation between binary values in C#

My question is that i have a list of binary string like below :
list=<"1111","1010","1010","0011">
and an input string of binary value st1=1010. I want to Xor between :
st3=st1 Xor list<0>
then :
st3=st3 Xor list<1>
st3=st3Xor list <2>;
st3=st3 Xor list <3>;
where the operation will be st1 Xor with first key in keys list and the result Xor with the second key in keys list and the result Xor with the third key in keys list and so on . Can any one help me please?
i have tried this code but it does not work as i expected :
foreach (string k in keys)
{
string st1 = textBox1.text;
string st2 = k;
string st3;
st3 = "";
//i wanted to make the length of both strings st1 and st2 equal
//here if the length of st1 greater than st2
if (st1.Length > st2.Length)
{
int n = st1.Length - st2.Length;
string pad = "";
for (int j = 1; j <= n; j++)
{ pad += 0; }
string recover = pad.ToString() + st2;
//this is my Xor operation that i made for string values
for (int counter = 0; counter < st1.Length; counter++)
{
if (st1[counter] != recover[counter])
{
st3 = st3 + '1';
}
else
{ st3 = st3 + '0'; }
}
listBox4.Items.Add("Xor :" + st3.ToString());
}
//here if st1 is less than st2
else if (st1.Length < st2.Length)
{
int nn = st2.Length - st1.Length;
string ppad = "";
for (int j = 1; j <= nn; j++)
{
ppad += 0;
}
string recover = ppad.ToString() + st1;
for (int counter = 0; counter < st2.Length; counter++)
{
if (st2[counter] != recover[counter])
{
st3 = st3 + '1';
}
else
{ st3 = st3 + '0'; }
}
listBox4.Items.Add("Xor :" + st3.ToString());}
//here if st1 equal st2
else
{
for (int counter = 0; counter < st1.Length; counter++)
{
if (st1[counter] != st2[counter])
{
st3 = st3 + '1';
}
else
{ st3 = st3 + '0'; }
}
listBox4.Items.Add("Xor :" + st3.ToString());
}
}
the result that i do not expected is :
Here's one approach (Arbitrary length binary strings):
Convert the strings back to integers BigIntegers, so that we can actually get the utility of existing bitwise Xor operator (^).
Use LINQ's Aggregate to consecutively left-fold the seed value (st1) with the converted list with Xor.
Since you seem interested only in the lowest 4 bits, I've applied a mask, although if all your numbers are strictly 4 bits, this isn't actually necessary (since 0 Xor 0 stays 0)
You can convert the int back to a binary string with Convert.ToString(x, 2) and then PadLeft to replace any missing leading zeroes.
Edit - OP has changed the question from an example 4 bit number and the requirement is now to work with arbitrary length binary strings. This approach still works, but we'll need to use BigInteger (which still has an XOR ^ operator), but we need helpers to parse and format binary strings, as these aren't built into BigInteger. The BitMask and padding have also been removed, since the strings aren't fixed length - the result will have at most 1 leading zero:
var list = new List<string>{"10101010101010101101","1101010101010101011",
"1110111111010101101","11111111111111111111111111","10101010110101010101"};
var listNum = list.Select(l => BinaryStringToBigInteger(l));
var st1 = "000000001";
var seedNumber = BinaryStringToBigInteger(st1);
var chainedXors = listNum.Aggregate(seedNumber, (prev, next) => prev ^ next);
// Back to binary representation of the string
var resultString = chainedXors.ToBinaryString();
And because there's no native support for converting BigIntegers to / from binary strings, you'll need a conversion helper such as Douglas's one here:
BigInteger BinaryStringToBigInteger(string binString)
{
return binString.Aggregate(BigInteger.Zero, (prev, next) => prev * 2 + next - '0');
}
And for the reverse operation, ToBinaryString is from this helper.
32 Bit Integer answer
If the Binary strings are 32 bits or less, then a much simpler solution exists, since there are out of the box conversions to / from binary strings. The same approach should apply for 64 bit longs.
var list = new List<string>{"1111","1010","1010","0011","0011"};
var listNum = list.Select(l => Convert.ToInt32(l, 2));
// If you only want the last 4 bits. Change this to include as many bits as needed.
var bitMask = Convert.ToInt32("00000000000000000000000000001111", 2);
var st1 = "1010";
var someNum = Convert.ToInt32(st1, 2);
var chainedXors = listNum.Aggregate(someNum, (prev, next) => prev ^ next);
// If you need the result back as a 4 bit binary-string, zero padded
var resultString = Convert.ToString(chainedXors & bitMask, 2)
.PadLeft(4, '0');
Try this code:
static void Main(string[] args)
{
List<string> list = new List<string> { "1111", "1010", "1010", "0011" };
string st1 = "1010";
foreach (string item in list)
{
st1 = XorBins(st1, item);
Console.WriteLine(st1);
}
Console.ReadKey();
}
private static string XorBins(string bin1, string bin2)
{
int len = Math.Max(bin1.Length, bin2.Length);
string res = "";
bin1 = bin1.PadLeft(len, '0');
bin2 = bin2.PadLeft(len, '0');
for (int i = 0; i < len; i++)
res += bin1[i] == bin2[i] ? '0' : '1';
return res;
}
Here is an Xor method for you:
public static string Xor(string s1, string s2) {
// find the length of the longest of the two strings
int longest = Math.Max(s1.Length, s2.Length);
// pad both strings to that length. You don't need to write the padding
// logic yourself! There is already a method that does that!
string first = s1.PadLeft(longest, '0');
string second = s2.PadLeft(longest, '0');
// Enumerable.Zip takes two sequences (in this case sequences of char, aka strings)
// and lets you transform each element in the sequences. Here what
// I did was check if the two chars are not equal, in which case
// I transform the two elements to a 1, 0 otherwise
return string.Join("", Enumerable.Zip(first, second, (x, y) => x != y ? '1' : '0'));
}
You can use it like this:
Xor("1111", "1010") // 0101

What's the fastest way to remove characters from an alpha-numeric string?

Say we have the following strings that we pass as parameters to the function below:
string sString = "S104";
string sString2 = "AS105";
string sString3 = "ASRVT106";
I want to be able to extract the numbers from the string to place them in an int variable. Is there a quicker and/or more efficient way of removing the letters from the strings than the following code?: (*These strings will be populated dynamically at runtime - they are not assigned values at construction.)
Code:
public GetID(string sCustomTag = null)
{
m_sCustomTag = sCustomTag;
try {
m_lID = Convert.ToInt32(m_sCustomTag); }
catch{
try{
int iSubIndex = 0;
char[] subString = sCustomTag.ToCharArray();
//ITERATE THROUGH THE CHAR ARRAY
for (int i = 0; i < subString.Count(); i++)
{
for (int j = 0; j < 10; j++)
{
if (subString[i] == j)
{
iSubIndex = i;
goto createID;
}
}
}
createID: m_lID = Convert.ToInt32(m_sCustomTag.Substring(iSubIndex));
}
//IF NONE OF THAT WORKS...
catch(Exception e)
{
m_lID = 00000;
throw e;
}
}
}
}
I've done things like this before, but I'm not sure if there's a more efficient way to do it. If it was just going to be a single letter at the beginning, I could just set the subStringIndex to 1 every time, but the users can essentially put in whatever they want. Generally, they will be formatted to a LETTER-then-NUMBER format, but if they don't, or they want to put in multiple letters like sString2 or sString3, then I need to be able to compensate for that. Furthermore, if the user puts in some whacked-out, non-traditional format like string sString 4 = S51A24;, is there a way to just remove any and all letters from the string?
I've looked about, and can't find anything on MSDN or Google. Any help or links to it are greatly appreciated!
You can use a regular expression. It's not necessarily faster, but it's more concise.
string sString = "S104";
string sString2 = "AS105";
string sString3 = "ASRVT106";
var re = new Regex(#"\d+");
Console.WriteLine(re.Match(sString).Value); // 104
Console.WriteLine(re.Match(sString2).Value); // 105
Console.WriteLine(re.Match(sString3).Value); // 106
You can use a Regex, but it's probably faster to just do:
public int ExtractInteger(string str)
{
var sb = new StringBuilder();
for (int i = 0; i < str.Length; i++)
if(Char.IsDigit(str[i])) sb.Append(str[i]);
return int.Parse(sb.ToString());
}
You can simplify further with some LINQ at the expense of a small performance penalty:
public int ExtractInteger(string str)
{
return int.Parse(new String(str.Where(c=>Char.IsDigit(c)).ToArray()));
}
Now, if you only want to parse the first sequence of consecutive digits, do this instead:
public int ExtractInteger(string str)
{
return int.Parse(new String(str.SkipWhile(c=>!Char.IsDigit(c)).TakeWhile(c=>Char.IsDigit(c)).ToArray()));
}
Fastest is to parse the string without removing anything:
var s = "S51A24";
int m_lID = 0;
for (int i = 0; i < s.Length; i++)
{
int d = s[i] - '0';
if ((uint)d < 10)
m_lID = m_lID * 10 + d;
}
Debug.Print(m_lID + ""); // 5124
string removeLetters(string s)
{
for (int i = 0; i < s.Length; i++)
{
char c = s[i];
if (IsEnglishLetter(c))
{
s = s.Remove(i, 1);
}
}
return s;
}
bool IsEnglishLetter(char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
While you asked "what's the fastest way to remove characters..." what you really appear to be asking is "how do I create an integer by extracting only the digits from the string"?
Going with this assumption, your first call to Convert.ToInt32 will be slow for the case where you have other than digits because of the exception throwing.
Let's try another approach. Let's think about each of the cases.
The string always starts with a series of digits (e.g. 123ABC => 123)
The string always ends with a series of digits (e.g. ABC123 => 123)
A string has a series of contiguous digits in the middle (e.g. AB123C ==> 123)
The digits are possibly noncontiguous (e.g. A77C12 => 7712)
Case 4 is the "safest" assumption (after all, it is a superset of Case 1, 2 and 3. So, we need an algorithm for that. As a bonus I'll provide algorithms specialized to the other cases.
The Main Algorithm, All Cases
Using in-place unsafe iteration of the characters of the string, which uses fixed, we can extract digits and convert them to a single number without the data copy in ToCharArray(). We can also avoid the allocations of, say, a StringBuilder implementation and a possibly slow regex solution.
NOTE: This is valid C# code though it's using pointers. It does look like C++, but I assure you it's C#.
public static unsafe int GetNumberForwardFullScan(string s)
{
int value = 0;
fixed (char* pString = s)
{
var pChar = pString;
for (int i = 0; i != s.Length; i++, pChar++)
{
// this just means if the char is not between 0-9, we exit the loop (i.e. stop calculating the integer)
if (*pChar < '0' || *pChar > '9')
continue;
// running recalculation of the integer
value = value * 10 + *pChar - '0';
}
}
return value;
}
Running this against any of the inputs: "AS106RVT", "ASRVT106", "106ASRVT", or "1AS0RVT6" results in pulling out 1, 0, 6 and calculating on each digit as
0*10 + 1 == 1
1*10 + 0 == 10
10*10 + 6 == 106
Case 1 Only Algorithm (Digits at Start of String)
This algorithm is identical to the one above, but instead of continue we can break as soon as we reach a non-digit. This would be much faster if we can assume all the inputs start with digits and the strings are long.
Case 2 Only Algorithm (Digits at End of String)
This is almost the same as Case 1 Only except you have to
iterate from the end of the string to the beginning (aka backwards) stopping on the first non-digit
change the calculation to sum up powers of ten.
Both of those are a bit tricky, so here's what that looks like
public static unsafe int GetNumberBackward(string s)
{
int value = 0;
fixed (char* pString = s)
{
char* pChar = pString + s.Length - 1;
for (int i = 0; i != -1; i++, pChar--)
{
if (*pChar < '0' || *pChar > '9')
break;
value = (*pChar - '0') * (int)Math.Pow(10, i) + value;
}
}
return value;
}
So each of the iteration of the calculation looks like
6*100 + 0 == 6
0*101 + 6 == 6
1*102 + 6 == 106
While I used Math.Pow in these examples, you can find integer only versions that might be faster.
Cases 1-3 Only (i.e. All Digits Contiguous Somewhere in the String
This algorithm says to
Scan all non-digits
Then scan only digits
First non-digit after that, stop
It would look like
public static unsafe int GetContiguousDigits(string s)
{
int value = 0;
fixed (char* pString = s)
{
var pChar = pString;
// skip non-digits
int i = 0;
for (; i != s.Length; i++, pChar++)
if (*pChar >= '0' && *pChar <= '9')
break;
for (; i != s.Length; i++, pChar++)
{
if (*pChar < '0' || *pChar > '9')
break;
value = value * 10 + *pChar - '0';
}
}
return value;
}

C# char Conversion/Append to/with int, int64, string

Lately I have been working through Project Euler, specifically
https://projecteuler.net/problem=4
I create to arrays
Multiply them together
Convert the number in a CharArry
Compare the numbers
If true, my problem arises
I attempt to convert the char back to an int, or long, or string,
and
I have attempted to append the char to an int, or long, or string, or whatever
void Main()
{
int[] arrOne = new int[900]; // Initializing Array One
int[] arrTwo = new int[900]; // Initializing Array Two
Console.WriteLine(PopulateAndConvert(arrOne, arrTwo)); // Sending info into class
}
int PopulateAndConvert(int[] a, int[] b)
{
char[] c = new char[1]; // char used to store tested number
//string[] m = new string[a.Length*b.Length];
long l = 0; // Used for testing code
for(int i = 0; i < a.Length; i++) // Populating Arrays One and Two
{
a[i] = i + 100;
b[i] = i + 100;
}
for(int j = a.Length-1; j >= 0; j--) // Beginning for-loops for multiplication and testing
{
//Console.WriteLine(j);
for(int k = b.Length-1; k >= 0; k--) // Second part of for-loop previously mentioned
{
//Console.WriteLine(k);
c = (a[j] * b[k]).ToString().ToCharArray(); // Where the math and conversion happens
//Console.WriteLine(c);
if(c.Length > 5) // Checking if digit of product is greater than 5
{
if((c[0] == c[c.Length-1]) && // Comparing first and second half of product
(c[1] == c[c.Length-2]) &&
(c[2] == c[c.Length-3]))
{
/*for(int n = 0; n < c.Length; n++) // Last tidbit of code that was being attempted
sb[l].Append(Convert.ToInt32(c[0]));
l++;
Console.WriteLine(sb); */
}
}
else if (c.Length < 5) // Product with less than 6 digits go here
{
if((Convert.ToInt32(c[0]) == Convert.ToInt32(c[4])) &&
(Convert.ToInt32(c[1]) == Convert.ToInt32(c[3])))
{
//m[l] = Convert.ToChar(c); l++;
}
}
}
}
// Everything below was used to check the code that I have been trying to work through
// And to place the given products in a ascending or descending order
//foreach (char x in m)
// Console.WriteLine(m);
//IEnumerable<char> sortDescendingQuery =
// from num in c
// orderby num descending
// select num;
return 0;
}
After some time (resting the mind is always beneficial) I found a solution:
if(c.Length > 5) // Checking if digit of product is greater than 5
{
int[] n = new int[c.Length];
StringBuilder sb = new StringBuilder();
if((c[0] == c[c.Length-1]) && // Comparing first and second half of product
(c[1] == c[c.Length-2]) &&
(c[2] == c[c.Length-3]))
{
for(int l = 0; l < c.Length; l++) // Converting each value in the char array to a stringbuilder
{
sb.Append(Convert.ToInt32(new string(c[l], 1)));
}
m[q] = Int32.Parse(sb.ToString()); // Converting stringbuilder into string and then into a long
q++;
}
}
I had to convert each individual value within the char array c[] to a string, then an int, then append it to the string builder sb.
After that I then convert sb to a string (via ToString()) and Parse it to an int.
It seems like a long work around, but it works.
Now I need to Sort it numerically (another hurdle).

Get number inside of a string and do a loop

I want to get a number of a string, and separate the string and the number, and then, do a loop and call a method the number of times the string says.
The string has to have this structure: "ABJ3" (Only one number accepted and 3 characters before it)
This is my code, but it repeat hundred of times, I don't know why
int veces = 0;
for (int i = 0; i < m.Length; i++)
{
if (Char.IsDigit(m[i]))
veces = Convert.ToInt32(m[i]);
}
if (m.Length == 4)
{
for (int i = 0; i <= veces; i++)
{
m = m.Substring(0, 3);
operaciones(m, u, t);
Thread.Sleep(100);
}
}
operaciones(m,u,t);
if (u.Length >= 14)
{
u = u.Substring(0, 15);
}
Some help please?
You have to convert your m[i] ToString() right now you are sending the char value to Convert.ToInt32 and that is a much higher value (9 = 57 for example)
char t = '9';
int te = Convert.ToInt32(t.ToString());
Console.WriteLine(te);
This gives us a result of 9 but
char t = '9';
int te = Convert.ToInt32(t);
Console.WriteLine(te);
Gives us a result of 57
So you need to change
veces = Convert.ToInt32(m[i]);
to
veces = Convert.ToInt32(m[i].ToString());
Hope it helped.
Best regards //KH.
You cannot convert the digits like this. You're overwriting them and taking only the last one. Moreover, you're taking its ASCII code, not digit value. You have to extract all digits first then convert them:
int position = 0;
int veces = 0;
string temp = ""
for (int i = 0; i < m.Length; i++) {
if (Char.IsDigit(m[i]))
position = i;
else
break;
}
veces = Convert.ToInt32(m.SubString(0, i + 1));
Alternatively, you can use regex instead.

Iterating through the Alphabet - C# a-caz

I have a question about iterate through the Alphabet.
I would like to have a loop that begins with "a" and ends with "z". After that, the loop begins "aa" and count to "az". after that begins with "ba" up to "bz" and so on...
Anybody know some solution?
Thanks
EDIT: I forgot that I give a char "a" to the function then the function must return b. if u give "bnc" then the function must return "bnd"
First effort, with just a-z then aa-zz
public static IEnumerable<string> GetExcelColumns()
{
for (char c = 'a'; c <= 'z'; c++)
{
yield return c.ToString();
}
char[] chars = new char[2];
for (char high = 'a'; high <= 'z'; high++)
{
chars[0] = high;
for (char low = 'a'; low <= 'z'; low++)
{
chars[1] = low;
yield return new string(chars);
}
}
}
Note that this will stop at 'zz'. Of course, there's some ugly duplication here in terms of the loops. Fortunately, that's easy to fix - and it can be even more flexible, too:
Second attempt: more flexible alphabet
private const string Alphabet = "abcdefghijklmnopqrstuvwxyz";
public static IEnumerable<string> GetExcelColumns()
{
return GetExcelColumns(Alphabet);
}
public static IEnumerable<string> GetExcelColumns(string alphabet)
{
foreach(char c in alphabet)
{
yield return c.ToString();
}
char[] chars = new char[2];
foreach(char high in alphabet)
{
chars[0] = high;
foreach(char low in alphabet)
{
chars[1] = low;
yield return new string(chars);
}
}
}
Now if you want to generate just a, b, c, d, aa, ab, ac, ad, ba, ... you'd call GetExcelColumns("abcd").
Third attempt (revised further) - infinite sequence
public static IEnumerable<string> GetExcelColumns(string alphabet)
{
int length = 0;
char[] chars = null;
int[] indexes = null;
while (true)
{
int position = length-1;
// Try to increment the least significant
// value.
while (position >= 0)
{
indexes[position]++;
if (indexes[position] == alphabet.Length)
{
for (int i=position; i < length; i++)
{
indexes[i] = 0;
chars[i] = alphabet[0];
}
position--;
}
else
{
chars[position] = alphabet[indexes[position]];
break;
}
}
// If we got all the way to the start of the array,
// we need an extra value
if (position == -1)
{
length++;
chars = new char[length];
indexes = new int[length];
for (int i=0; i < length; i++)
{
chars[i] = alphabet[0];
}
}
yield return new string(chars);
}
}
It's possible that it would be cleaner code using recursion, but it wouldn't be as efficient.
Note that if you want to stop at a certain point, you can just use LINQ:
var query = GetExcelColumns().TakeWhile(x => x != "zzz");
"Restarting" the iterator
To restart the iterator from a given point, you could indeed use SkipWhile as suggested by thesoftwarejedi. That's fairly inefficient, of course. If you're able to keep any state between call, you can just keep the iterator (for either solution):
using (IEnumerator<string> iterator = GetExcelColumns())
{
iterator.MoveNext();
string firstAttempt = iterator.Current;
if (someCondition)
{
iterator.MoveNext();
string secondAttempt = iterator.Current;
// etc
}
}
Alternatively, you may well be able to structure your code to use a foreach anyway, just breaking out on the first value you can actually use.
Edit: Made it do exactly as the OP's latest edit wants
This is the simplest solution, and tested:
static void Main(string[] args)
{
Console.WriteLine(GetNextBase26("a"));
Console.WriteLine(GetNextBase26("bnc"));
}
private static string GetNextBase26(string a)
{
return Base26Sequence().SkipWhile(x => x != a).Skip(1).First();
}
private static IEnumerable<string> Base26Sequence()
{
long i = 0L;
while (true)
yield return Base26Encode(i++);
}
private static char[] base26Chars = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
private static string Base26Encode(Int64 value)
{
string returnValue = null;
do
{
returnValue = base26Chars[value % 26] + returnValue;
value /= 26;
} while (value-- != 0);
return returnValue;
}
The following populates a list with the required strings:
List<string> result = new List<string>();
for (char ch = 'a'; ch <= 'z'; ch++){
result.Add (ch.ToString());
}
for (char i = 'a'; i <= 'z'; i++)
{
for (char j = 'a'; j <= 'z'; j++)
{
result.Add (i.ToString() + j.ToString());
}
}
I know there are plenty of answers here, and one's been accepted, but IMO they all make it harder than it needs to be. I think the following is simpler and cleaner:
static string NextColumn(string column){
char[] c = column.ToCharArray();
for(int i = c.Length - 1; i >= 0; i--){
if(char.ToUpper(c[i]++) < 'Z')
break;
c[i] -= (char)26;
if(i == 0)
return "A" + new string(c);
}
return new string(c);
}
Note that this doesn't do any input validation. If you don't trust your callers, you should add an IsNullOrEmpty check at the beginning, and a c[i] >= 'A' && c[i] <= 'Z' || c[i] >= 'a' && c[i] <= 'z' check at the top of the loop. Or just leave it be and let it be GIGO.
You may also find use for these companion functions:
static string GetColumnName(int index){
StringBuilder txt = new StringBuilder();
txt.Append((char)('A' + index % 26));
//txt.Append((char)('A' + --index % 26));
while((index /= 26) > 0)
txt.Insert(0, (char)('A' + --index % 26));
return txt.ToString();
}
static int GetColumnIndex(string name){
int rtn = 0;
foreach(char c in name)
rtn = rtn * 26 + (char.ToUpper(c) - '#');
return rtn - 1;
//return rtn;
}
These two functions are zero-based. That is, "A" = 0, "Z" = 25, "AA" = 26, etc. To make them one-based (like Excel's COM interface), remove the line above the commented line in each function, and uncomment those lines.
As with the NextColumn function, these functions don't validate their inputs. Both with give you garbage if that's what they get.
Here’s what I came up with.
/// <summary>
/// Return an incremented alphabtical string
/// </summary>
/// <param name="letter">The string to be incremented</param>
/// <returns>the incremented string</returns>
public static string NextLetter(string letter)
{
const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (!string.IsNullOrEmpty(letter))
{
char lastLetterInString = letter[letter.Length - 1];
// if the last letter in the string is the last letter of the alphabet
if (alphabet.IndexOf(lastLetterInString) == alphabet.Length - 1)
{
//replace the last letter in the string with the first leter of the alphbat and get the next letter for the rest of the string
return NextLetter(letter.Substring(0, letter.Length - 1)) + alphabet[0];
}
else
{
// replace the last letter in the string with the proceeding letter of the alphabet
return letter.Remove(letter.Length-1).Insert(letter.Length-1, (alphabet[alphabet.IndexOf(letter[letter.Length-1])+1]).ToString() );
}
}
//return the first letter of the alphabet
return alphabet[0].ToString();
}
just curious , why not just
private string alphRecursive(int c) {
var alphabet = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
if (c >= alphabet.Length) {
return alphRecursive(c/alphabet.Length) + alphabet[c%alphabet.Length];
} else {
return "" + alphabet[c%alphabet.Length];
}
}
This is like displaying an int, only using base 26 in stead of base 10. Try the following algorithm to find the nth entry of the array
q = n div 26;
r = n mod 26;
s = '';
while (q > 0 || r > 0) {
s = alphabet[r] + s;
q = q div 26;
r = q mod 26;
}
Of course, if you want the first n entries, this is not the most efficient solution. In this case, try something like daniel's solution.
I gave this a go and came up with this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Alphabetty
{
class Program
{
const string alphabet = "abcdefghijklmnopqrstuvwxyz";
static int cursor = 0;
static int prefixCursor;
static string prefix = string.Empty;
static bool done = false;
static void Main(string[] args)
{
string s = string.Empty;
while (s != "Done")
{
s = GetNextString();
Console.WriteLine(s);
}
Console.ReadKey();
}
static string GetNextString()
{
if (done) return "Done";
char? nextLetter = GetNextLetter(ref cursor);
if (nextLetter == null)
{
char? nextPrefixLetter = GetNextLetter(ref prefixCursor);
if(nextPrefixLetter == null)
{
done = true;
return "Done";
}
prefix = nextPrefixLetter.Value.ToString();
nextLetter = GetNextLetter(ref cursor);
}
return prefix + nextLetter;
}
static char? GetNextLetter(ref int letterCursor)
{
if (letterCursor == alphabet.Length)
{
letterCursor = 0;
return null;
}
char c = alphabet[letterCursor];
letterCursor++;
return c;
}
}
}
Here is something I had cooked up that may be similar. I was experimenting with iteration counts in order to design a numbering schema that was as small as possible, yet gave me enough uniqueness.
I knew that each time a added an Alpha character, it would increase the possibilities 26x but I wasn't sure how many letters, numbers, or the pattern I wanted to use.
That lead me to the code below. Basically you pass it an AlphaNumber string, and every position that has a Letter, would eventually increment to "z\Z" and every position that had a Number, would eventually increment to "9".
So you can call it 1 of two ways..
//This would give you the next Itteration... (H3reIsaStup4dExamplf)
string myNextValue = IncrementAlphaNumericValue("H3reIsaStup4dExample")
//Or Loop it resulting eventually as "Z9zzZzzZzzz9zZzzzzzz"
string myNextValue = "H3reIsaStup4dExample"
while (myNextValue != null)
{
myNextValue = IncrementAlphaNumericValue(myNextValue)
//And of course do something with this like write it out
}
(For me, I was doing something like "1AA000")
public string IncrementAlphaNumericValue(string Value)
{
//We only allow Characters a-b, A-Z, 0-9
if (System.Text.RegularExpressions.Regex.IsMatch(Value, "^[a-zA-Z0-9]+$") == false)
{
throw new Exception("Invalid Character: Must be a-Z or 0-9");
}
//We work with each Character so it's best to convert the string to a char array for incrementing
char[] myCharacterArray = Value.ToCharArray();
//So what we do here is step backwards through the Characters and increment the first one we can.
for (Int32 myCharIndex = myCharacterArray.Length - 1; myCharIndex >= 0; myCharIndex--)
{
//Converts the Character to it's ASCII value
Int32 myCharValue = Convert.ToInt32(myCharacterArray[myCharIndex]);
//We only Increment this Character Position, if it is not already at it's Max value (Z = 90, z = 122, 57 = 9)
if (myCharValue != 57 && myCharValue != 90 && myCharValue != 122)
{
myCharacterArray[myCharIndex]++;
//Now that we have Incremented the Character, we "reset" all the values to the right of it
for (Int32 myResetIndex = myCharIndex + 1; myResetIndex < myCharacterArray.Length; myResetIndex++)
{
myCharValue = Convert.ToInt32(myCharacterArray[myResetIndex]);
if (myCharValue >= 65 && myCharValue <= 90)
{
myCharacterArray[myResetIndex] = 'A';
}
else if (myCharValue >= 97 && myCharValue <= 122)
{
myCharacterArray[myResetIndex] = 'a';
}
else if (myCharValue >= 48 && myCharValue <= 57)
{
myCharacterArray[myResetIndex] = '0';
}
}
//Now we just return an new Value
return new string(myCharacterArray);
}
}
//If we got through the Character Loop and were not able to increment anything, we retun a NULL.
return null;
}
Here's my attempt using recursion:
public static void PrintAlphabet(string alphabet, string prefix)
{
for (int i = 0; i < alphabet.Length; i++) {
Console.WriteLine(prefix + alphabet[i].ToString());
}
if (prefix.Length < alphabet.Length - 1) {
for (int i = 0; i < alphabet.Length; i++) {
PrintAlphabet(alphabet, prefix + alphabet[i]);
}
}
}
Then simply call PrintAlphabet("abcd", "");

Categories

Resources