Aligning text into three columns - c#

I would like to show the LinkedList data into three separate columns with List1, List2, and List3 as the column heading. I am unable to align the LinkedList data under these headings.
void LoopThruLinkedList(LinkedList<string> strList)
{
foreach(string str in strList)
{
Console.WriteLine(string.Format($"{str, -20}"));
}
//Console.WriteLine();
}
// Creating a linkedlist
// Using LinkedList class
LinkedList<string> my_list = new LinkedList<string>();
// Adding elements in the LinkedList
// Using AddLast() method
my_list.AddLast("Zoya");
my_list.AddLast("Shilpa");
my_list.AddLast("Rohit");
my_list.AddLast("Rohan");
my_list.AddLast("Juhi");
my_list.AddLast("Zoya");
my_list.AddLast("Rohit");
string List1 = "List One Students: ";
string List2 = "List Two Students: ";
string List3 = "List Three Students: ";
Console.WriteLine($"{List1, -20}{List2, -20}{List3, -20}");
// Accessing the elements of LinkedList using the foreach loop
LoopThruLinkedList(my_list);
LoopThruLinkedList(my_list);
LoopThruLinkedList(my_list);

There are a lot of issues here
Why do you need linkedList? they have no indexers and makes any solution inefficent
Why are all the lists the same, however maybe this is just an example.
You need to flatten this somehow to write it to the console sequentially
However this might point you in the right direction
void LoopThruLinkedList(params LinkedList<string>[] strLists)
{
var max = strLists.Max(x => x.Count());
for (int i = 0; i < max; i++)
{
foreach (var item in strLists)
Console.Write($"{(item.Count > i ? item.ElementAt(i) : ""),-20}");
Console.WriteLine();
}
}
// Creating a linkedlist
// Using LinkedList class
LinkedList<string> my_list = new LinkedList<string>();
// Adding elements in the LinkedList
// Using AddLast() method
my_list.AddLast("Zoya");
my_list.AddLast("Shilpa");
my_list.AddLast("Rohit");
my_list.AddLast("Rohan");
my_list.AddLast("Juhi");
my_list.AddLast("Zoya");
my_list.AddLast("Rohit");
string List1 = "List One Students: ";
string List2 = "List Two Students: ";
string List3 = "List Three Students: ";
Console.WriteLine($"{List1,-20}{List2,-20}{List3,-20}");
// Accessing the elements of LinkedList using the foreach loop
LoopThruLinkedList(my_list, my_list, my_list);
Or another variation using enumeration in a pivot style operation with GroupBy
void LoopThruLinkedList(params LinkedList<string>[] strLists)
{
var results = strLists
.SelectMany(inner => inner.Select((item, index) => new { item, index }))
.GroupBy(i => i.index, i => i.item)
.Select(g => g.ToList());
foreach (var list in results)
{
foreach (var item in list)
Console.Write($"{item,-20}");
Console.WriteLine();
}
}

Here is a variation that uses only enumerators and is therefore more efficient than accessing elements by index:
void PrintLists(LinkedList<string>[] lists, string[] captions)
{
//Find the necessary column widths
var columnWidths = new int[lists.Length];
for(int i = 0; i < lists.Length; ++i)
{
columnWidths[i] = captions[i].Length;
foreach (var s in lists[i])
columnWidths[i] = Math.Max(columnWidths[i], s.Length);
columnWidths[i] += 2; //spacing
}
//Print the headings
for(int i = 0; i < lists.Length; ++i)
Console.Write(captions[i].PadRight(columnWidths[i]));
Console.WriteLine();
//Initialize iterators
var iterators = new LinkedList<string>.Enumerator[lists.Length];
var iteratorsValid = new bool[lists.Length];
for (int i = 0; i < lists.Length; ++i)
{
iterators[i] = lists[i].GetEnumerator();
iteratorsValid[i] = iterators[i].MoveNext();
}
//Print the rest of the table
while (iteratorsValid.Any(b => b))
{
for (int i = 0; i < lists.Length; ++i)
{
if (iteratorsValid[i])
{
var item = iterators[i].Current;
Console.Write(item.PadRight(columnWidths[i]));
iteratorsValid[i] = iterators[i].MoveNext();
}
else
Console.Write(new String(' ', columnWidths[i]));
}
Console.WriteLine();
}
}
Which you call like
PrintLists( new [] { my_list, my_list, my_list},
new [] { "List One Students: ", "List Two Students: ", "List Three Students: " });

