Sorting and iterating widget starting from anywhere - c#

List<Widget> list = List<Widget>();
Widget widgetA = new Widget();
Widget widgetB = new Widget();
Widget widgetC = new Widget();
Widget widgetD = new Widget();
Widget widgetE = new Widget();
Widget widgetF = new Widget();
list.AddRange(new[] { widgetA, widgetB, widgetC, widgetD, widgetE, widgetF });
What would I need to do to sort starting somewhere in the middle?
for example,
list.AddRange(new[] { widgetD, widgetE, widgetF, widgetA, widgetB, widgetC });
Or, in other words, start the index somewhere in the middle and then wrap the remaining objects in order.

You can use a combination of Skip, Take and Union.
List<Widget> widgets = ...
// now to split and wrap
int startIndex = 3;
var newList = widgets.Skip(startIndex)
.Union(widgets.Take(startIndex)
.ToList();

First sort them, then take first few elements and put them at the end.
var mid = 2; // starting point
var arr = new[] { widgetA, widgetB, widgetC, widgetD, widgetE, widgetF };
arr.Sort(); //you can use your own comparator for sorting
list.AddRange(arr.Skip(mid).Concat(arr.Take(mid)));

Related

Using a case statement while creating a list

I have a list which is a bunch of functions as displayed below
var listOfSteps = new List<StepList>
{
new Setup(parameters),
new InitializeClass(parameters),
new calculate(parameters),
//72 steps in total
}
I have to add another function to the list based on a condition.
int result = select something from someTable
var listOfSteps = new List<StepList>
{
new Setup(parameters),
new InitializeClass(parameters),
new calculate(parameters),
if (result = 5) {new display(parameters)}, //This is the additional step to be added
//72 steps in total
}
I am trying to avoid creating 2 different sets for a simple if condition like below
if(result = 5)
{
var listOfSteps = new List<StepList> //list with the new function
{
new Setup(parameters),
new InitializeClass(parameters),
new calculate(parameters),
new display(parameters),
//72 steps in total
}
}
else
{ //list without the new function
new Setup(parameters),
new InitializeClass(parameters),
new calculate(parameters),
//72 steps in total
}
Is there a way to implement this condition within the list creation?
Collection initializers are just syntactic sugar. Example:
var ints = new List<int> { 1, 2, 3 };
// is equivalent to
var temp = new List<int>();
temp.Add(1);
temp.Add(2);
temp.Add(3);
var ints = temp;
This means that you can simply add this new item conditionally afterwards:
var listOfSteps = new List<StepList>
{
new Setup(parameters),
new InitializeClass(parameters),
new calculate(parameters),
...
}
if (result == 5) {
listOfSteps.Add(new display(parameters));
}
I think it would be very readable if you can add a new kind of step - "do nothing":
// shouldn't StepList be renamed to "Step"?
class DoNothing: StepList {
...
// implement this so that when the list of steps is processed,
// this will do nothing
}
Then, you can use a simple ternary operator:
var listOfSteps = new List<StepList>
{
new Setup(parameters),
new InitializeClass(parameters),
new calculate(parameters),
result == 5 ?
new display(parameters)
:
new DoNothing(),
}
It would be even more readable if you can make factory methods for all your steps, so that you don't have the word new all the time.
You can also use a switch expression:
var listOfSteps = new List<StepList>
{
new Setup(parameters),
new InitializeClass(parameters),
new calculate(parameters),
result switch {
5 => new display(parameters),
_ => new DoNothing(),
}
}
which would be very useful if you have more complicated decisions to make.
If the number of steps in the list of steps is important, you can always just remove the DoNothing steps from the list with a Where when you check the count.
listOfSteps.Where(step => step is not DoNothing).Count()
I suppose that StepList is a class which causes some operation to be performed when its object is encountered. The problem you are facing is that, depending on external condition, one StepList object should be added or not, and that complicates List initialization, right?
If that is the issue, then you may introduce a special "do nothing" StepList subclass, e.g. called Empty. Your list initialization would then look like this:
var listOfSteps = new List<StepList>
{
new Setup(parameters),
new InitializeClass(parameters),
new calculate(parameters),
(result == 5 ? new display(parameters) : new Empty())
// ... 72 steps in total
}
With this design, your list will always have the same number of items, but the result of their execution would depend on external condition.
I would use a method with IEnumerable as return type and do yield returns where necessary.
eg.
private IEnumerable<StepList> GetSteps()
{
yield return new Setup(parameters);
yield return new InitializeClass(parameters);
yield return new calculate(parameters);
if (result = 5) yield return new display(parameters);
...
}
and than intialize the list like this:
var listOfSteps = GetSteps().ToList();
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield

How to elegantly populate a heterogeneous array in c#

I have 3 different classes (1 parent and 2 children), and I have created a heterogeneous array:
Parent[] parentArray = new Parent[9];
I would like to populate this array with 3 Parent objects, 3 child1 objects, and 3 child2 objects, in that order.
I would like to know if there is a more elegant way to do it than just doing this:
parentArray[0] = new Parent();
parentArray[1] = new Parent();
parentArray[2] = new Parent();
parentArray[3] = new Child1();
parentArray[4] = new Child1();
....
parentArray[9] = new Child2();
Thanks!
Like this?
var parentArray = new Parent[]
{
new Parent(),
new Parent(),
new Parent(),
new Child1(),
...
};
Personal i just think you should use the object initializer. However for sheer boredom you can use Linq and a generic factory method.
// Given
public static IEnumerable<T> Factory<T>(int count) where T : Parent, new()
=> Enumerable.Range(0, count).Select(x => new T());
...
// Usage
var array = Factory<Parent>(3)
.Union(Factory<Child1>(3))
.Union(Factory<Child2>(3))
.ToArray();
In this situation, you want to perform a certain number of actions repeatedly. Loops are commonly used to do this. Because you're initializing an array, for loops are a good fit, as they expose an integer that can be used to index the array. In your scenario, you can use three such loops.
Parent[] parentArray = new Parent[9];
for (int i = 0; i < 3; i++)
{
parentArray[i] = new Parent();
}
for (int i = 3; i < 6; i++)
{
parentArray[i] = new Child1();
}
for (int i = 6; i < 9; i++)
{
parentArray[i] = new Child2();
}

C# Chart - multiple curves but color gets overwritten

So i'm displaying multiple curves in my chart. However, when i generate a random color, all the other curves will get this color too.
int fileIndex=0;
Random r = new Random();
foreach (var i in graphContainer)
{
fileIndex++;
var series = new Series
{
Name = legendNames[fileIndex],
Color = Color.FromArgb(r.Next(0, 256), r.Next(0, 256), r.Next(0, 256)),
IsVisibleInLegend = true,
IsXValueIndexed = false,
ChartType = SeriesChartType.Line
};
foreach (var j in i)
{
series.Points.AddXY (j.Item2, j.Item1);
}
chart.Invalidate ();
chart.Series.Add (series);
}
Note: all the curves except one has the same values, but you get the idea.
Why is my curves getting the same colors?
Entire function:
private void generateWaveformsFromFileBtn_Click(object sender, EventArgs e)
{
int fileIndex = -1;
string folder = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + #"\WaveForms\";
string filter = "*.csv";
string[] filePath = Directory.GetFiles(folder, filter);
List<string> legendNames = new List<string>();
List<Tuple<double, double>> graph = new List<Tuple<double, double>>();
List<List<Tuple<double, double>>> graphContainer = new List<List<Tuple<double, double>>>();
chart.Series.Clear();
foreach(var fileName in filePath) {
legendNames.Add(Path.GetFileName(fileName));
using (TextFieldParser csvParser = new TextFieldParser(fileName))
{
csvParser.SetDelimiters (new string[] { ";" });
csvParser.ReadLine ();
while (!csvParser.EndOfData)
{
string[] fields = csvParser.ReadFields();
double current = Double.Parse(fields[0]);
double inductance = Double.Parse(fields[1]);
graph.Add (new Tuple<double,double>(current, inductance));
}
graphContainer.Add(graph);
}
}
Random r = new Random();
foreach (var i in graphContainer)
{
fileIndex++;
var series = new Series
{
Name = legendNames[fileIndex],
Color = Color.FromArgb(r.Next(0, 256), r.Next(0, 256), r.Next(0, 256)),
IsVisibleInLegend = true,
IsXValueIndexed = false,
ChartType = SeriesChartType.Line
};
foreach (var j in i)
{
series.Points.AddXY (j.Item2, j.Item1);
}
chart.Invalidate ();
chart.Series.Add (series);
}
}
Suggestion: (?)
You have already posted one solution:
Before adding new data in a loop, make sure the old data are deleted. You do it by creating a new instance:
graph = new List<Tuple<double, double>>();
This is a valid approach.
But for completeness' sake let's look at the more direct aproach as well:
graph.Clear();
This clears the data from the list object. But you are adding the graph to the graphContainer list like this:
graphContainer.Add(graph);
which means that the very object gets put into the list which, before the next loop, will be cleared. This is a common mistake. The solution is simple: Don't add the graph object itself into the graphContainer list but a copy of it :
graphContainer.Add(graph.ToList());
The ToList() call is a simple way to create a copy of a List<T>.
As for the post you linked to: The old post is strictly about runtime performance. And not about creating copies of the lists.
But even there, the recommended way to do it is depending on whether you can predict the number of elements you will put in each list.
If you can predict it and if it is somewhat largish, let's say more than a few hundred of large or a many thousands of smallish elements you should tell the list which capacity it should reserve.
This avoids multiple growing pains. After a Clear() the Capacity is retained, avoiding new allocations.
Your elements (Tuples) are small, so you probably don't need to worry about the list growing performance..
As suggested by user #Taw, i forgot to create a new instance of the graph or empty it, between runs.
graph = new List<Tuple<double, double>>(); // <-- solved it
while (!csvParser.EndOfData)
{
string[] fields = csvParser.ReadFields();
...
}
EDIT:
Do not use yourList.Clear() see here why: https://stackoverflow.com/a/35848659/2902996

Control over random value

I use a random for browse list I do like this:
var oServer = new SmtpServer("");
var random = new Random();
var randomIndex = random.Next(list.Count());
var socks = list[randomIndex];
oServer.SocksProxyServer = socks.IpAddress.ToString();
oServer.SocksProxyPort = socks.Port;
I want that if value selected, will be not selected again in next. I want that it choose another value.
What I should do?
If you can't remove from list itself, remove from possibleIndexes:
private static random = new Random();
...
List<int> possibleIndexes = Enumerable
.Range(0, list.Count())
.ToList();
...
int idx = random.Next(possibleIndexes.Count());
int index = possibleIndexes[idx];
possibleIndexes.RemoveAt(idx);
var socks = list[index];
...
Just remove the item from the list if you don't want to use it again:
var socks = list[randomIndex];
list.RemoveAt(randomIndex);
If you don't want to lose data in orginal list first copy the list to another one then remove item from your copy list
var copyList=new List<yourListType>();
copyList.AddRange(list);//copy form your orginal list
var socks = copyList[randomIndex];
copyList.RemoveAt(randomIndex);
Or you can generate unique random number
List<int> randomNumbers = new List<int>(list.Count());
do
{
randomIndex = random.Next();
} while (randomNumbers.Contains(randomIndex));
randomNumbers.Add(randomIndex);
var socks = list[randomIndex];

Assigning a button tag based on random items from an array

{
var buttonnameString = new List<string> { "button1", ... , "button12" };
for(int i = 0; i < 12; i++)
{
// Random car assignment to button
Random myRandom = new Random();
var carString = new List<string> { "Camaro", ... , "Model T" };
int index = myRandom.Next(carString.Count);
var name = carString[index];
carString.RemoveAt(index);
Tag = name.ToString();
}
}
In advance, thank you for any help that is provided. I'm a first year C# student so I know I have a lot to learn, but I've exhausted the extent of my google skills on trying to get this to work.
What I'm trying to do, is make a matching program. This program will have 12 buttons, labeled "button1", "button2"....etc. When the button is clicked, it will display it's tag, which is provided from a random array. I've gotten the random feature to work on assigning only a single buttons tag. Where I'm hung up at, is repeating this to all the buttons in a groupbox. I've tried the foreach venue, but couldn't get it to work successfully. Eventually I tried other methods as well. Below is where I've stopped at as I'm unsure on where to go. The two major questions that I have are
How do I assign the random string to a wildcard button tag?
What is the best way to accomplish randomly assigning 12 car names, to 12 buttons? The use of two arrays?
Try this:
{
var numOfButtons = 12;
var matchingButtonIndex = 0;
// Random car assignment to button
Random myRandom = new Random();
var buttons = new List<Button> { button1, ... , button12 };
var carString = new List<string> { "Camaro", ... , "Model T" };
while (matchingButtonIndex < numOfButtons)
{
int index = myRandom.Next(carString.Count);
var name = carString[index];
if (name != null)
{
buttons[matchingButtonIndex].Tag = name;
carString[index] = null;
matchingButtonIndex = matchingButtonIndex + 1;
}
}
}
Notice I have changed the button names array to an array of buttons. I have also emptied out the carString as I find unused car names.
Iterating over buttons is easy when you iterate through the form's Controls
var carString = new List<string> { "Camaro", "Mini Cooper", "Porsche 944", "Ford Focus", "Chevy Blazer", "Model T", "Camaro", "Mini Cooper", "Porsche 944", "Ford Focus", "Chevy Blazer", "Model T" };
foreach(Control c in Controls)
{
if (c is Button)
{
Random myRandom = new Random();
int index = myRandom.Next(carString.Count);
c.Tag = carString[index];
}
}

Categories