I am making a modular system in C#. Modules are divided by groups and are all managed by a command object.
Right now I am writing the same code over and over because I can't find a good way to generalise it in a way that wouldn't cause performance to drop. This structure is used in a game and called every frame.
I would like modules to inherit the interfaces they are interested in. Most of my interfaces for the modules have a single function.
For example I have a IModuleCanUse, IModuleUse and IModuleGetState, interface (I have many more)
This is what it looks like now:
(The example below is simplified but should get the point across)
public bool CanUse(){
for (int i = 0; i < m_AllModuleGroups.Count; i++) {
var moduleGroup = m_AllModuleGroups[i];
for (int j = 0; j < moduleGroup.Modules.Count; j++) {
var module = moduleGroup.Modules[j];
if (!module.IsActive || !(module is IModuleCanUse moduleT)) { continue; }
var canuse = moduleT.CanUse();
if(canUse == false){
return false;
}
}
}
return true;
}
public void Use(){
for (int i = 0; i < m_AllModuleGroups.Count; i++) {
var moduleGroup = m_AllModuleGroups[i];
for (int j = 0; j < moduleGroup.Modules.Count; j++) {
var module = moduleGroup.Modules[j];
if (!module.IsActive || !(module is IModuleUse moduleT)) { continue; }
moduleT.Use();
}
}
}
public ModuleState GetState(){
var state = ModuleState.NoState;
for (int i = 0; i < m_AllModuleGroups.Count; i++) {
var moduleGroup = m_AllModuleGroups[i];
for (int j = 0; j < moduleGroup.Modules.Count; j++) {
var module = moduleGroup.Modules[j];
if (!module.IsActive || !(module is IModuleGetState moduleT)) { continue; }
state = moduleT.GetState(state);
}
}
return state
}
As you can see I am repeating the same 5 first lines of code. Iterating through the modules and checking if it is active and inherits the interface.
Is there a way to create an IEnumerator, or function that wouldn't cause garbage allocation or performance degradation and that could allow me to write this in less lines of code?
The order in which the modules are iterated through matters, and it can change at runtime, so caching the modules that inherit a certain interface in seperate lists isn't really a good option.
My main concern is if I do this with all interfaces, and then I decide to refactor the condition to call functions on modules, I'll have to go through all of them, and I might forget one which could cause bugs.
Something like this is the closest thing I can think of for a generic approach. But I am assuming this creates garbage when the action parameter is created with a lambda function
´´´
public void GenericModuleAction<T>(Action<T> action)
{
for (int i = 0; i < m_AllModuleGroups.Count; i++) {
var moduleGroup = m_AllModuleGroups[i];
for (int j = 0; j < moduleGroup.EnabledBaseModules.Count; j++) {
var module = moduleGroup.EnabledBaseModules[j];
if (!module.IsActive || !(module is T moduleT)) { continue; }
action(moduleT);
}
}
}
public void Use(){
GenericModuleAction<IModuleUse>(module => module.Use());
}
´´´
Thank you for your time.
I wrote this simple program:
class Program
{
static void Main(string[] args)
{
Console.Write("Number of elements in the array: ");
int numberOfElements = int.Parse(Console.ReadLine());
int[] array = new int[numberOfElements];
for(int i = 0; i < numberOfElements; i++)
{
Console.Write($"Element no {i+1}: ");
array[i] = int.Parse(Console.ReadLine());
}
for(int i = 0; i < array.Length; i++)
{
int count = 0;
for(int j = 0; j < array.Length; j++)
{
if(array[i] == array[j])
{
count++;
}
}
Console.WriteLine($"{array[i]} appears " + count + " times");
}
}
}
}
Is there any option to make the displayed values print only once?
For example, if there are three occurrences - the message displays three times. Is it possible to make it display once when there are more occurrences though?
You could use a GroupBy instead of the for loop
Groups the elements of a sequence.
var results = array
.GroupBy(x => x)
.Select(x => new {Value = x, Count = x.Count()});
foreach(var g in results)
Console.WriteLine($"{g.Value} appears {g.Count} times");
Or another way it to use a HashSet to keep track of what you have displayed. A HashSet is basically a collection that contains no duplicate elements. The Add methods returns true if it can add an element or false otherwise
HashSet<T>.Add(T) Method
returns true if the element is added to the HashSet object; false if the
element is already present.
var hashSet = new HashSet<int>();
for (int i = 0; i < array.Length; i++)
{
int count = 0;
for (int j = 0; j < array.Length; j++)
if (array[i] == array[j])
count++;
// Add to the hashset, if the add method returns true,
// it means the value was uniquely added, ergo you have not displayed yet
if (hashSet.Add(array[i]))
Console.WriteLine($"{array[i]} appears " + count + " times");
}
Full Demo Here
Or another approach is to use a Dictionary. The premise is to iterate over the array, try an add each item to the dictionary with TryAdd if it's already found increment the value
var dictionary = new Dictionary<int,int>();
foreach(var item in array)
if(!dictionary.TryAdd(item,1))
dictionary[item]++;
foreach(var item in dictionary)
Console.WriteLine($"{item.Key} appears {item.Value} times");
Full Demo Here
The first idea I had was the same of the comment from Jon Skeet, since the simplicity it implies.
The idea is to set null for the value we have already counted (matched).
From a developer point of view it is very simple and doesn't deviate too much from the OP's code.
Console.Write("Number of elements in the array: ");
int numberOfElements = int.Parse(Console.ReadLine());
int?[] array = new int?[numberOfElements];
for (int i = 0; i < numberOfElements; i++)
{
Console.Write($"Element no {i + 1}: ");
array[i] = int.Parse(Console.ReadLine());
}
for (int i = 0; i < array.Length; i++)
{
int count = 0;
int? current = array[i];
if (array[i] != null)
{
for (int j = 0; j < array.Length; j++)
{
if (current == array[j])
{
count++;
array[j] = null;
}
}
Console.WriteLine($"{current} appears " + count + " times");
}
}
int?[] defines a nullable value type. Therefore each item in the array can have either a null or int value - documentation here.
An approach using Dictionary with O(n) complexity.
Console.Write("Number of elements in the array: ");
int numberOfElements = int.Parse(Console.ReadLine());
var dictionary = new Dictionary<int, int>();
for (int i = 0; i < numberOfElements; i++)
{
Console.Write($"Element no {i + 1}: ");
var value = int.Parse(Console.ReadLine());
if (!dictionary.ContainsKey(value)) dictionary.Add(value, 0);
dictionary[value] = dictionary[value] + 1;
}
foreach (var item in dictionary)
{
Console.WriteLine($"{item.Key} appears {item.Value} times");
}
One simple way would be to swap your outer for loop with a foreach using a set to obtain distinct values.
So replace this:
for (int i = 0; i < array.Length; i++)
With this:
foreach (int i in new HashSet<int>(array))
And this:
if (array[i] == array[j])
With this:
if (i == array[j])
Other approach more suited for you would be too take only unique values from array, i.e.:
var unique = array.Distinct().ToArray();
for (int i = 0; i < unique.Length; i++)
{
int count = 0;
for (int j = 0; j < array.Length; j++)
{
if (array[i] == array[j])
{
count++;
}
}
Console.WriteLine($"{unique[i]} appears " + count + " times");
}
In the inner loop, try to check if there were already any occurrences of the current element until you exceed the outer index.
for(int i = 0; i < array.Length-1; i++)
{
int count = 1;
bool appeared = false;
for(int j = 0; j < array.Length; j++)
{
// until we are not at the same index as the outer loop
// check if we haven't already met the current element
if(j < i)
{
if (array[i] == array[j])
{
// set current value appearance to true
// to know if current element should be displayed
appeared = true;
// break the loop because there is no sense of continuing
// current look
break;
}
}
// if we are ahead of outer index
// check if there are occurences of the element
else if(j > i)
{
if (array[i] == array[j])
count++;
}
}
// don't print the current element if it has appeared before
if(!appeared)
Console.WriteLine($"{array[i]} appears {count} times");
}
I believe there should be a more optimal solution, as this one's time complexity is linear... You can think of some optimization. For example, you can store occurred elements in the array and check through the array at each iteration, so you don't need to start the inner loop from the beginning, but instead start it from the outer loop's position + 1 but it's also not the best solution.
P.S check out about string interpolation, because you don't need to concatenate strings when you use it.
You can also use Lookup here:
var sb = new StringBuilder();
foreach (var value in array.ToLookup(item => item))
{
sb.AppendLine($"{value.Key} appears " + value.Count() + " times");
}
Console.WriteLine(sb.ToString());
I'm trying to find an apartment number which is not already taken. So list<> is a list with already taken apartments and all<> is a list of all the apartments. so I try to iterate through them to find an apartment that is not already taken.
This does not work:
list<> // 4 indexes
all<> // 25 indexes
for (int i = 0;i < all.Count; i++)
{
for (int j = 0; j < list.Count; j++)
{
if (all[i].apartmentNr != list[j].apartmentNr)
{
apartmentNr = all[i].apartmentNr;
}
}
}
The problem is that you don't check all the list items so apartmentNr is set on first mismatch, but it's possibly taken on next list item. Thus you need to check all list items, before you can make a conclusion that it's free:
list<> // 4 indexes
all<> // 25 indexes
for (int i = 0;i < all.Count; i++)
{
bool taken = false;
for (int j = 0; j < list.Count; j++)
{
if (all[i].apartmentNr == list[j].apartmentNr)
{
taken = true;
break; // no need to check the rest
}
}
if (!taken) {
apartmentNr = all[i].apartmentNr;
break; // found first free
}
}
using System.Linq;
//availableApartments will contain all available apartments
var availableApartments = all.Except(list)
// Will get you the first item.
var freeApartment = availableApartments.FirstOrDefault()
Reference - Enumerable.Except
I am trying to get positions in a list of some values to compare them with another list.
for (int i = 0; i <= commands.ToArray().Length; i++)
{
levensheteinvalues_commands.Add(commands.ToArray()[i].ToString());
levensheteinvalues_numbers.Add(
Program.ComputeLevenshteinDistance(args[0],
commands.ToArray()[i].ToString()));
}
for (int i = 0; i <= commands.ToArray().Length; i++)
{
if (smallestlevensheteinvalue == 0)
{
smallestlevensheteinvalue = levensheteinvalues_numbers[i];
}
else if (smallestlevensheteinvalue > levensheteinvalues_numbers[i])
{
smallestlevensheteinvalue = levensheteinvalues_numbers[i];
}
}
var indexes = levensheteinvalues_numbers.GetIndexes(smallestlevensheteinvalue);
Why doesn't
var indexes = levensheteinvalues_numbers.GetIndexes(smallestlevensheteinvalue);
work? And when I get the value how can I compare it to another list?
The code you have posted have some serious problems. This May solve your problem since your code as well as your approach is very unclear and ambiguous. I have blindly edited the code to fix the serious problems.
for (int i = 0; i < commands.Count(); i++) {
levensheteinvalues_commands.Add(commands.ElementAt(i).ToString());
Program.ComputeLevenshteinDistance(args[0], commands.ElementAt(i).ToString()));
}
for (int i = 0; i < commands.Count(); i++) {
if (smallestlevensheteinvalue == 0)
{
smallestlevensheteinvalue = levensheteinvalues_numbers[i];
}
else if (smallestlevensheteinvalue > levensheteinvalues_numbers[i])
{
smallestlevensheteinvalue = levensheteinvalues_numbers[i];
}
}
int index = levensheteinvalues_numbers.IndexOf(levensheteinvalues_numbers.Min());
How do I go about ranking a list based on one criteria, and then separating ties (of which there are likely to be a lot) based on another?
My (working) attempt to rank drivers based on their points totals.
for (int j = 0; j < career.NumberOfDrivers; j++)
{
int rank = 1;
for (int i = 0; i < career.NumberOfDrivers; i++)
{
if (career.driver[j].championshipPoints < career.driver[i].championshipPoints)
{
rank += 1;
}
}
career.driver[j].championshipRank = rank;
}
Presumably I then want to cycle through afterwards checking for ties - the problem is there are definitely going to be situations when I have several drivers tied on 0.
How do I go about simply breaking these tiebreakers alphabetically (ie based on first letter of driver's names)?
EDIT - my solution if its of interest. Pretty nasty hack in some respects but does the job.
List<Driver> tempDriver = new List<Driver>();
for (int i = 0; i < career.driver.Count; i++)
{
tempDriver.Add(career.driver[i]);
}
tempDriver.Sort(
delegate(Driver d1, Driver d2)
{
if (d1.championshipPoints == d2.championshipPoints)
{
return d1.Name.CompareTo(d2.Name);
}
return d1.championshipPoints.CompareTo(d2.championshipPoints);
}
);
for (int i = 0; i < career.NumberOfDrivers; i++)
{
for (int j = 0; j < career.NumberOfDrivers; j++)
{
if (career.driver[i].Name == tempDriver[j].Name)
{
career.driver[i].championshipRank = j + 1;
}
}
}
You can use Sort() on your drivers List, using a delegate.
If drivers have same points number, return the comparison result of their names.
Else return the comparison or their points
driversList.Sort(
delegate(Driver d1, Driver d2)
{
if (d1.championshipPoints == d2.championshipPoints)
{
return d1.name.CompareTo(d2.name);
}
return d1.championshipPoints.CompareTo(d2.championshipPoints);
}
);
Then the ranking of a driver is simply its index in the sorted list.
Edit:
This solution has n*log(n) complexity (the OP first attempt is O(n^2) )