Related

How can I split an array into smaller arrays?

I want to take a list of names, add them to an array, then split that array into N groups, then display those arrays in separate textboxes in a Windows form. So far I have this, which takes the list and splits them up, but honestly, I don't think it's doing what I want it to do.
MasterList:
Johnny, Mark, Tom, Carl, Jenny, Susie, Ben, Tim, Angie
Group 1: Johnny, Mark, Angie
Group 2: Tom, Carl
Group 3: Jenny, Susie
Group 4: Ben, Tim
void addnamestoList_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(this.studentnameTboxContent))
{
int num = (int)MessageBox.Show("No content to format.",
"Message",
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
else
{
transformText();
}
}
public void transformText()
{
string[] masterList = studentnameTboxContent.Split('\n');
var split = from index in Enumerable.Range(0, masterList.Length)
group masterList[index] by index / int.Parse(groupcountTboxContent);
studentNames.Text = "";
foreach (var Array in split)
{
studentNames.AppendText(string.Join(Environment.NewLine, Array.ToArray()));
}
}
Method to randomize list:
private string[] randomizeList(string[] list)
{
Random rnd = new Random();
string[] randomList = list.OrderBy(x => rnd.Next()).ToArray();
return randomList;
}
Here's one way to do it, but it isn't very elegant. Basically calculate the group size based on the group count entered by the user, determine how many items should be in each group, and determine the number of remaining items that need to be added to the first lists (if the group cannot be evenly divided by the count).
Then, in a loop, skip the number of items you've taken so far, then take the group size number of items, and if there are still extra items that need to be added, grab them from the end of the list:
var masterList = new List<string>
{
"Johnny", "Mark", "Tom", "Carl",
"Jenny", "Susie", "Ben", "Tim", "Angie"
};
var groupCount = 4; // Entered by the user
var minGroupSize = masterList.Count / groupCount;
var extraItems = masterList.Count % groupCount;
var groupedNames = new List<List<string>>();
for (int i = 0; i < groupCount; i++)
{
groupedNames.Add(masterList.Skip(i * minGroupSize).Take(minGroupSize).ToList());
if (i < extraItems)
{
groupedNames[i].Add(masterList[masterList.Count - 1 - i]);
}
}
Console.WriteLine("Here are the groups:");
for(int index = 0; index < groupedNames.Count; index++)
{
Console.WriteLine($"#{index + 1}: {string.Join(", ", groupedNames[index])}");
}
Console.Write("\nDone!\nPress any key to exit...");
Console.ReadKey();
Output
This code can easily be extracted into a method so it can be re-used elsewhere:
static List<List<string>> GetGroups(List<string> masterList, int groupCount)
{
var groups = new List<List<string>>();
// Argument validation. All of these conditions should be true.
if (masterList != null && groupCount > 0 && groupCount <= masterList.Count)
{
var minGroupSize = masterList.Count / groupCount;
var extraItems = masterList.Count % groupCount;
for (int i = 0; i < groupCount; i++)
{
groups.Add(masterList.Skip(i * minGroupSize).Take(minGroupSize).ToList());
if (i < extraItems)
{
groups[i].Add(masterList[masterList.Count - 1 - i]);
}
}
}
return groups;
}
Usage
var masterList = new List<string>
{
"Johnny", "Mark", "Tom", "Carl", "Jenny",
"Susie", "Ben", "Tim", "Angie"
};
var groupedNames = GetGroups(masterList, 4);

c# dynamically modifying list

I don't understand how to do it with foreach...
The goal is to modify a list each time we change Num.
Is the way with a Canvas List and a working List is ok for nice coding?
class Program
{
static void Main(string[] args)
{
int i_Num = 0;
string Str_Num = "";
string[] linkToPLC = {"toto[{0}].test{1}", "tata[{0}].test{1}", "titi[{0}].test{1}"};
List<string> genlnkPLCCanvas = new List<string>(linkToPLC);
List<string> genlnkPLCworkingwith = new List<string>(linkToPLC);
Console.WriteLine("Insert Num: ");
Str_Num = Console.ReadLine();
i_Num = Convert.ToInt32(Str_Num);
for (int item = 0; item < genlnkPLCCanvas.Count; item++)
{
genlnkPLCworkingwith[item] = String.Format(genlnkPLCworkingwith[item], i_Num, 200);
Console.WriteLine("with List: result= " + genlnkPLCworkingwith[item]);
}
//foreach (string item in genlnkPLCCanvas) genlnkPLCworkingwith[item] = String.Format(item, i_Num, 200);
Console.ReadKey();
}
}
If you want to modify the existing list, you have to use for loop instead of foreach one:
foreach (var item in list) ...
should be changed into
for (int i = 0; i < list.Count; ++i) {
var item = list[i]; // not necessary, but often convenient
...
list[i] = ... // modification
...
}
For instance
for (int i = 0; i < genlnkPLCCanvas.Count; ++i) {
var item = genlnkPLCCanvas[i];
genlnkPLCCanvas[i] = string.Format(item, i_StationNum, 200);
}
When testing try creating reports (put all the logic into the single readable query) and then printing them out in one go:
...
var withListReport = genlnkPLC
.Select(item => "with List: result = " + string.Format(item, i_StationNum, 200));
var withoutListReport = genlnkPLC
.Select(item => "without List: result = " + string.Format(item, i_StationNum, 200));
// now you can do whatever you want with the reports:
// - print them to console
// Console.WriteLine(string.Join(Envrironment.NewLine, withListReport));
// - save to file:
// File.WriteAllLines(#"C:\MyFile.txt", withListReport);
// - print to, say, WinForm UI:
// MyTextBox.Text = string.Join(Envrironment.NewLine, withListReport);
Console.WriteLine(string.Join(Envrironment.NewLine, withListReport));
Console.WriteLine(string.Join(Envrironment.NewLine, withoutListReport));
Console.ReadKey();
String.Format() returns a string, it doesn't change whatever you're formatting. Therefore, your first foreach (var item in genlnkPLC) creates temporary strings that are immediately destroyed.
foreach (var item in genlnkPLC)
{
Console.WriteLine("with List = " + String.Format(item, i_StationNum, 200));
}
In the statement
foreach (var item in genlnkPLC)
Console.WriteLine("with List: result= "+item);
you are not using String.Format to insert arguments into the members of genlnkPLC, which are apparently intended as format strings. You can use
foreach (var item in genlnkPLC)
Console.WriteLine("without List result = " + String.Format(item, i_StationNum, 200));
instead.
The problem is that you can't change the reference of the elements enumerated in a foreach loop. string is an inmutable object, so changing it replaces the old reference with a new one. If you want to change the elements in the list, you'll need to do it in a for loop, like this:
for (int item = 0; item < genlnkPLC.Count; item++)
genlnkPLC[item]= String.Format(genlnkPLC[item], i_StationNum, 200);
No need to repeat foreach (the first one, did nothing to your item). Try this:
foreach (var item in genlnkPLC)
Console.WriteLine("with List: result= "+ String.Format(item, i_StationNum, 200));
As M.Bychenko says: "If you want to modify the existing list, you have to use for loop instead of foreach one:"
And thanks for the report tipp!
class Program
{
static void Main(string[] args)
{
int i_Num = 0;
string Str_Num = "";
string[] linkToPLC = {"toto[{0}].test{1}", "tata[{0}].test{1}", "titi[{0}].test{1}"};
List<string> genlnkPLCCanvas = new List<string>(linkToPLC);
List<string> genlnkPLCworkingwith = new List<string>(linkToPLC);
Console.WriteLine("Insert Num: ");
Str_Num = Console.ReadLine();
i_Num = Convert.ToInt32(Str_Num);
for (int item = 0; item < genlnkPLCCanvas.Count; item++)
{
genlnkPLCworkingwith[item] = String.Format(genlnkPLCCanvas[item], i_Num, 200);
}
var CanvasListReport = genlnkPLCCanvas.Select(item => "Canvas List = " + item);
var WorkingListReport = genlnkPLCworkingwith.Select(item => "Working list = " + item);//string.Format(item, i_Num, 200));
// now you can do whatever you want with the reports:
// - print them to console
// Console.WriteLine(string.Join(Envrironment.NewLine, withListReport));
// - save to file: File.WriteAllLines(#"C:\MyFile.txt", withListReport);
// - print to, say, WinForm UI:
// MyTextBox.Text = string.Join(Envrironment.NewLine, withListReport)
Console.WriteLine(string.Join(Environment.NewLine, CanvasListReport));
Console.WriteLine(string.Join(Environment.NewLine, WorkingListReport));
Console.ReadKey();
}
}
This is because
String.Format(item, i_StationNum, 200)
doesn't change the string in the list.
You have to assign the String.Format result to your item.

Generate combinations of elements held in multiple list of strings in C#

I'm trying to automate the nested foreach provided that there is a Master List holding List of strings as items for the following scenario.
Here for example I have 5 list of strings held by a master list lstMaster
List<string> lst1 = new List<string> { "1", "2" };
List<string> lst2 = new List<string> { "-" };
List<string> lst3 = new List<string> { "Jan", "Feb" };
List<string> lst4 = new List<string> { "-" };
List<string> lst5 = new List<string> { "2014", "2015" };
List<List<string>> lstMaster = new List<List<string>> { lst1, lst2, lst3, lst4, lst5 };
List<string> lstRes = new List<string>();
foreach (var item1 in lst1)
{
foreach (var item2 in lst2)
{
foreach (var item3 in lst3)
{
foreach (var item4 in lst4)
{
foreach (var item5 in lst5)
{
lstRes.Add(item1 + item2 + item3 + item4 + item5);
}
}
}
}
}
I want to automate the below for loop regardless of the number of list items held by the master list lstMaster
Just do a cross-join with each successive list:
IEnumerable<string> lstRes = new List<string> {null};
foreach(var list in lstMaster)
{
// cross join the current result with each member of the next list
lstRes = lstRes.SelectMany(o => list.Select(s => o + s));
}
results:
List<String> (8 items)
------------------------
1-Jan-2014
1-Jan-2015
1-Feb-2014
1-Feb-2015
2-Jan-2014
2-Jan-2015
2-Feb-2014
2-Feb-2015
Notes:
Declaring lstRes as an IEnumerable<string> prevents the unnecessary creation of additional lists that will be thrown away
with each iteration
The instinctual null is used so that the first cross-join will have something to build on (with strings, null + s = s)
To make this truly dynamic you need two arrays of int loop variables (index and count):
int numLoops = lstMaster.Count;
int[] loopIndex = new int[numLoops];
int[] loopCnt = new int[numLoops];
Then you need the logic to iterate through all these loopIndexes.
Init to start value (optional)
for(int i = 0; i < numLoops; i++) loopIndex[i] = 0;
for(int i = 0; i < numLoops; i++) loopCnt[i] = lstMaster[i].Count;
Finally a big loop that works through all combinations.
bool finished = false;
while(!finished)
{
// access current element
string line = "";
for(int i = 0; i < numLoops; i++)
{
line += lstMaster[i][loopIndex[i]];
}
llstRes.Add(line);
int n = numLoops-1;
for(;;)
{
// increment innermost loop
loopIndex[n]++;
// if at Cnt: reset, increment outer loop
if(loopIndex[n] < loopCnt[n]) break;
loopIndex[n] = 0;
n--;
if(n < 0)
{
finished=true;
break;
}
}
}
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<IEnumerable<T>> lists)
{
IEnumerable<IEnumerable<T>> result = new List<IEnumerable<T>> { new List<T>() };
return lists.Aggregate(result, (current, list) => current.SelectMany(o => list.Select(s => o.Union(new[] { s }))));
}
var totalCombinations = 1;
foreach (var l in lstMaster)
{
totalCombinations *= l.Count == 0 ? 1 : l.Count;
}
var res = new string[totalCombinations];
for (int i = 0; i < lstMaster.Count; ++i)
{
var numOfEntries = totalCombinations / lstMaster[i].Count;
for (int j = 0; j < lstMaster[i].Count; ++j)
{
for (int k = numOfEntries * j; k < numOfEntries * (j + 1); ++k)
{
if (res[k] == null)
{
res[k] = lstMaster[i][j];
}
else
{
res[k] += lstMaster[i][j];
}
}
}
}
The algorithm starts from calculating how many combinations we need for all the sub lists.
When we know that we create a result array with exactly this number of entries. Then the algorithm iterates through all the sub lists, extract item from a sub list and calculates how many times the item should occur in the result and adds the item the specified number of times to the results. Moves to next item in the same list and adds to remaining fields (or as many as required if there is more than two items in the list). And it continues through all the sub lists and all the items.
One area though that needs improvement is when the list is empty. There is a risk of DivideByZeroException. I didn't add that. I'd prefer to focus on conveying the idea behind the calculations and didn't want to obfuscate it with additional checks.

