Shuffle string array without duplicates - c#

I am using the Knuth-Fisher-Yates algorithm to display a shuffled array of string items on a windows form. I do not get any duplicates, which is what I was trying to achieve, however, it only spits out 12 of the 13 elements of the array. How can I get it to display all 13 elements of the array? Here is my code:
private void FormBlue1_Load(object sender, EventArgs e)
{
// set the forms borderstyle
this.FormBorderStyle = FormBorderStyle.Fixed3D;
// create array of stationOneParts to display on form
string[] stationOneParts = new string[13];
stationOneParts[0] = "20-packing";
stationOneParts[1] = "5269-stempad";
stationOneParts[2] = "5112-freeze plug";
stationOneParts[3] = "2644-o'ring";
stationOneParts[4] = "5347-stem";
stationOneParts[5] = "4350-top packing";
stationOneParts[6] = "5084-3n1 body";
stationOneParts[7] = "4472-packing washer";
stationOneParts[8] = "3744-vr valve o'ring";
stationOneParts[9] = "2061-packing spring";
stationOneParts[10] = "2037-packing nut";
stationOneParts[11] = "2015-latch ring";
stationOneParts[12] = "stem assembly";
Random parts = new Random();
// loop through stationOneParts using a Swap method to shuffle
labelBlueOne.Text = "\n";
for (int i = stationOneParts.Length - 1; i > 0; i--)
{
int j = parts.Next(i + 1);
Swap(ref stationOneParts[i], ref stationOneParts[j]);
// display in a random order
labelBlueOne.Text += stationOneParts[i] + "\n";
}
}
private void Swap(ref string firstElement, ref string secondElement)
{
string temp = firstElement;
firstElement = secondElement;
secondElement = temp;
}

You don't access the first element.
for (int i = stationOneParts.Length - 1; i >= 0; i--).

As you are showing the texts using the loop that swaps the items, you will not show the last item, because it's never swapped by itself.
Just show the last item after the loop:
labelBlueOne.Text += stationOneParts[0] + "\n";
Alternatively, you can display all the items outside the loop that shuffles them:
for (int i = stationOneParts.Length - 1; i > 0; i--) {
Swap(ref stationOneParts[i], ref stationOneParts[parts.Next(i + 1)]);
}
labelBlueOne.Text = "\n" + String.Join("\n", stationOneParts);

Change your loop condition to i >= 0.

Simpliest approach :
Random rnd = new Random();
var stationOneParts = new List<string>{
"20-packing",
"5269-stempad",
"5112-freeze plug",
"2644-o'ring",
"5347-stem",
"4350-top packing",
"5084-3n1 body",
"4472-packing washer",
"3744-vr valve o'ring",
"2061-packing spring",
"2037-packing nut",
"2015-latch ring",
"stem assembly"}.OrderBy(s => rnd.Next());
labelBlueOne.Text = string.Join(Environment.NewLine, stationOneParts);

Since you mention C# 4.0, why not write is C#-ish?
using System.Linq;
// ...
var stationOneParts = new [] { "20-packing",
"5269-stempad",
"5112-freeze plug",
"2644-o'ring",
"5347-stem",
"4350-top packing",
"5084-3n1 body",
"4472-packing washer",
"3744-vr valve o'ring",
"2061-packing spring",
"2037-packing nut",
"2015-latch ring",
"stem assembly" };
Random rand = new Random();
stationOneParts = stationOneParts
.Distinct() // see subject: '... without duplicates'
.Select(i => new { i, key=rand.Next() })
.OrderBy(p => p.key)
.Select(p => p.i)
.ToArray();

Related

How can the rows of a list be divided into the largest possible parts (according to the row limit) and the most equal row packages?

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

Why a method is modifying a List<string> variable in my Load Page?

I want to create two List with methods, the first method create a new list, and the second modify the first one but return a new one. For example, the first list has 200 items, and after add and delete some items in the second method, the returned one has 120 items.
But the second method is actually modifying the first list (now both list has 120 items).
What am I doing wrong?.
PD. I'm learning
protected void Page_Load(object sender, EventArgs e)
{
List<string> firstList = OEEClass.GetCompleteListDocument(DateTime.Today, "BZX"); // Let say it has 200 items
List<string> modifiedList = OEEClass.ModifyList(firstList); // The returned list has less items
}
public class OEEClass
{
public static List<string> GetCompleteListDocument(DateTime Fecha, string noMaquina)
{
var rawDoc = new HtmlDocument();
var tempList = new List<string>();
string url = #"C:/www/WPCS-Feedback/" + Fecha.Year + "/" + Fecha.Month + "/" + Fecha.Day.ToString("d2") + "/" + "Production state data.htm";
if (File.Exists(url) == true)
{
rawDoc.Load(url);
string cleanString = rawDoc.DocumentNode.InnerText.Trim().Replace(" ", "");
cleanString = Regex.Replace(cleanString, #"^\s+$[\r\n]*", string.Empty, RegexOptions.Multiline);
tempList = Regex.Split(cleanString, "\r\n|\r|\n").ToList();
tempList.RemoveRange(0, 5);
for (int j = 0; j < tempList.Count; j++)
{
if (tempList[j].StartsWith("ProductionTerminated") || tempList[j].StartsWith("ProductionInterrumpted"))
{
tempList.Insert(j + 4, "PressSingleCycleActivated=0");
}
}
}
return tempList;
}
public static List<string> ModifyList(List<string> completeListDocument)
{
for (int i = 0; i < completeListDocument.Count; i++)
{
if (completeListDocument[i].StartsWith("MachineSetup"))
{
completeListDocument.RemoveRange(i, 6);
i--;
}
}
return completeListDocument;
}
}
The simplest thing you can do is make a copy of your list before modifying it:
public static List<string> ModifyList(List<string> completeListDocument)
{
var results = new List<string>(completeListDocument);
for (int i = 0; i < results.Count; i++)
{
if (results[i].StartsWith("MachineSetup"))
{
results.RemoveRange(i, 6);
i--;
}
}
return results;
}

Find values which sum to 0 in Excel with many items

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;
}
}
}

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);

