Say I have a string like
Line 1
Line 2
I want to turn this string 90 degrees clockwise, so that it becomes
LL
ii
nn
ee
12
The rotation needs to be performed only once such that it is in effect 'turning lines into columns.' Performing it twice should give the original string. (If it was truly rotating by 90 degrees, it would have to be repeated four times to arrive back at the original.)
using System;
using System.Collections;
using System.Collections.Generic;
namespace rotateString
{
class Program
{
static void Main(string[] args)
{
var strings = new string[] { "Line 1", "Line 2" };
var lines = new List<string>();
var done = false;
var j = 0;
do
{
var line = "";
for (var i = 0; i < strings.Length; ++i)
{
var s = strings[i];
if (j >= s.Length)
{
done = true;
break;
}
line += s[j];
}
lines.Add(line);
++j;
} while (!done);
for(int i=0; i < lines.Count; ++i)
{
Console.WriteLine(string.Format("{0} : '{1}'", i, lines[i]));
}
}
}
}
Output:
0 : 'LL'
1 : 'ii'
2 : 'nn'
3 : 'ee'
4 : ' '
5 : '12'
6 : ''
Note that this pretty much assumes the strings are all the same length. Adjusting it to work with the longest string length is trivial.
Using a StringBuilder would be a bit more efficient but, unless you're working with a lot of very long strings, you'll never see the difference in performance.
This was interesting to try out and write. I haven't spent time on validation and characters, but I was trying to see if I can write something that is somewhat "compact" (challenge to myself mostly in evening hours).
#KayZed I will also run your implementation, I see you did some more validations about the inputs.
#3Dave I see we had similar ideas about this ;)
My idea around this was a projection of the input strings (plus now I hard-coded the length of the Span<char>, that calculation can be made simple) into the "flattened" structure of all characters with the offset of the index based on the number of inputs.
My "solution":
using System.Collections.Generic;
public class Program
{
public static void Main(string[] args)
{
var inputs = new[] { "Short", "Line 1", "Line 2", "Much longer line 3", "🧐" };
Rotate(inputs);
}
public static void Rotate(IReadOnlyCollection<string> aValues)
{
Span<char> chars = stackalloc char[100];
var offset = 0;
foreach (var value in aValues)
{
var index = 0;
foreach (char character in value)
{
chars[index + offset] = character;
index += aValues.Count;
}
offset++;
}
var position = 0;
foreach (char character in chars)
{
Console.Write(character == default(char) ? ' ' : character);
Console.Write(' ');
if (position == aValues.Count - 1)
{
Console.WriteLine();
position = 0;
continue;
}
position++;
}
}
}
I probably missed some edge cases (and didn't handle the "special" characters), but hope this gives an idea on some "optimisations".
The output:
S L L M �
h i i u �
o n n c
r e e h
t
1 2 l
o
n
g
e
r
l
i
n
e
3
It will need to add leading spaces (i.e. columns which were preceding shorter lines in the original string.) This method gives you an option to make this visible (fillChar - several options provided as constants.)
Also the additional ReverseLineOrder method reverses the line order in case you would want to rotate 90 degrees, reversing all lines.
using System;
using System.Globalization;
using System.Text;
namespace Library.Text
{
public static class TextRotate
{
public const char Space = ' ';
public const char MiddleDot = '\u00b7';
public const char Circle = '\u25cb';
public const char BlackSquare = '\u25a0';
public const char NoBreakSpace = '\u00a0';
public static string LinesToColumns(string s, char fillChar = Space)
{
// A line feed at the end of the text is not seen as content.
// However, if the text ends in a line feed, we make sure the output ends in one, too.
bool endsWithNewline = s.EndsWith(Environment.NewLine);
string[] linesIn = s.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
int[][] textElements = new int[linesIn.Length][];
int longestLine_chars = 0;
int longestLine_elems = 0; // The longest line, in text elements
for (int l = 0; l < linesIn.Length; l++)
{
string line = linesIn[l];
if (line.Length > longestLine_chars)
{
longestLine_chars = line.Length;
}
var elems = StringInfo.ParseCombiningCharacters(line); // Gets indices of surrogate pairs, combining characters etc.
if (elems.Length > longestLine_elems)
{
longestLine_elems = elems.Length;
}
textElements[l] = elems;
}
// Go through columns (columns in the input text, and columns in terms of text elements - NOT chars)
string[] columns = new string[longestLine_elems];
var builder = new StringBuilder(longestLine_chars * linesIn.Length + Math.Max(longestLine_chars, linesIn.Length) * Environment.NewLine.Length);
for (int column = 0; column < longestLine_elems; column++)
{
builder.Clear();
System.Diagnostics.Debug.Assert(builder.Length == 0);
int cutoff = 0;
for (int l = 0; l < linesIn.Length; l++)
{
// Is the line long enough to reach to this column?
int[] lineTextElements = textElements[l];
int textElementsInLine = lineTextElements.Length;
if (textElementsInLine > column)
{
int firstCharIndex = lineTextElements[column];
if (column + 1 < textElementsInLine)
{
int nrOfChars = lineTextElements[column + 1] - firstCharIndex;
builder.Append(linesIn[l], firstCharIndex, nrOfChars);
}
else
{
builder.Append(linesIn[l], firstCharIndex, linesIn[l].Length - firstCharIndex);
}
cutoff = builder.Length;
}
else
{
builder.Append(fillChar);
}
}
// Trim the fill char off line endings (for when rotating back)
while (cutoff > 0 && builder[cutoff - 1] == fillChar)
{
cutoff--;
}
// Resulting column
columns[column] = builder.ToString(0, cutoff);
}
// Turn the columns into lines
builder.Clear();
foreach (var c in columns)
{
builder.AppendLine(c);
}
if (!endsWithNewline && builder.Length > 0)
{
builder.Length -= Environment.NewLine.Length;
}
return builder.ToString();
}
public static string ReverseLineOrder(string s)
{
// A line feed at the end of the text is not seen as content.
// However, if the text ends in a line feed, we make sure the output ends in one, too.
bool endsWithNewline = s.EndsWith(Environment.NewLine);
string[] linesIn = s.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
var builder = new StringBuilder(s.Length);
for (int l = linesIn.Length - (endsWithNewline ? 2 : 1); l >= 0; l--)
{
builder.AppendLine(linesIn[l]);
}
if (!endsWithNewline && builder.Length > 0)
{
builder.Length -= Environment.NewLine.Length;
}
return builder.ToString();
}
}
}
Related
This question already has answers here:
Finding longest word in string
(4 answers)
Closed 3 years ago.
I got an assignment to make a method to find the longest word in a string without split, distinct and foreach.
I was able to split the words and count the length but I am stuck on how can I actually compare and write them out.
static void Main(string[] args)
{
String s1 = "Alex has 2 hands.";
longestWord(s1);
Console.
}
static void longestWord(String s1)
{
char emp = ' ';
int count = 0;
char[] ee = s1.ToCharArray();
for (int i = 0; i < ee.Length; i++)
{
if (ee[i] == emp || ee[i] == '.')
{
count++;
Console.Write(" " + (count-1));
Console.WriteLine();
count = 0;
}
else
{
Console.Write(ee[i]);
count++;
}
}
}
The output right now looks like this:
Alex 4
has 3
2 1
hands 5
I am pretty sure I would be able to get only the longest number to show by comparing count before reset with temp int but how to write out the word with it.
Or if there is a easier way which probably is.
You are already on a good way. Instead of directly printing the words, store the length and position of the longest word and print it at the end. Like so:
static void longestWord(String s1)
{
char emp = ' ';
int longestLength = 0;
int longestStart = 0;
int currentStart = 0;
for (int i = 0; i < s1.Length; i++)
{
if (s1[i] == emp || s1[i] == '.')
{
// calculate the current word length
int currentLength = i - currentStart;
// test if this is longer than the currently longest
if(currentLength > longestLength)
{
longestLength = currentLength;
longestStart = currentStart;
}
// a new word starts at the next character
currentStart = i + 1;
}
}
// print the longest word
Console.WriteLine($"Longest word has length {longestLength}: \"{s1.Substring(longestStart, longestLength)}\"");
}
There is no need for the .ToCharArray(). You can access the string directly.
I will question whether you are actually supposed to treat "2" as a word and count it at all. Using regular expressions will allow you to approach the problem using a LINQ one-liner:
static void Main(string[] args)
{
String s1 = "Alex has 2 hands.";
var word = longestWord(s1);
Console.WriteLine(word);
//Console.ReadLine();
}
static string longestWord(string s1) {
return Regex.Matches(s1,"[A-Za-z]+") // find all sequences containing alphabetical characters, you can add numberas as well: [A-Za-z0-9]
.Cast<Match>() // cast results to Enumberable<Match> so we can apply LINQ to it
.OrderByDescending(m => m.Length) // luckily for us, Match comes with Length, so we just sort our results by it
.Select(m => m.Value) // instead of picking up everything, we only want the actual word
.First(); // since we ordered by descending - first item will be the longest word
}
You can store for every word the chars in new list of chars (list for dynamic length)
and if the new word is longer of the prev long word convert it to string.
If you have two word in same length it will take the first.
If you want the last change the "maxLength < count" to "maxLength <= count"
static string longestWord(String s1)
{
char emp = ' ';
int count = 0;
int maxLength = 0;
string maxWord = string.Empty;
List<char> newWord = new List<char>();
char[] ee = s1.ToCharArray();
for (int i = 0; i < ee.Length; i++)
{
if (ee[i] == emp || ee[i] == '.')
{
if (maxLength < count)
{
maxLength = count;
maxWord = new string(newWord.ToArray());
}
count = 0;
newWord = new List<char>();
}
else
{
newWord.Add(ee[i]);
count++;
}
}
return maxWord;
}
I have an array of string elements of various words. I need the characters of each word be split equally into the text component of 3 buttons. For example, the array could hold the elements "maybe", "his", "car". In each game one of these words will be pulled from the array and its characters divided into the 3 buttons. For example, button 1 will have "ma", button 2 will have "yb" and button 3 "e" (for the word maybe). I then hide the text element of one button for the user to drag and drop the correct missing letter(s) into the space. The purpose of the game is to help children learn to spell. Does anyone know how I could go about dividing the characters equally into the 3 buttons?
Here's a function that would split the word into the amount of segments you want. You can then iterate over that list to set each segment to a button.Text.
public List<string> SplitInSegments(string word, int segments)
{
int wordLength = word.Length;
// The remainder tells us how many segments will get an extra letter
int remainder = wordLength % segments;
// The base length of a segment
// This is a floor division, because we're dividing ints.
// So 5 / 3 = 1
int segmentLength = wordLength / segments;
var result = new List<string>();
int startIndex = 0;
for (int i = 0; i < segments; i++)
{
// This segment may get an extra letter, if its index is smaller then the remainder
int currentSegmentLength = segmentLength + (i < remainder ? 1 : 0);
string currentSegment = word.Substring(startIndex, currentSegmentLength);
// Set the startindex for the next segment.
startIndex += currentSegmentLength;
result.Add(currentSegment);
}
return result;
}
usage:
// returns ["ma", "yb", "e"]
var segments = SplitInSegments("maybe", 3);
Edit
I like the fact that this is for teaching children. So here comes.
Regarding your question on splitting the string based on specific letter sequences: After you've split the string using regex, you will have an array of strings. Then determine the amount of items in the splitted string and concatenate or split further based on the number of segments:
// sequences to split on first
static readonly string[] splitSequences = {
"el",
"ol",
"bo"
};
static readonly string regexDelimiters = string.Join('|', splitSequences.Select(s => "(" + s + ")"));
// Method to split on sequences
public static List<string> SplitOnSequences(string word)
{
return Regex.Split(word, regexDelimiters).Where(s => !string.IsNullOrEmpty(s)).ToList();
}
public static List<string> SplitInSegments(string word, int segments)
{
int wordLength = word.Length;
// The remainder tells us how many segments will get an extra letter
int remainder = wordLength % segments;
// The base length of a segment
// This is a floor division, because we're dividing ints.
// So 5 / 3 = 1
int segmentLength = wordLength / segments;
var result = new List<string>();
int startIndex = 0;
for (int i = 0; i < segments; i++)
{
// This segment may get an extra letter, if its index is smaller then the remainder
int currentSegmentLength = segmentLength + (i < remainder ? 1 : 0);
string currentSegment = word.Substring(startIndex, currentSegmentLength);
// Set the startindex for the next segment.
startIndex += currentSegmentLength;
result.Add(currentSegment);
}
return result;
}
// Splitword will now always return 3 segments
public static List<string> SplitWord(string word)
{
if (word == null)
{
throw new ArgumentNullException(nameof(word));
}
if (word.Length < 3)
{
throw new ArgumentException("Word must be at least 3 characters long", nameof(word));
}
var splitted = SplitOnSequences(word);
var result = new List<string>();
if (splitted.Count == 1)
{
// If the result is not splitted, just split it evenly.
result = SplitInSegments(word, 3);
}
else if (splitted.Count == 2)
{
// If we've got 2 segments, split the shortest segment again.
if (splitted[1].Length > splitted[0].Length
&& !splitSequences.Contains(splitted[1]))
{
result.Add(splitted[0]);
result.AddRange(SplitInSegments(splitted[1], 2));
}
else
{
result.AddRange(SplitInSegments(splitted[0], 2));
result.Add(splitted[1]);
}
}
else // splitted.Count >= 3
{
// 3 segments is good.
result = splitted;
// More than 3 segments, combine some together.
while (result.Count > 3)
{
// Find the shortest combination of two segments
int shortestComboCount = int.MaxValue;
int shortestComboIndex = 0;
for (int i = 0; i < result.Count - 1; i++)
{
int currentComboCount = result[i].Length + result[i + 1].Length;
if (currentComboCount < shortestComboCount)
{
shortestComboCount = currentComboCount;
shortestComboIndex = i;
}
}
// Combine the shortest segments and replace in the result.
string combo = result[shortestComboIndex] + result[shortestComboIndex + 1];
result.RemoveAt(shortestComboIndex + 1);
result[shortestComboIndex] = combo;
}
}
return result;
}
Now when you call the code:
// always returns three segments.
var splitted = SplitWord(word);
Here is another approach.
First make sure that the word can be divided by the desired segments (add a dummy space if necessary) , then use a Linq statement to get your parts and when adding the result trim away the dummy characters.
public static string[] SplitInSegments(string word, int segments)
{
while(word.Length % segments != 0) { word+=" ";}
var result = new List<string>();
for(int x=0; x < word.Count(); x += word.Length / segments)
result.Add((new string(word.Skip(x).Take(word.Length / segments).ToArray()).Trim()));
return result.ToArray();
}
You can split your string into a list and generate buttons based on your list. The logic for splitting the word into a string list would be something similar to this:
string test = "maybe";
List list = new List();
int i = 0, len = 2;
while(i <= test.Length)
{
int lastIndex = test.Length - 1;
list.Add(test.Substring(i, i + len > lastIndex? (i + len) - test.Length : len));
i += len;
}
HTH
so I'm working on this problem: https://www.hackerrank.com/challenges/30-review-loop/problem (it's in C#)
and so far I'm just trying to break it down piece by piece, and so far I was able to get it to show every other character, but I'm not sure how to concatenate each letter into a new string.
My code for the problem is as follows I've commented out the two for loops, because I felt like there was a more elegant solution to this than what I had, but I didn't want to lose where I was in case another path proved to be more challenging.
using System;
using System.Collections.Generic;
using System.IO;
class Solution {
static void Main(String[] args) {
/* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution */
int inputQTY = Int32.Parse(Console.ReadLine());
string input = Console.ReadLine(); // The example gives us the first word to be Hacker then the next word Rank on the next line, so the outputs would be Hce akr, and Rn ak respectively.
int strLen = input.Length;
char[] inputCharArray = input.ToCharArray();
string output = "";
/*
for (int j = 0; j < inputQTY; j++){
for (int i = 0; i < strLen; i++) {
if (j % 2 == 0 && i % 2 == 0) {
Console.WriteLine(inputCharArray[i]);
output = new string (new char[] {inputCharArray[i]});
Console.WriteLine(output);
Console.WriteLine("This is i: {0}", i);
Console.WriteLine("This is j: {0}", j);
Console.WriteLine("--------------");
Console.WriteLine("");
}
else {
Console.WriteLine("This is the next j part hopefully: {0}", j);
}
}
}*/
}
}
Like I understand I need to first step through the word, grab every other letter, then step through the word again and grab the remaining letters, then concatenate those letters into words, and concatenate those words into a sentence, so j would be the loop giving me the two words where I is the loop getting the two words put together..... but I'm having a difficult time wrapping my head around where I'm going wrong here. On top of this, I feel like there's another approach entirely that I'm missing, using commands I may not even know about.
Anyhoo any help is appreciated, hopefully I won't be so green after this. Thanks!
Ok so I ended up solving it with the following code, thank you for all your help everyone!
I ended up solving it with the following code (in C#):
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
class Solution {
static void Main(String[] args) {
/* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution */
int count = Int32.Parse(Console.ReadLine());
for (int k = 0; k < count; k++) {
char[] word = Console.ReadLine().ToCharArray();
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
for (int i = 0; i < word.Length; i+=2) {
sb1.Append(word[i]);
}
for (int j = 1; j < word.Length; j+=2) {
sb2.Append(word[j]);
}
Console.WriteLine(sb1 + " " + sb2);
}
}
}
LINQ version, updated to fix index error:
output = $"{new string(s.Where((x,i) => i % 2 == 0).ToArray())} {new string(s.Where((x,i) => i % 2 != 0).ToArray())}";
To explain, you're grabbing every character whose index in the string is evenly divisible by 2 and printing it, then every character in the string whose index is not evenly divisible by 2 and printing it.
Update:
Since I was asked for further explanation. First, here's the full code that runs successfully in the HackerRank challenge:
using System;
using System.Collections.Generic;
using System.Linq;
class Solution
{
static void Main(String[] args)
{
List<string> tests = new List<string>();
var testCount = int.Parse(Console.ReadLine());
for (var i = 0; i < testCount; i++)
{
tests.Add(Console.ReadLine());
}
foreach (var s in tests)
{
Console.WriteLine($"{new string(s.Where((x, i) => i % 2 == 0).ToArray())} {new string(s.Where((x, i) => i % 2 != 0).ToArray())}");
}
}
}
Regarding what each section of the code does:
i % 2 == 0
This is a test to see if a number is evenly divisible by two, or an even number.
s.Where((x,i) => i % 2 == 0)
This says, for the array of characters that make up the string 's', return all characters (the result is an IEnumerable) where that character's index (location in the string) is an even number.
new string(s.Where((x,i) => i % 2 == 0).ToArray())
This says to take that IEnumerable of characters with even numbered indexes and return them to an Array of characters. Then, create a new string out of that array of characters.
For the odd numbers, it's the same, but you use != 0 in the mod.
I used this simple method of appending to two StringBuilder objects
var sb1 = new StringBuilder();
var sb2 = new StringBuilder();
int i = 0;
foreach (char c in input)
{
var sb = (i % 2 == 0 ? sb1 : sb2);
sb.Append(c);
i = i + 1;
}
output = sb1.ToString() + " " + sb2.ToString();
this is the long way..
int count = int.Parse(Console.ReadLine());
for(int k = 0; k < count; k++){
char[] inputChars = Console.ReadLine().ToCharArray();
char[] evenChars = new char[inputChars.Length % 2 == 0 ? inputChars.Length / 2 : (inputChars.Length + 1) / 2];
char[] oddChars = new char[inputChars.Length - evenChars.Length];
int evenIndex=0,oddIndex = 0;
for(int i = 0; i < inputChars.Length;i++)
if(i % 2 == 0)
evenChars[evenIndex++] = inputChars[i];
else
oddChars[oddIndex++] = inputChars[i];
Console.WriteLine(string.Format("{0} {1}",string.Concat(evenChars),string.Concat(oddChars)));
}
an alternative..
int count = int.Parse(Console.ReadLine());
for(int k = 0; k < count; k++){
string input = Console.ReadLine();
Enumerable.Range(0, input.Length)
.OrderBy(o => o % 2 != 0)
.Select(o => {
if(o == 1)
Console.Write(" ");
Console.Write(input[o]);
return input[o];
}).ToArray();
Console.Write("\n");
}
My task is to print words in an array in a rectangular frame. Like this:
*********
* Hello *
* World *
* in *
* a *
* Frame *
*********
I wrote a code witch works just fine, but I'm just curious - how can I do the same just with only one foreach cycle?
using System;
namespace RectangleAroundWords
{
class Program
{
public static void Main()
{
string[] words = new[] { "Hello", "World", "in", "a", "frame" };
int length = 0;
foreach (var item in words)
{
if (length < item.Length)
{
length = item.Length;
}
}
String tabs = new string('*', length + 4);
Console.WriteLine(tabs);
foreach (var item in words)
{
Console.WriteLine("* " + item.PadRight(length, ' ') + " *");
}
Console.WriteLine(tabs);
Console.ReadKey();
}
}
}
Although I think my first answer is the better approach in readability and best practices, it is possible to do this completley without any loop by using recursion (just to be a nit-picker ;-):
public static void Main()
{
string[] words = new[] { "Hello", "World", "in", "a", "frame" };
var output = Recurse(words);
String tabs = new string('*', output.Item2 + 4);
Console.WriteLine(tabs);
Console.WriteLine(output.Item1);
Console.WriteLine(tabs);
Console.ReadKey();
}
Tuple<string, int> Recurse(string[] words, int index = 0, int maxLength = 0)
{
maxLength = Math.Max(maxLength, words[index].Length);
if (index < words.Length - 1)
{
var output = Recurse(words, index + 1, maxLength);
maxLength = output.Item2;
return Tuple.Create(
string.Format("* {0} *{1}{2}", words[index].PadRight(maxLength), Environment.NewLine, output.Item1),
maxLength);
}
return Tuple.Create(
string.Format("* {0} *", words[index].PadRight(maxLength)),
maxLength);
}
Compare and decide yourself...
With this snippet you can get the maximum length without the first loop:
int length = words.Select(w => w.Length).Max();
or shorter:
int length = words.Max(w => w.Length);
Also I think it would be better to first create the complete output string by using the StringBuilder class:
string[] words = new[] { "Hello", "World", "in", "a", "frame" };
int length = words.Max(w => w.Length);
var sb = new StringBuilder();
String tabs = new string('*', length + 4);
sb.AppendLine(tabs);
foreach (var item in words)
{
sb.AppendFormat("* {0} *", item.PadRight(length));
sb.AppendLine();
}
sb.AppendLine(tabs);
Console.WriteLine(sb.ToString());
Console.ReadKey();
You'd have to create coordinates then calculate the character at each coordinate somehow.
You know the maximum height without looping (words.Length) so increment a pointer and take modulo and divisor by this height to give x,y coords. Continue until you don't find any character at the given coordinates.
string[] words = new[] { "Hello", "World", "in", "a", "frame" };
int height = words.Length + 4;
int row = 0;
int column = 0;
void Write(char c)
{
Console.SetCursorPosition(column, row);
System.Console.Write(c);
}
int i = 0;
int completedWordCount = 0;
int? lastColumn = null;
do
{
row = i % height;
column = i / height;
if (row == 0 || row == height - 1)
{
completedWordCount = 0;
Write('*');
}
if (column == 0 || column == lastColumn)
{
Write('*');
}
if (row > 1 && row < height - 2 && column > 1)
{
string word = words[row - 2];
if (column - 2 < word.Length)
{
Write(word[column - 2]);
}
else
{
completedWordCount++;
}
if (completedWordCount == words.Length && !lastColumn.HasValue)
{
lastColumn = column + 2;
}
}
i++;
} while ((!lastColumn.HasValue || column < lastColumn) || row != height - 1);
Not exactly a foreach, but only one 'iteration'.
Based on the question: "how can I do the same just with only one foreach cycle?"
At this point, I only see 2 options:
If the string list is inserted by the user (BEFORE YOUR CODE and does not comes from a DB table), just read the length of the input and keep it on a MaxLengthTillNow, and then you are ok with it.
Crappy solution: make the "rectangle" fixed with with 100 :P no matter what you will write inside, will be ok..... but this is probably not the purpose of your problem :)
In conclusion: all the linq/lambdas/func, etc... solutions ARE always using "loops" behind doors..... so, probably there is no answer for your question.
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", "");