Counting words using LinkedList

I have a class WordCount which has string wordDic and int count. Next, I have a List.
I have ANOTHER List which has lots of words inside it. I am trying to use List to count the occurrences of each word inside List.
Below is where I am stuck.
class WordCount
{
string wordDic;
int count;
}
List<WordCount> usd = new List<WordCount>();
foreach (string word in wordsList)
{
if (usd.wordDic.Contains(new WordCount {wordDic=word, count=0 }))
usd.count[value] = usd.counts[value] + 1;
else
usd.Add(new WordCount() {wordDic=word, count=1});
}
I don't know how to properly implement this in code but I am trying to search my List to see if the word in wordsList already exists and if it does, add 1 to count but if it doesn't then insert it inside usd with count of 1.
Note: *I have to use Lists to do this. I am not allowed to use anything else like hash tables...*
This is the answer before you edited to only use lists...btw, what is driving that requirement?
List<string> words = new List<string> {...};
// For case-insensitive you can instantiate with
// new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
Dictionary<string, int> counts = new Dictionary<string, int>();
foreach (string word in words)
{
if (counts.ContainsKey(word))
{
counts[word] += 1;
}
else
{
counts[word] = 1;
}
}
If you can only use lists, Can you use List<KeyValuePair<string,int>> counts which is the same thing as a dictionary (although I'm not sure it would guarantee uniqueness). The solution would be very similar. If you can only use lists the following will work.
List<string> words = new List<string>{...};
List<string> foundWord = new List<string>();
List<int> countWord = new List<int>();
foreach (string word in words)
{
if (foundWord.Contains(word))
{
countWord[foundWord.IndexOf(word)] += 1;
}
else
{
foundWord.Add(word);
countWord.Add(1);
}
}
Using your WordCount class
List<string> words = new List<string>{...};
List<WordCount> foundWord = new List<WordCount>();
foreach (string word in words)
{
WordCount match = foundWord.SingleOrDefault(w => w.wordDic == word);
if (match!= null)
{
match.count += 1;
}
else
{
foundWord.Add(new WordCount { wordDic = word, count = 1 });
}
}
You can use Linq to do this.
static void Main(string[] args)
{
List<string> wordsList = new List<string>()
{
"Cat",
"Dog",
"Cat",
"Hat"
};
List<WordCount> usd = wordsList.GroupBy(x => x)
.Select(x => new WordCount() { wordDic = x.Key, count = x.Count() })
.ToList();
}
Use linq: Assuming your list of words :
string[] words = { "blueberry", "chimpanzee", "abacus", "banana", "abacus","apple", "cheese" };
You can do:
var count =
from word in words
group word.ToUpper() by word.ToUpper() into g
where g.Count() > 0
select new { g.Key, Count = g.Count() };
(or in your case, select new WordCount()... it'll depend on how you have your constructor set up)...
the result will look like:
First, all of your class member is private, thus, they could not be accessed somewhere out of your class. Let's assume you're using them in WordCount class too.
Second, your count member is an int. Therefore, follow statement will not work:
usd.count[value] = usd.counts[value] + 1;
And I think you've made a mistype between counts and count.
To solve your problem, find the counter responding your word. If it exists, increase count value, otherwise, create the new one.
foreach (string word in wordsList) {
WordCount counter = usd.Find(c => c.wordDic == word);
if (counter != null) // Counter exists
counter.count++;
else
usd.Add(new WordCount() { wordDic=word, count = 1 }); // Create new one
}
You should use a Dictionary as its faster when using the "Contains" method.
Just replace your list with this
Dictionary usd = new Dictionary();
foreach (string word in wordsList)
{
if (usd.ContainsKey(word.ToLower()))
usd.count[word.ToLower()].count++;
else
usd.Add(word.ToLower(), new WordCount() {wordDic=word, count=1});
}

Need help on a simple string sort method from a string array

seriously need some guideline on string sorting methodology. Perhaps, if able to provide some sample code would be a great help. This is not a homework. I would need this sorting method for concurrently checking multiple channel names and feed the channel accordingly based on the sort name/string result.
Firstly I would have the string array pattern something like below:
string[] strList1 = new string[] {"TDC1ABF", "TDC1ABI", "TDC1ABO" };
string[] strList2 = new string[] {"TDC2ABF", "TDC2ABI", "TDC2ABO"};
string[] strList3 = new string[] {"TDC3ABF", "TDC3ABO","TDC3ABI"}; //2nd and 3rd element are swapped
I would like to received a string[] result like below:
//result1 = "TDC1ABF , TDC2ABF, TDC3ABF"
//result2 = "TDC1ABI , TDC2ABI, TDC3ABI"
//result3 = "TDC1ABO , TDC2ABO, TDC3ABO"
Ok, here is my idea of doing the sorting.
First, each of the strList sort keyword *ABF.
Then, put all the strings with *ABF into result array.
Finally do Order sort to have the string array align into TDC1ABF, TDC2ABF, TDC3ABF accordingly.
Do the same thing for the other string array inside a loop.
So, my problem is.. how to search *ABF within a string inside a string array?
static void Main()
{
var strList1 = new[] { "TDC1ABF", "TDC1ABI", "TDC1ABO" };
var strList2 = new[] { "TDC2ABF", "TDC2ABI", "TDC2ABO" };
var strList3 = new[] { "TDC3ABF", "TDC3ABO", "TDC3ABI" };
var allItems = strList1.Concat(strList2).Concat(strList3);
var abfItems = allItems.Where(item => item.ToUpper().EndsWith("ABF"))
.OrderBy(item => item);
var abiItems = allItems.Where(item => item.ToUpper().EndsWith("ABI"))
.OrderBy(item => item);
var aboItems = allItems.Where(item => item.ToUpper().EndsWith("ABO"))
.OrderBy(item => item);
}
If you do something like this then you can compare all the sums and arrange them in order. The lower sums are the ones closer to 1st and the higher are the ones that are farther down.
static void Main(string[] args)
{
string[] strList1 = new string[] { "TDC1ABF", "TDC1ABI", "TDC1ABO" };
string[] strList2 = new string[] { "TDC2ABF", "TDC2ABI", "TDC2ABO" };
string[] strList3 = new string[] { "TDC3ABF", "TDC3ABO", "TDC3ABI" };
arrange(strList1);
arrange(strList2);
arrange(strList3);
}
public static void arrange(string[] list)
{
Console.WriteLine("OUT OF ORDER");
foreach (string item in list)
{
Console.WriteLine(item);
}
Console.WriteLine();
for (int x = 0; x < list.Length - 1; x++)
{
char[] temp = list[x].ToCharArray();
char[] temp1 = list[x + 1].ToCharArray();
int sum = 0;
foreach (char letter in temp)
{
sum += (int)letter; //This adds the ASCII value of each char
}
int sum2 = 0;
foreach (char letter in temp1)
{
sum2 += (int)letter; //This adds the ASCII value of each char
}
if (sum > sum2)
{
string swap1 = list[x];
list[x] = list[x + 1];
list[x + 1] = swap1;
}
}
Console.WriteLine("IN ORDER");
foreach (string item in list)
{
Console.WriteLine(item);
}
Console.WriteLine();
Console.ReadLine();
}
If the arrays are guaranteed to have as many elements as there are arrays then you could sort the individual arrays first, dump the sorted arrays into an nxn array and then transpose the matrix.

Categories