Ideas on how can i implement a decision criteria matrix in c# - c#

like the title said, i need to implement a decision matrix, one like the image below but with n amount of criteria and n amount of decision
I've thought on using a Dictionary where i can store the Criteria and Decision and the value, like Dictionary<DecisionCriteria, int> and class DecisionCriteria would be:
class DecisionCriteria { string Criteria; string Decision; }
but i don't know if thats is a good idea.
Another possible solution would imply something like a string array for each row
string[] row = {"U Delaware" , "12", "57", "16".....}, but i also don't think this would be very efficient.
I appreciate your ideas. Thanks
I have not started coding yet, i need to have an idea first

Here is one possible example of creating a DecisionCriteria class, populating a list of DecisionCriterias, sorting the list and getting the top choice:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
//Create a list of DecisionCriteria obejects.
List<DecisionCriteria> dcList = new List<DecisionCriteria>();
//Create a new DecisionCriteria and add to the list.
DecisionCriteria dc = new DecisionCriteria("U Delaware");
dc.AddCriteria("Distance", 12);
dc.AddCriteria("Social Life", 57);
//Add more criteria if needed;
dcList.Add(dc);
//Create a new DecisionCriteria and add to the list.
dc = new DecisionCriteria("Syracuse");
dc.AddCriteria("Distance", 36);
dc.AddCriteria("Social Life", 38);
//Add more criteria if needed;
dcList.Add(dc);
//Add more DecisionCriteria objects to the list if needed.
//Sort the list;
dcList.Sort((a,b) => b.TotalBenefit.CompareTo(a.TotalBenefit)); //Sort Descending
//dcList.Sort((a,b) => a.TotalBenefit.CompareTo(b.TotalBenefit)); //Sort Ascending.
//Get best choice
DecisionCriteria bestChoice = dcList[0];
Console.WriteLine("Best Choice is: " + bestChoice.EntityName + " with value: " + bestChoice.TotalBenefit.ToString() );
}
//Create a class to hold the criteria for each entity.
public class DecisionCriteria
{
//Properties to hold the name and criteria data.
public string EntityName { get; set; }
public Dictionary<string,int> Criteria { get; set; }
//Constructor takes a name (i.e. "U of Delaware")
public DecisionCriteria(string EntityName) {
this.EntityName = EntityName;
Criteria = new Dictionary<string,int>(); //Make sure Criteria property is initialized with an empty dictionary.
}
//Add up all the criteria to get a total.
public int TotalBenefit {
get
{
//You can modify this to calculate the total benefit. You might have some criteria that decrement the total value instead of incrementing it (detractors vs benefits). Or apply weighting to some criteria.
int sum = 0;
foreach(KeyValuePair<string, int> kvp in Criteria) {
sum += kvp.Value;
}
return sum;
}
}
//Use to add criteria to the entity/this object
public void AddCriteria(string Name, int Value) {
Criteria.Add(Name, Value);
}
}
}

Related

C# 2 keys and a value [duplicate]

