I have a String Array that contains some names in it.
I want to show the names in my console with string column alignment.
For example this is my array:
{"First","Second","Third","Forth","Fifth","Sixth","Seventh","Eighth","Ninth"};
And then display them in this format:
First Second Third
Forth Fifth Sixth
Seventh Eighth Ninth
As you see these names are aligned in columns with spaces between them, that's what I want. How to do that?
string[] str={"First","Second","Third","Forth","Fifth","Sixth","Seventh","Eighth","Ninth"};
for (int i=0;i<str.Length-2;i+=3)
Console.WriteLine ("{0,-10} {1,-10} {2,-10}",str[i],str[i+1],str[i+2]);
Demo Code
https://repl.it/repls/CelebratedMuffledCybernetics
Since you have expressed the desire to have full adaptability of column widths, then below is the entire solution. As I said, it won't be easy at all.
This solution supports words of any length (not longer than the screen width). The result is always the screen populated as densely as possible, given the text.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleDemo
{
class Program
{
static IEnumerable<(string value, int row, int col)> Break(IEnumerable<string> data, int columnsNumber) =>
data.Select((item, index) => (item, index / columnsNumber, index % columnsNumber));
static IEnumerable<int> GetColumnWidths(IEnumerable<string> data, int columnsNumber) =>
Break(data, columnsNumber)
.GroupBy(tuple => tuple.col)
.Select(group => group.Max(tuple => tuple.value.Length));
static int RequiredScreenWidth(IEnumerable<string> data, int columnsNumber) =>
GetColumnWidths(data, columnsNumber)
.Sum() + columnsNumber - 1;
static int SuggestNumberOfColumns(IEnumerable<string> data, int screenWidth) =>
Enumerable.Range(1, int.MaxValue)
.TakeWhile(columnsNumber => RequiredScreenWidth(data, columnsNumber) <= screenWidth)
.DefaultIfEmpty(0)
.Last();
static IEnumerable<string> Pad(IEnumerable<string> data, int columnsNumber, int[] columnWidths) =>
Break(data, columnsNumber)
.Select(tuple => (tuple.value.PadRight(columnWidths[tuple.col], ' ')));
static IEnumerable<string> GenerateRows(IEnumerable<string> paddedData, int columnsNumber) =>
Break(paddedData, columnsNumber)
.GroupBy(tuple => tuple.row)
.Select(row => string.Join(" ", row.Select(tuple => tuple.value).ToArray()));
static string GenerateColumnatedText(IEnumerable<string> paddedData, int columnsNumber) =>
string.Join(Environment.NewLine, GenerateRows(paddedData, columnsNumber).ToArray());
static void Main(string[] args)
{
IEnumerable<string> data = new[]
{
"Over", "time", "the", ".NET", "Framework", "has", "added", "many",
"features", "that", "made", "concurrent", "programming", "a", "lot",
"easier", "This", "started", "with", "the", "introduction", "of",
"the", "thread", "pool", "got", "a", "lot", "more", "powerful", "with",
"the", "task-based", "model", "and", "the", "Task", "Parallel",
"Library", "and", "was", "improved", "even", "more", "by", "the",
"addition", "of", "the", "async", "and", "await", "language",
"keywords", "While", "creating", "and", "running", "concurrently",
"is", "easier", "than", "ever", "one", "of", "the", "fundamental",
"problems", "still", "exists", "mutable", "shared", "state", "Reading",
"from", "multiple", "threads", "is", "typically", "very", "easy", "but",
"once", "the", "state", "needs", "to", "be", "updated", "it", "gets",
"a", "lot", "harder", "especially", "in", "designs", "that", "require",
"locking", "An", "alternative", "to", "locking", "is", "making", "use",
"of", "immutable", "state", "Immutable", "data", "structures", "are",
"guaranteed", "to", "never", "change", "and", "can", "thus", "be",
"passed", "freely", "between", "different", "threads", "without",
"worrying", "about", "stepping", "on", "somebody", "else’s", "toes"
};
int columnsNumber = SuggestNumberOfColumns(data, 80);
int[] columnWidths = GetColumnWidths(data, columnsNumber).ToArray();
IEnumerable<string> padded = Pad(data, columnsNumber, columnWidths);
string text = GenerateColumnatedText(padded, columnsNumber);
Console.WriteLine(text);
Console.ReadLine();
}
}
}
When applied to the text from the demo, and to the screen of 60 characters wide, the result is a perfectly aligned order of columns:
Over time the .NET Framework
has added many features that
made concurrent programming a lot
easier This started with the
introduction of the thread pool
got a lot more powerful
with the task-based model and
the Task Parallel Library and
was improved even more by
the addition of the async
and await language keywords While
creating and running concurrently is
easier than ever one of
the fundamental problems still exists
mutable shared state Reading from
multiple threads is typically very
easy but once the state
needs to be updated it
gets a lot harder especially
in designs that require locking
An alternative to locking is
making use of immutable state
Immutable data structures are guaranteed
to never change and can
thus be passed freely between
different threads without worrying about
stepping on somebody else's toes
Sample Example
string[] val = { "First", "Second", "Third", "Forth", "Fifth", "Sixth", "Seventh", "Eighth", "Ninth" };
for (int i = 0,j=1; i < val.Length ; i++) {
Console.Write("{0} ", val[i]);
if (j % 3 == 0)
{
Console.WriteLine();
}
j++;
}
Console.ReadLine();
You can use add tabs to your string when printing them on screen with WriteLine using \t escape sequence.
For example if you are printing 3 string on screen, you can do it like this:
Console.WriteLine("String 1" + "\t"
+ "String 2" + "\t"
+ "String 3");
Related
Since C# version 8, there is a new way of indexing, as shown in the example:
void Main()
{
Index idx = ^5; // counted from last to first element
var sentence = new string[]
{
"The", "quick", "brown", "fox", // position 0 ... 3
"jumped", "over", "the", "lazy", "dog" // position 4 ... 8
};
var lstSentence = sentence.ToList(); // applicable to list as well
Console.WriteLine(sentence[idx]);
Console.WriteLine(lstSentence[idx]);
}
It returns jumped.
So far so good. But how can you get the absolute position representing ^5 efficiently ?
I saw .NET internally is using Index (which I added to this example), and there is a value property.
How can I get the position of sentence[^5] ?
Something like
var absPos = sentence[^5].Index.Value; // get absolute position (which is = 4)
(but unfortunately this doesn't seem to exist)
I know there is a formula (number of elements - position from right), but the compiler / .net calculates and probably stores this somewhere.
Update: idx.Value isn't the solution, it just stores the relative value 5 together with .IsFromEnd, which is true. But you can't access it directly, like sentence[^5].Index.Value which is what I wanted to know.
Note: This is also useful for Dictionary, if you take a look at the RudimentaryMultiValuedDictionary Example - if you want to add an additional indexer like public List<TValue> this[Index idx] you will require that. internalDictionary.ElementAtOrDefault(i), which you need for this purpose, does only work with integer values (absolute positions).
If you use a decompiler to see what code is generated for your example, it looks like this:
private static void Main()
{
Index index = new Index(5, true);
string[] strArray = new string[] { "The", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dog" };
Console.WriteLine(strArray[index.GetOffset((int)strArray.Length)]);
}
As you can see, it just uses index.GetOffset((int)strArray1.Length) to calculate the offset. There's no way to do this yourself other than by doing it the same way. There's no other language construct that you can use.
(I'm not sure why it casts the strArray1.Length property to int, since it's already an int.)
Also note that if you put the index inline using code like this:
var sentence = new string[]
{
"The", "quick", "brown", "fox",
"jumped", "over", "the", "lazy", "dog"
};
Console.WriteLine(sentence[^5]);
That gets compiled to:
string[] strArray = new string[] { "The", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dog" };
Console.WriteLine(strArray[(int)strArray.Length - 5]);
Which has optimised away the use of an Index object altogether.
As I mentioned in the comments below, you could write an extension method which accepts an IList. This would then work with anything that implements IList such as List<T>, arrays, ImmutableSortedSet<T> and so on.
For example:
public static class IndexExt
{
public static int AbsIndexFor(this IList self, Index index)
{
return index.GetOffset(self.Count);
}
}
Then you can do:
var array = new string[]
{
"The", "quick", "brown", "fox",
"jumped", "over", "the", "lazy", "dog"
};
Console.WriteLine(array.AbsIndexFor(^5));
var list = new List<string>
{
"The", "quick", "brown", "fox",
"jumped", "over", "the", "lazy", "dog"
};
Console.WriteLine(list.AbsIndexFor(^5));
I may not know how to search for a solution properly yet but I could not find anything related to the thing I got stuck on below:
I have a List of strings containing 21 words
public static List<string> LevelOneWords = new List<string> { "As", "If", "Go", "Do", "So", "No", "Up", "Hi", "Me", "Be", "Ah", "An", "Aw", "Is", "Ok", "At", "We", "Am", "Ab", "Ex", "It"};
How do I go on about generating a specific amount of these following a user decision?
Because rand.Next(LevelOneWords.Count) only lets you generate random words from the amount available in the list.
But say I wanted to generate 50 instances?
To put you in the picture, this is a Typer Shark kind of game and in the training session, I want the user to have the freedom to choose how much words should be generated.
Thanking you in advance since I am new to programming and I find StackOverFlow's community very helpful!
You can use rand.Next() X times using a loop:
List<string> selectedWords = new List<string>();
for (int i = 0; i < 50; ++i)
{
string randomWord = LevelOneWords[rand.Next(LevelOneWords.Count)];
selectedWords.Add(randomWord);
}
The background of this program is simple enough: I want a user to be able to enter any letter (A, B, C, etc.) into a textbox, and with the click of a button, have the program return how many U.S. states begin with that letter (e.g. letter A entered and result is 4).
Here is my code as of now...
private void btnClick_Click(object sender, EventArgs e)
{
string[] States = new String[50] {"Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado",
"Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas",
"Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri",
"Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina",
"North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota",
"Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"};
string myLetter = txtboxEnter.Text;
int result;
result = 0;
for (int i = 0; i <= States.Length - 1; i++)
{
if (States[i].Substring(0, 1) == myLetter)
{
result = result + i;
}
else
{
result = 0;
}
}
lblDisplay.Text = Convert.ToString(result);
}
As you see, I have the States declared in an array.
The issue I am having is with the for loop and the If statement inside it.
The value always returned is 0. I feel as though I need another line of code directly for the loop to total values. Am I correct?
Change this:
if (States[i].Substring(0, 1) == myLetter)
{
result = result + i;
}
else
{
result = 0;
}
To this:
if (States[i].Substring(0, 1) == myLetter)
{
++result;
}
And if you want to make things a bit more efficient, change that comparison to this:
if (States[i].StartsWith(myLetter))
Linq could be used for a more eloquent solution i.e. use:
States.Count(state => state.StartsWith(myLetter));
instead of your "for" loop.
If all you're doing is finding the number of states that start with each letter, I wouldn't use this kind of solution, since the data is basically static. You could do a table lookup instead using a Dictionary<char, int> and work with single characters instead of strings, and make it a class variable so you don't have to create it every time. For example:
//-snip-
private Dictionary<char, int> States;
//-snip-
public Form1()
{
States = new Dictionary<char, int>();
States.add('A', 4);
States.add('B', 0);
States.add('C', 3);
States.add('D', 1);
//... etc.
}
//-snip-
private void btnClick_Click(object sender, EventArgs e)
{
char myLetter = txtboxEnter.Text.Trim()[0]; // do some other input sanitation here
int result = States[myLetter];
lblDisplay.Text = Convert.ToString(result); // consider making your Dictionary <char, string> so you can skip the conversion here
}
And if you still insist on your solution, you can also add some performance tweaks to it by taking advantage of the fact that your array is sorted. You could do a binary search but that's kind of overkill, but at least you could break out of your loop once you have found a state matching the criteria and then a state that no longer matches the criteria (e.g. if you're looking for all words in the English dictionary starting with B, you would stop once you hit a word starting with C).
Using Lambda
result = Array.FindAll(States, x => x.StartsWith(myLetter)).Length;
This question already has answers here:
Randomize a List<T>
(28 answers)
Closed 9 years ago.
I have two arrays called numbers and sounds. My code is below. When I use random generator, I don't want the same value to be assigned to the sounds array. sounds[0], sounds[1], sounds[3] should have different values all the time. Thanks
string[] numbers ={ "1", "2", "3", "4", "5", "6", "7","8","9","10","11","12","13","14","15","16","17","18","19","20"};
Random r= new Random();
sounds[0] = numbers[r.Next(0, numbers.Count())];
sounds[1] = numbers[r.Next(0, numbers.Count())];
sounds[2] = numbers[r.Next(0, numbers.Count())];
If you want a simple approach and performance is not an issue, you could convert your array into a list and remove each element as it is taken:
string[] numbers ={ "1", "2", "3", "4", "5", "6", "7","8","9","10","11","12","13","14","15","16","17","18","19","20"};
List<string> remaining = new List<string>(numbers);
Random r = new Random();
for (int i = 0; i < 3; ++i)
{
int n = r.Next(0, remaining.Count);
sounds[i] = remaining[n];
remaining.RemoveAt(n);
}
Here is a rather simple/naive answer that may give you some ideas. Note that you should probably use .Length rather than .Count() since .Length is an O(1) operation, and you already have the property there. This solution doesn't scale if sounds is bigger. If that's the case, you'll want to shuffle the numbers array. This will give you three unique values though.
string[] numbers = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" };
string[] sounds = new string[3];
Random r = new Random();
//get the first one, this one will always be unique
sounds[0] = numbers[r.Next(0, numbers.Length)];
//set the second equal to the first, so that it will enter the while loop.
sounds[1] = sounds[0];
while (sounds[1] == sounds[0]) //repeats until it gets a unique value
sounds[1] = numbers[r.Next(0, numbers.Length)];
sounds[2] = sounds[0];
while (sounds[2] == sounds[1] || sounds[2] == sounds[0]) //works the same as previous, except it checks for both
sounds[2] = numbers[r.Next(0, numbers.Length)];
I have a predefined string array with the letters from A to Q :
string[] SkippedAreasArray = new string[] {"A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q"};
In a TextBox inside Windows Form the user can enter skippedAreas like this: A,B,C,D... there is validations and restrictions to use only letters and commas so the input is guaranteed to be in this format.
What I do is taking the user input and populate another string array:
string[] SkippedAreasFromForm = new string[17];
...
SkippedAreasFromForm = (txtSkippedAreas.Text).Split(',');
Now comes the tricky part for which I'm seeking assistance. The user must enter Number of areas for example - 3. Which mean he is working only with A, B, and C. If the number of areas was 2 then he can only use A and B if the number of areas was 4 then A, B, C and D are available and so on.
What I need is to check if in the array SkippedAreasFromForm which is populated with the user input there is an area that doesn't match the above criteria.
What this mean in terms of coding - I need to take every element from SkippedAreasFromForm, take it's integer value from the predefined SkippedAreasArray and see if this value is equal or greater (>=) of the value that he entered as `Number of areas. If there is an area which is outside the scope of the selected number an error should be shown.
What I have right now is :
foreach (string tempAreaValue in SkippedAreasFromForm)
{
for (int i = 0; i < SkippedAreasArray.Length; i++)
{
if (tempAreaValue == SkippedAreasArray[i])
{
if ((i + 1) > entity.AreasCnt)
{
MessageBox.Show("You must use areas only within the Number of Areas scope!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
txtSkippedAreas.Focus();
return false;
}
}
}
}
For the few test I made it works. But first - at least for me it seems over complicated. Second - I'm not sure that this algorithm is working exactly as I need to or I'm just having correct results by luck. Third - I'm coding C# for 2months now and this seems to me like an excellent candidate for a LINQ expression - do you think it would be better using LINQ and I would appreciate a help in the transformation.
I think you're just looking for IndexOf:
int index = SkippedAreasArray.IndexOf(tempAreaValue);
if (index >= entity.AreasCnt)
{
...
}
(You might also want to check for index being -1, which would occur if the element wasn't in the list at all. Also, consider duplicates - can the user enter A, A, A?)
If you are looking for the elements in the array from start index to max string elements based on areaNumber, then:
int areaNumber = 4;
var result = SkippedAreasArray.Select((r, i) => new { Value = r, Index = i })
.Where(r => r.Index <= areaNumber - 1)
.Select(r => r.Value)
.ToArray();
For areaNumber 4 you will get "A", "B", "C", "D"
EDIT:
I'm looking if every element from the user input (which is array
A,C,H,Q..) is inside the area determined from the areNumber (from comment)
Suppose your userInputArray is:
string[] userInputArray = new string[] {"A", "C", "H", "Q"};
Then you can use Enumerable.Except in the following manner:
if(result.Except(userInputArray).Any())
{
//Invalid
}
else
{
//Valid
}