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);
Related
RowLimit: 4
Example 1:
List with 10 rows
The desired result: 4, 3, 3
Example 2:
List with 9 rows
The desired result: 3, 3, 3
Example 3:
List with 8 rows
The desired result: 4, 4
Example 4:
List with 7 rows
The desired result: 4, 3
At the moment I simply divide the rows according to the rows limit
public List<List<string>> TextWithRowLimit(List<string> TextRows, int RowLimit)
{
List<List<string>> list = new List<List<string>>();
int RowLimitCounter = 1;
List<string> TextRowPackages = new List<string>();
for (int row = 0; row < TextRows.Count; ++row)
{
TextRowPackages.Add($"{TextRows[row]} ");
if (RowLimitCounter == RowLimit)
{
list.Add(TextRowPackages);
RowLimitCounter = 0;
TextRowPackages = new List<string>();
}
RowLimitCounter++;
}
if(TextRowPackages.Count>0)
list.Add(TextRowPackages);
return list;
}
Probably not the prettiest code, but I think this will work for you.
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var initialList = Enumerable.Range(1, 10).Select(i => i.ToString()).ToList();
var limit = 4;
var bucketUtility = new BucketUtility();
var list = bucketUtility.TextWithRowLimit(initialList, limit);
}
}
public sealed class BucketUtility
{
public List<List<string>> TextWithRowLimit(List<string> textRows, int rowLimit)
{
var totalRowsCount = textRows.Count;
var remainder = textRows.Count % rowLimit;
var totalBucketsNeeded = remainder == 0 ? totalRowsCount / rowLimit : (totalRowsCount / rowLimit) + 1;
// calculate how many would fill buckets
var initialFillCount = totalBucketsNeeded * rowLimit;
// this can be negative
var remainingToFill = totalRowsCount - initialFillCount;
// size the buckets correctly
var buckets = SizeBuckets(totalBucketsNeeded, rowLimit, remainingToFill);
// assign text values to buckets
int skip = 0;
var result = buckets.Select((bucketSize, i) =>
{
var result = textRows.Select(t => t).Skip(skip).Take(bucketSize).ToList();
skip += bucketSize;
return result;
}).ToList();
return result;
}
private List<int> SizeBuckets(int totalBucketsNeeded, int rowLimit, int remainingToFill)
{
var totalBuckets = Enumerable.Range(0, totalBucketsNeeded).ToList();
var buckets = new List<int>();
for (int i = 0; i < totalBuckets.Count; i++)
{
int bucketSize = rowLimit;
if (remainingToFill > 0)
{
bucketSize++;
remainingToFill--;
}
if (remainingToFill < 0)
{
bucketSize--;
remainingToFill++;
}
buckets.Add(bucketSize);
}
// order them in correct order
return buckets.OrderByDescending(i => i).ToList();
}
}
Can run it here
https://dotnetfiddle.net/CxdHaO
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: " });
I have to find each subset in a enough big list, 500/1000 items that are positive and negative and are decimal, whiches sum to 0. I'm not an expert so I read many and many articles and solutions, and then I wrote my code. Datas comes from Excel worksheet and I would to mark found sums there.
Code works in this way:
Initally I find all pair that sum to 0
Then I put the remains sums into a list and take the combinations within 20 items, beacause I know the it is not possible bigger combination sum to 0
In these combinations I search if one combinations sums to 0 and save it in result list, else save sum in dictionary as key and then I'll search if dictionary contains next sums (so I check pairs of these subsets)
I keep track of the index so I can reach and modify the cells
To found solutions is enough fast but when I want elaborate the results in Excel become really slow. I don't take care about find all solutions but I want to find as max as possible in a short time.
What do you think about this solution? How can I improve the speed? How can I skip easly the sums that are already taken? And how can mark the cells fastly in my worksheet, beacuse now here is the bottleneck of the program?
I hope it is enough clear :) Thanks to everybody for any help
Here my code of the combination's part:
List<decimal> listDecimal = new List<decimal>();
List<string> listRange = new List<string>();
List<decimal> resDecimal = new List<decimal>();
List<IEnumerable<decimal>> resDecimal2 = new List<IEnumerable<decimal>>();
List<IEnumerable<string>> resIndex = new List<IEnumerable<string>>();
Dictionary<decimal, int> dicSumma = new Dictionary<decimal, int>();
foreach (TarkistaSummat.CellsRemain el in list)
{
decimal sumDec = Convert.ToDecimal(el.Summa.Value);
listDecimal.Add(sumDec);
string row = el.Summa.Cells.Row.ToString();
string col = el.Summa.Cells.Column.ToString();
string range = el.Summa.Cells.Row.ToString() + ":" + el.Summa.Cells.Column.ToString();
listRange.Add(range);
}
var subsets = new List<IEnumerable<decimal>> { new List<decimal>() };
var subsetsIndex = new List<IEnumerable<string>> { new List<string>() };
for (int i = 0; i < list.Count; i++)
{
if (i > 20)
{
List<IEnumerable<decimal>> parSubsets = subsets.GetRange(i, i + 20);
List<IEnumerable<string>> parSubsetsIndex = subsetsIndex.GetRange(i, i + 20);
var Z = parSubsets.Select(x => x.Concat(new[] { listDecimal[i] }));
//var Zfound = Z.Select(x => x).Where(w => w.Sum() ==0);
subsets.AddRange(Z.ToList());
var Zr = parSubsetsIndex.Select(x => x.Concat(new[] { listRange[i] }));
subsetsIndex.AddRange(Zr.ToList());
}
else
{
var T = subsets.Select(y => y.Concat(new[] { listDecimal[i] }));
//var Tfound = T.Select(x => x).Where(w => w.Sum() == 0);
//resDecimal2.AddRange(Tfound);
//var TnotFound = T.Except(Tfound);
subsets.AddRange(T.ToList());
var Tr = subsetsIndex.Select(y => y.Concat(new[] { listRange[i] }));
subsetsIndex.AddRange(Tr.ToList());
}
for (int i = 0; i < subsets.Count; i++)
{
decimal sumDec = subsets[i].Sum();
if (sumDec == 0m)
{
resDecimal2.Add(subsets[i]);
resIndex.Add(subsetsIndex[i]);
continue;
}
else
{
if(dicSumma.ContainsKey(sumDec * -1))
{
dicSumma.TryGetValue(sumDec * -1, out int index);
IEnumerable<decimal> addComb = subsets[i].Union(subsets[index]);
resDecimal2.Add(addComb);
var indexComb = subsetsIndex[i].Union(subsetsIndex[index]);
resIndex.Add(indexComb);
}
else
{
if(!dicSumma.ContainsKey(sumDec))
{
dicSumma.Add(sumDec, i);
}
}
}
}
for (int i = 0; i < resIndex.Count; i++)
{
//List<Range> ranges = new List<Range>();
foreach(string el in resIndex[i])
{
string[] split = el.Split(':');
Range cell = actSheet.Cells[Convert.ToInt32(split[0]), Convert.ToInt32(split[1])];
cell.Interior.ColorIndex = 6;
}
}
}
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.
I have a collection. And in this collection, if a duplicate is added, I want to append the text " - N" (Where N is an integer that is not used by a current item in the collection).
For Example, if I have the following list:
item1
item2
and try to add 'item1' again, I want the list to end up like so:
item1
item2
item1 - 1
If I try to add 'item1' again, the list will then be:
item1
item2
item1 - 1
item1 - 2
Pretty straight forward. Below is my simple algorithm, but I'm getting a noticeable loss in performance when dealing with 10,000 items. Obviously that's going to happen somewhat, but are there better approaches to this? Couldn't find any similar question asked, so figure I'd see if anyone has ran into a similar issue.
Item copyItem = new Item();
string tempName = name;
int copyNumber = 1;
while(copyItem != null)
{
copyItem = MyCollection.FirstOrDefault(blah => blah.Name == tempName);
if (copyItem == null)
{
name = tempName;
break;
}
tempName = name + " - " + copyNumber;
++copyNumber;
}
I would firstly sort the values - thanks to this you only need to make a check with the previous value and not with the whole collection.
So it could look like this:
List<string> values = new List<string> { "item1", "item1", "item1" };
values.Sort();
string previousValue = string.Empty;
int number = 1;
for(int i = 0 ; i < values.Count; i ++)
{
if (values[i].Equals(previousValue))
{
previousValue = values[i];
values[i] = values[i] + "-" + number;
number++;
}
else
{
previousValue = values[i];
number = 1;
}
}
I would use a Dictionary<string, int> to store the number of duplicates for a particular item. So a helper method would look something like this:
Dictionary<string, int> countDictionary = new Dictionary<string, int>(); // case sensitive!
string GetNameForItem(string itemName)
{
var name = itemName;
var count = 0;
countDictionary.TryGetValue(itemName, out count);
if (count > 0)
name = string.Format("{0} - {1}", itemName, count);
countDictionary[itemName] = count + 1;
return name;
}
Alternatively, you could split up the operation into several methods if you didn't want GetNameForItem to automatically increment on retrieval:
int GetCountForItem(string itemName)
{
var count = 0;
countDictionary.TryGetValue(itemName, out count);
return count;
}
string GetNameForItem(string itemName)
{
var name = itemName;
var count = GetCountForItem(itemName);
if (count > 0)
name = string.Format("{0} - {1}", itemName, count);
return name;
}
int IncrementCountForItem(string itemName)
{
var newCount = GetCountForItem(itemName) + 1;
countDictionary[itemName] = newCount;
return newCount;
}
It is important to note that if you are supporting deletion from the collection, you will have to update the count accordingly:
int DecrementCountForItem(string itemName)
{
var newCount = Math.Max(0, GetCountForItem(itemName) - 1); // Prevent count from going negative!
countDictionary[itemName] = newCount;
return newCount;
}
You will also have to keep in mind what happens if you have two items, say "Item A" and "Item A - 1", then you delete "Item A". Should you rename "Item A - 1" to "Item A"?
Okay so you need an iterator per value and not a global one. This code will do the thing.
// Inputs for Tests purpose
List<string> values = new List<string> { "item1", "item2", "item1", "item1" };
// Result data
List<string> finalResult = new List<string>();
// 1 - Group by item value
var tempResult = from i in values
group i by i;
// We loop over all different item name
foreach (var curItem in tempResult)
{
// Thanks to the group by we know how many item with the same name exists
for (int ite = 0; ite < curItem.Count(); ite++)
{
if (ite == 0)
finalResult.Add(curItem.Key);
else
finalResult.Add(string.Format("{0} - {1}", curItem.Key, ite));
}
}
Thanks to LINQ you can reduce the amount of code, next code will do exactly the same thing and should be also quickier because I use the ToList() method so the LINQ query will not have a deferred execution.
// Inputs for Tests purpose
List<string> values = new List<string> { "item1", "item2", "item1", "item1" };
// Result data
List<string> finalResult = new List<string>();
values.GroupBy<string, string>(s1 => s1).ToList().ForEach(curItem =>
{
for (int ite = 0; ite < curItem.Count(); ite++)
{
finalResult.Add(ite == 0 ? curItem.Key : string.Format("{0} - {1}", curItem.Key, ite));
}
});