This question already has answers here:
Multi-key dictionary in c#? [duplicate]
(16 answers)
Closed 1 year ago.
I want to store my player scores. I will have different worlds and worlds have different levels.
Thats why i want something like..
public static void AddScore(int world, int level, int rolls, float time, float score)
{
_scores[world][level] = new LevelScore(rolls, time, score);
}
public static LevelScore GetScore(int world, int level)
{
if (_scores.ContainsKey(world))
{
var scoresOfWorld = _scores[world];
if(scoresOfWorld.ContainsKey(level))
{
return scoresOfWorld[level];
}
}
return new LevelScore();
}
I tried it with Dictionary inside a Dictionary..
public static Dictionary<int, Dictionary<int, LevelScore>> _scores = new Dictionary<int, Dictionary<int, LevelScore>>();
but AddScore(...) leads into "KeyNotFoundException: The given key was not present in the dictionary."
I thought the key would be added if it is not existing. What is the best way for me to archive what i want easy?
You could do with a dictionary having key as combination of world and level.
var scores = new Dictionary<string, LevelScore>();
....
if (!scores.ContainsKey($"{world}_{level}"))
{
scores.Add($"{world}_{level}", value);
}
else
{
....
}
AddScore(...) leads into "KeyNotFoundException
That's because you need to add a new inner Dictionary<int, LevelScore> to the outer dictionary before you access it
dict[0][1] = ...
If there is no inner Dictionary<int, LevelScore> registered in the outer dictionary at dict[0] then you get a KeyNotFound when trying to retrieve the inner dict and set its [1]'th index to ...
You'd need a nested dictionary set code to be like:
if(!dict.TryGetValue(world, out var innerDict))
dict[world] = innerDict = new Dictionary<int, LevelScore>();
innerDict[level] = new LevelScore(rolls, time, score);
The if either retrieves the inner dictionary if it exists, or ensures one is created (and assigned to the innerDict variable) if it does not. This then means the second line can succeed (because either the innerDict is known and was retrieved, or it is new, and was set)
If you don't get on with that form, the older form also works (it just needs more lookups, but they're cheap enough that it'd be a premature optimization to obsess over them at the expense of not being able to read the code as easily)):
//ensure key exists
if(!dict.ContainsKey(world))
dict[world] = new Dictionary<int, LevelScore>();
dict[world][level] = new LevelScore(rolls, time, score);
You need to create the dictionary for the world first. It is not created automatically.
_scores[world] = new Dictionary<int, LevelScore>();
_scores[world][level] = new LevelScore(rolls, time, score);
the best way would be to use a relational data set, not dictionary. A Dictionary inside of a Dictionary is a hierarchical model. Look at this code
public class Score
{
public int World { get; set; }
public int Level { get; set; }
public int Rolls { get; set; }
public float Time { get; set; }
public float ScoreNum { get; set; }
}
public class ScoreBuilder
{
public List<Score> Scores { get; set; } = new List<Score>();
public void AddScore(int world, int level, int rolls, float time, float score)
{
var scoreObj = new Score { World = world, Level = level, Rolls = rolls, Time = time, ScoreNum = score };
Scores.Add(scoreObj);
}
public Score GetScore(int world, int level)
{
return Scores.FirstOrDefault(s=> s.World==world && s.Level==level);
}
}
You can add new score very easy and you can get any score very easy, using pure Linq.
How to use
var scores = new ScoreBuilder();
scores.AddScore(....);
scores.GetScore(...)

Linq Object Array