How do I make List of Lists? And then add to each List values?

class ExtractLinks
{
WebClient contents = new WebClient();
string cont;
List<string> links = new List<string>();
List<string> FilteredLinks = new List<string>();
List<string> Respones = new List<string>();
List<List<string>> Threads = new List<List<string>>();
public void Links(string FileName)
{
HtmlDocument doc = new HtmlDocument();
doc.Load(FileName);
foreach (HtmlNode link in doc.DocumentNode.SelectNodes("//a[#href]"))
{
HtmlAttribute att = link.Attributes["href"];
if (att.Value.StartsWith("http://rotter.net/forum/scoops1"))
{
links.Add(att.Value);
}
}
for (int i = 0; i < links.Count; i++)
{
int f = links[i].IndexOf("#");
string test = links[i].Substring(0, f);
FilteredLinks.Add(test);
}
for (int i = 0; i < FilteredLinks.Count; i++)
{
contents.Encoding = System.Text.Encoding.GetEncoding(1255);
cont = contents.DownloadString(FilteredLinks[i]);
GetResponsers(cont);
}
}
private void GetResponsers(string contents)
{
int f = 0;
int startPos = 0;
while (true)
{
string firstTag = "<FONT CLASS='text16b'>";
string lastTag = "&n";
f = contents.IndexOf(firstTag, startPos);
if (f == -1)
{
break;
}
int g = contents.IndexOf(lastTag, f);
startPos = g + lastTag.Length;
string responser = contents.Substring(f + firstTag.Length, g - f - firstTag.Length);
foreach (List<string> subList in Threads)
{
}
}
}
}
I created this variable :
List<List<string>> Threads = new List<List<string>>();
The first thing I don't know yet how to do is how to create inside Threads number of Lists according to the FilteredLinks.Count inside the Links method.
Second thing is in the GetResponsers method I did:
foreach (List<string> subList in Threads)
{
}
But what I want is that first time it will add all the values from variable responser to the first List in Threads. Then when it's getting to the break; it stop then and then in the Links methods its calling GetResponsers(cont); again this time I want that all the values in responser to be added to the second List in Threads.
I know that each time it's getting to the break; it will get the next FilteredLink from FilteredLinks.
How do I create number of Lists in Threads according to the FilteredLinks.Count?
How do I make the code in GetResponsers to add the responser ?
You don't need to specify the count for the number of lists in Threads, since it is a list, you can simply keep adding lists to it. So the first part is correct where you are declaring it.
The second part --> Your calling method will change. Look below for the calling method.
The third part --> Change private void GetResponsers(string contents) to private void GetResponsers(List threadList, string contents). Look below for implementation change.
Also the loop will look like this then
//other code you have
List<List<string>> Threads = new List<List<string>>();
public void Links(string FileName)
{
// ...other code you have
for (int i = 0; i < FilteredLinks.Count; i++)
{
threads.Add(new List<string>);
contents.Encoding = System.Text.Encoding.GetEncoding(1255);
cont = contents.DownloadString(FilteredLinks[i]);
GetResponsers(threads[threads.Count - 1], cont);
}
}
private void GetResponsers(List<string> threadList, string contents)
{
int f = 0;
int startPos = 0;
while (true)
{
string firstTag = "<FONT CLASS='text16b'>";
string lastTag = "&n";
f = contents.IndexOf(firstTag, startPos);
if (f == -1)
{
break;
}
int g = contents.IndexOf(lastTag, f);
startPos = g + lastTag.Length;
string responser = contents.Substring(f + firstTag.Length, g - f - firstTag.Length);
threadList.Add(responser);
}
}
PS: Please excuse the formatting.
How do i make List of Lists ? And then add to each List values?
The following codesnippet demonstrates you, how to handle List<List<string>>.
List<List<string>> threads = new List<List<string>>();
List<string> list1 = new List<string>();
list1.Add("List1_1");
list1.Add("List1_2")
threads.Add(list1);
List<string> list2 = new List<string>();
list1.Add("List2_1");
list1.Add("List2_2")
list1.Add("List2_3")
threads.Add(list2);
How do i create number of Lists in Threads according to the
FilteredLinks.Count ?
for(int i = 0; i < FilteredLinks.Count; i++)
{
var newList = new List<string>();
newList.Add("item1"); //add whatever you wish, here.
newList.Add("item2");
Threads.Add(newList);
}
I'm afraid I can't help you with Question #2, since I don't understand what you try to achieve there exactly.

Categories