Using a case statement while creating a list - c#

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

Related

checking two lists contains same property values

I have two lists as follow
var topicsA = new List<Topic>()
{
new Topic(){ TopicCode = "T01", TopicAccessed = false },
new Topic(){ TopicCode = "T02", TopicAccessed = false },
new Topic(){ TopicCode = "T03", TopicAccessed = false }
};
var topicsB = new List<Topic>()
{
new Topic(){ TopicCode = "T01"},
new Topic(){ TopicCode = "T02"}
};
What is a quick way of setting the TopicAccessed value of topicsA to true where the property values of TopicCode are the same in topicsB
for example, in this case T01and T02 would have TopicAccessed set to true
Regards
All answers show quadratic algorithm complexity (Big O). This code snippet shows linear big O:
var accessedTopicsByCode = topicsB.ToDictionary(x => x.TopicCode);
foreach (var t in topicsA)
{
if (accessedTopicsByCode.ContainsKey(t.TopicCode))
{
t.TopicAccessed = true;
}
}
Use a dictionary to have better performance:
var hash = new Dictionary<string,bool>();
foreach(var topicB in topicsB)
{
hash[topicB.TopicCode] = true;
}
foreach(var topicA in topicsA)
{
topicA.TopicAccessed = hash.ContainsKey(topicA.TopicCode);
}
The complexity of this solution is O(n+m) whereas nested loop is O(n*m). ContainsKey's complexity is O(1)
Or you can also use a HashSet because we only need the key, not the value:
var hash = new HashSet<string>();
foreach(var topicB in topicsB)
{
hash.Add(topicB.TopicCode);
}
foreach(var topicA in topicsA)
{
topicA.TopicAccessed = hash.Contains(topicA.TopicCode);
}
This has the same complexity as using a Dictionary, but it's more efficient in memory usage. Contains' complexity is O(1)
You could try something like this:
// We iterate though the items of topicsA list.
foreach(var topicA in topicsA)
{
// If topicsB list contains any item with the same TopicCode like
// the current's item TopicCode, update the TopicAccessed.
if(topicsB.Any(x=>x.TopicCode == topicaA.TopicCode))
{
topicA.TopicAccessed = true;
}
}

Pass multiple parameters to a task

I wish to pass two BlockingCollection<>s to a task. I tried to put them in an object array and pass them but it doesn't work. Can anyone help me with this? The code where i am trying to pass the values is written below:
var lineHolders = new[]
{
new BlockingCollection<string>(linesCapacity),
new BlockingCollection<string>(linesCapacity),
new BlockingCollection<string>(linesCapacity),
new BlockingCollection<string>(linesCapacity)
};
var chunksHolder = new[]
{
new BlockingCollection<List<BsonDocument>>(chunksCapacity),
new BlockingCollection<List<BsonDocument>>(chunksCapacity)
};
for (var processors = 0; processors < 16; processors++)
{
var myLineHolder = lineHolders[processors%lineHolders.Length];
var myChunkHolder = chunksHolder[processors%chunksHolder.Length];
processorTaskArray[processors] = Task.Factory.StartNew((arg) =>
{
var lines = (BlockingCollection<string>) arg[0]; // compiler generates error here
var chunks = (BlockingCollection<List<BsonDocument>>) arg[1]; // compiler generates error here
// perform my work...
},
new object []
{
myLineHolder,
myChunkHolder
});
}
You're using the following overload of StartNew:
public Task StartNew(
Action<Object> action,
Object state
)
Since it's just an object you can't apply indexing on it. Cast it and it will work fine.
for (var processors = 0; processors < 16; processors++)
{
var myLineHolder = lineHolders[processors % lineHolders.Length];
var myChunkHolder = chunksHolder[processors % chunksHolder.Length];
processorTaskArray[processors] = Task.Factory.StartNew((arg) =>
{
var properArg = (object[]) arg;
var lines = (BlockingCollection<string>) properArg[0]; // compiler generates error here
var chunks = (BlockingCollection<List<BsonDocument>>) properArg[1]; // compiler generates error here
// perform my work...
},
new object[]
{
myLineHolder,
myChunkHolder
});
}

Flatten hierarchical list of types with selectmany linq

Wanted to see if anyone could offer any suggestions on how to condense the list aggregation while using selectmany. Any recommendations are welcome.
I want a flat list of all elements including the Program. Creating a list I can search through.
Thanks!
Program prog = new Program
{
Mods = new List<Mods>
{
new Mods
{
Acts = new List<Acts>
{
new Acts
{
},
new Acts
{
}
}
},
new Mods
{
Acts = new List<Acts>
{
new Acts
{
},
new Acts
{
}
}
},
}
};
//everything is an Element.
var pes = new List<Element> {prog};
pes.AddRange(prog.Mods.ToList<Element>());
pes.AddRange(prog.Mods.SelectMany(m => m.Acts).ToList<Element>());
How about this?
var pes =
Enumerable.Repeat<Element>(prog, 1)
.Concat(prog.Mods)
.Concat(prog.Mods.SelectMany(m => m.Acts))
.ToList();
This assumes IEnumerable<T> is covariant (C# 4.0 or greater). If it's not, you can do this instead:
var pes =
Enumerable.Repeat<Element>(prog, 1)
.Concat(prog.Mods.Cast<Element>())
.Concat(prog.Mods.SelectMany(m => (Element)m.Acts))
.ToList();

C#: Object Array with Constructor parameter

I have a class Measurement.
I have a constructor inside this class. as:
class Measurement
{
public Measurement(MainWindow mainWindow)
{
....
}
}
How can I create an Array of 8 Objects with the MainWindow Parameter?
Like somewhere in my code:
Measurement[] measurements= new Measurement[8](mainWin);
Do you want an array with a single reference 8 times, or 8 separate Measurement objects?
For the first:
var measurements = Enumerable.Repeat(new Measurement(mainWin), 8).ToArray();
For the second:
var measurements = Enumerable.Range(0, 8)
.Select(_ => new Measurement(mainWin))
.ToArray();
(Or just create an array without initializing the elements, then populate it with a loop, of course. It's a matter of personal preference.)
You can use LINQ:
var measurements = Enumerable.Range(0, 8).Select(i => new Measurement(mainWin)).ToArray();
A second way is to use the array initializer syntax:
var measurements = new[] {
new Measurements(mainWin), new Measurements(mainWin),
new Measurements(mainWin), new Measurements(mainWin),
new Measurements(mainWin), new Measurements(mainWin),
new Measurements(mainWin), new Measurements(mainWin)
};
There is this way (by using Enumerable.Repeat) :
var measurements = Enumerable.Repeat(new Measurement(mainWin), 8).ToArray();
Quote :
Generates a sequence that contains one repeated value.
Measurement[] measurements= new Measurement[8];
for(int i = 0; i < measurements.Length; i++)
{
measurements[i] = new Measurement(mainWin);
}

Sorting and iterating widget starting from anywhere

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

Categories