I am having some trouble in querying an object array using LINQ. I want to retrieve all products that contains the value passed.
My Product class
public class Product
{
public int mProductId;
public string mProductName;
public string mProductColor;
public string mProductSize;
public string mProductStatus;
public string mProductCode;
public int ProductId{ get { return mProductId; }}
public string ProductName { get{return mProductName; }}
public string ProductColor { get{return mProductColor;} }
public string ProductSize { get{return mProductSize;} }
public string ProductStatus { get{return mProductStatus;} }
public string ProductCode {get { return mProductCode; }}
}
public class ProductList
{
public static Product[] mProductList = {
new Product { mProductId = Resource.Drawable.Product1,
mProductName = "Green Lumberjack Cap",
mProductColor = "Color Brown",
mProductSize = "One Size Fits All",
mProductCode= "9780201760439",
mProductStatus= "In Stock"},
new Product { mProductId = Resource.Drawable.Product2,
mProductName = "Square Bar stool",
mProductColor= "Color Brown",
mProductSize = "One Size Fits All",
mProductCode= "9780201760440",
mProductStatus= "In Stock"},
new Product { mProductId = Resource.Drawable.Product3,
mProductName = "Vitra bathroom Tile",
mProductColor= "Color Brown",
mProductSize = "One Size Fits All",
mProductCode= "9780201760539",
mProductStatus= "In Stock"},
};
private Product[] mProducts;
Random mRandom;
public ProductList ()
{
mProducts = mProductList;
}
// Return the number of photos in the photo album:
public int NumPhotos
{
get { return mProducts.Length; }
}
// Indexer (read only) for accessing a photo:
public Product this[int i]
{
get { return mProducts[i]; }
}
// Pick a random photo and swap it with the top:
public int RandomSwap()
{
// Save the photo at the top:
Product tmpProduct = mProducts[0];
// Generate a next random index between 1 and
// Length (noninclusive):
int rnd = mRandom.Next(1, mProducts.Length);
// Exchange top photo with randomly-chosen photo:
mProducts[0] = mProducts[rnd];
mProducts[rnd] = tmpProduct;
// Return the index of which photo was swapped with the top:
return rnd;
}
// Shuffle the order of the photos:
public void Shuffle ()
{
// Use the Fisher-Yates shuffle algorithm:
for (int idx = 0; idx < mProducts.Length; ++idx)
{
// Save the photo at idx:
Product tmpProduct = mProducts[idx];
// Generate a next random index between idx (inclusive) and
// Length (noninclusive):
int rnd = mRandom.Next(idx, mProducts.Length);
// Exchange photo at idx with randomly-chosen (later) photo:
mProducts[idx] = mProducts[rnd];
mProducts[rnd] = tmpProduct;
}
}
}
and my LINQ statement is
var result = from p in nProductList<Product>
where ( p.mProductName.Contains(query) || p.mProductColor.Contains(query))
select p;
I have also declared nProductList in my class as
public ProductList nProductList;
It will be really great to know what am I doing wrong.
Thank you
In order to get the where keyword syntax to work, your ProductList class must have a Where(Func<Product, bool>) method on it. Most lists get this automatically because they implement IEnumerable<>, and the System.Linq namespace has a Where() extension method which matches this signature.
You could make ProductList implement the IEnumerable<Product> interface, or make it extend a class like List<Product> which already implements that interface, or add your own Where() method. However, I'd personally suggest that you just expose mProductList as an IEnumerable<Product> via a public property getter, and change your consuming code to query against that.
The reason why your linq statement does not work is because you did not define where. Imagine the old style linq:
nProductList.Where(p=>p.mProductName.Contains(query) || p.mProductColor.Contains(query)).Select(p=>);
nProductList does not have Where(Func) defined so it does not work.
Normally for your ProductList there are two ways to implement. First way is to inherit from IEnumerable<Product> as ProductList : IEnumerable<Product>;
Second way is to create a member in ProductList and make it public like
public class ProductList
{
public IEnumerable<Product> Products {get; private set;}
...
}
Usually the preferable way between the two above will depends on is there more properties or more methods in your ProductList class. More methods goes to the first way because it's more like an expended method collection of IEnumerable class (like your example), while more properties goes to the seconding way as this is more like another class just having a list in it and something else.

How can I deal with multiple foreach loops when I'm working with multiple data types

I have a problem that has been bothering me for a while now, it concerns the growth of loops in my program exponentially. I will let the code below do the talking and add comments within.
void Main()
{
//Here we are just creating simple lists
List<string> strings = new List<string>();
strings.Add("a");
strings.Add("b");
strings.Add("c");
List<int> integers = new List<int>();
integers.Add(1);
integers.Add(2);
integers.Add(3);
//Creating complex classes ( not really )
ComplexClass cc1 = new ComplexClass();
cc1.CCString = "A test";
cc1.CCInt = 2;
ComplexClass cc2 = new ComplexClass();
cc2.CCString = "Another test";
cc2.CCInt = 6;
//Creating a list of these too
List< ComplexClass > complexClasses = new List< ComplexClass >();
complexClasses.Add(cc1);
complexClasses.Add(cc2);
//Here i want to create every possible combination using each of the lists
//and then add these to a testData class to do other things with, serialize, save, print etc.
//The main question is here, the for loops will definitely increase exponentially with each
//list added to.
foreach( int i in integers )
{
foreach( string s in strings )
{
foreach( ComplexClass compClass in complexClasses )
{
TestData data = new TestData();
data.TestInteger = i;
data.TestString = s;
data.TestComplexClass = compClass;
OutPutTestData( data );
}
}
}
}
//Simply outputs the data as test but I will be keeping the object for later also
public void OutPutTestData( TestData testData )
{
Console.WriteLine( testData.TestString + testData.TestInteger + testData.TestComplexClass.CCString );
}
//The "Complex class" again not that complex but an example of what im tring to achieve
public class ComplexClass
{
public string CCString{ get; set; }
public int CCInt { get; set; }
}
//The overall test object which holds multiple properties of different data types
public class TestData
{
public string TestString { get; set; }
public int TestInteger { get; set; }
public ComplexClass TestComplexClass { get; set; }
}
Output
a1 Test1
a1 Test2
b1 Test1
b1 Test2
c1 Test1
c1 Test2
a2 Test1
a2 Test2
b2 Test1
b2 Test2
c2 Test1
c2 Test2
a3 Test1
a3 Test2
b3 Test1
b3 Test2
c3 Test1
c3 Test2
As you can see the loops work and give me every possible combination of the supplied data.
My problem is the exponential growth of the for loops as i add more lists. There could be a large number of lists.
I do understand that the number of iterations will increase as the combinations are discovered, thats not a problem as i plan to programmatically limit the amount of iterations that can occur based on the users input after estimating the total iterations possible.
e.g. Total iterations would be 234 so only iterate 120 times ( 120 combinations )
The code provided works great with nested foreach loops but as it grows exponentially it becomes hard to read, hard to manage and generally unsightly.
I have looked at Permutation algorithms like these:
Algorithm to generate all possible permutations of a list?
Understanding Recursion to generate permutations.
But they only allow the use of one specific datatype and not multiple.
I also looked into cartesian product but again the only examples i have found refer to only a single data type.
Even though you selected an answer, I thought you may want to have a look at this... With the use of recursion, all you have to do is put all of your Lists in a List<IList>. All you have to do with this is just add any newly added Lists to the List<IList>.
I added a override ToString() to your ComplexClass for this to fit.
public static void Test()
{
//Here we are just creating simple lists
List<string> strings = new List<string>();
strings.Add("a");
strings.Add("b");
strings.Add("c");
List<int> integers = new List<int>();
integers.Add(1);
integers.Add(2);
integers.Add(3);
//Creating complex classes ( not really )
ComplexClass cc1 = new ComplexClass();
cc1.CCString = "A test";
cc1.CCInt = 2;
ComplexClass cc2 = new ComplexClass();
cc2.CCString = "Another test";
cc2.CCInt = 6;
//Creating a list of these too
List<ComplexClass> complexClasses = new List<ComplexClass>();
complexClasses.Add(cc1);
complexClasses.Add(cc2);
// NEW LIST
List<double> doubles = new List<double>();
doubles.Add(99.99);
doubles.Add(100.12);
List<IList> myLists = new List<IList> {integers, strings, complexClasses, doubles};
Permutate("", myLists, 0);
Console.ReadLine();
}
public static void Permutate(string s, List<IList> list, int i)
{
if (i == list.Count)
{
Console.WriteLine(s);
}
else
{
foreach (object obj in list[i])
{
Permutate(s + obj + " ", list, i + 1);
}
}
}
//The "Complex class" again not that complex but an example of what im tring to achieve
public class ComplexClass
{
public string CCString { get; set; }
public int CCInt { get; set; }
// Added override
public override string ToString()
{
return CCString + CCInt;
}
}
Results (Not all results were captured):
You could get rid of the for loops by doing a cross join in Linq:
var query =
from i in integers
from s in strings
from compClass in complexClasses
select new TestData()
{
TestInteger = i,
TestString = s,
TestComplexClass = compClass
};
foreach (var data in query)
OutPutTestData( data );
If the lists were all of the same type then you could build a query that cross-joins a varying number of lists. In your case since the lists are of varying types it's not possible (without reflection, dynamic, or something uglier)

How to populate a listbox sorted with unsorted array

I have an array of items with two properties, name and position. The array is not sorted in any way and I want to populate a listbox in order of position.
I can do it with this code down below, first I add all items so I have a list with correct numbers of items and then replace the items at correct position. But I wonder if there is a better way to do it?
I want the listbox to have the names in this order: Mark, John and James.
Note: the James, Mark and John data is just an example, and I can't sort the standing array.
public class _standing
{
public _standing(string _name, int _pos) {
name = _name;
position = _pos;
}
public string name { get; set; }
public int position { get; set; }
}
_standing a = new _standing("James", 2);
_standing b = new _standing("Mark", 0);
_standing c = new _standing("John", 1);
_standing[] standing = new _standing[]{a, b, c};
for (int i = 0; i < standing.Length; i++) {
listBox1.Items.Add(standing[i].name);
}
for (int i = 0; i < standing.Length; i++) {
listBox1.Items.RemoveAt(standing[i].position);
listBox1.Items.Insert(standing[i].position, standing[i].name);
}
You can just use the OrderBy method of the array:
standing = standing.OrderBy(i => i.position).ToArray();
listBox1.Items.AddRange(standing);
You can also order by decscending:
standing.OrderByDescending(i => i.position).ToArray();
These both require a reference to System.Linq
Also, since OrderBy returns a new object, you can also do this without re-ordering your original list:
_standing a = new _standing("James", 2);
_standing b = new _standing("Mark", 0);
_standing c = new _standing("John", 1);
_standing[] standing = new _standing[] { a, b, c };
listBox1.Items.AddRange(standing.OrderBy(i => i.position).ToArray());
Update
In order to show something meaningful in your listBox1, you should override the ToString method on your _standing class, something like:
public class _standing
{
public string name { get; set; }
public int position { get; set; }
public _standing(string _name, int _pos)
{
name = _name;
position = _pos;
}
public override string ToString()
{
return position + ": " + name;
}
}
Finally, I have to mention that your casing/naming conventions are not standard C#. The standard is for Classes and Properties to be PascalCase, for arguments to be camelCase, and for private fields to be pascalCase with an optional underscore prefix. So your code would ideally look something like:
public class Standing
{
public string Name { get; set; }
public int Position { get; set; }
public Standing(string name, int position)
{
Name = name;
Position = position;
}
public override string ToString()
{
return Position + ": " + Name;
}
}
and...
Standing a = new Standing("James", 2);
Standing b = new Standing("Mark", 0);
Standing c = new Standing("John", 1);
Standing[] standing = { a, b, c };
listBox1.Items.AddRange(standing.OrderBy(i => i.Position).ToArray());
If I were you I would create a list of _standing first and add to the list. Something like:
List<_standing> standingList = new List<_standing>();
standingList.Add(new _standing("James", 2));
etc...
The advantage of using a List over the array type is that it's size is dynamic. If you are only ever going to have 3 _standing objects then an array is fine, but in reality that is probably unlikely to be the case.
Then you can use a Linq query to sort the list by position
IEnumerable<_standing> sorted = standingList.OrderBy(stand => stand.Position);
You now have a sorted List using .NET built in sorting algorithms and you can add this to your control Items collection. You can save time by using the AddRange method:
listBox1.Items.AddRange(sorted);
Sources for reference:
Sorting a collection
Add range

C# List - Sorting Data

I am trying to add elements into a list, order them and then output them, there a number of "columns" if you like, per list
List<Info> infoList = new List<Info>();
while (dr.Read())
{
meeting_id = dr.GetValue(0).ToString();
try
{
Appointment appointment = Appointment.Bind(service, new ItemId(meeting_id));
Info data = new Info();
data.Start = appointment.Start;
data.Fruit = Convert.ToInt32(dr.GetValue(1));
data.Nuts = Convert.ToInt32(dr.GetValue(2));
infoList.Add(data);
}
Then to output it I want to order it by Start and then display all associated columns
for (int i = 0; i < infoList.Count; i++)
{
meet = meet + infoList[i];
}
First question: is the way I am inputting the data right?
Second question: How to I output all the columns to display all the associated columns? Is this possible? Is there a better practice?
Thanks
EDIT:
The class if you are interested:
public class Info
{
public DateTime Start { get; set; }
public int Fruit { get; set; }
public int Nuts { get; set; }
}
You can use Enumerable.OrderBy extension for enumerating your collection in some particular order (e.g. ordered by Start property value):
foreach(var info in infoList.OrderBy(i => i.Start))
{
// use info object here
// info.Fruits
// info.Nuts
}
BTW consider to add sorting on database side - that will be more efficient

Categories

Resources