I am just trying to understand the different output of almost similar code. The only difference is that in first case i store queries temporarily and then do a union. In second case I do it in one statement.
First outputs
BMW
Honda
Second outputs
BMW
Honda
Harley
Enfield
static void Main(string[] args)
{
var queries = GetCar().Cast<Vehicle>();
queries.Union(GetBike());
var queries1 = GetCar().Cast<Vehicle>().Union(GetBike());
//First case
foreach (Vehicle v in queries)
Console.WriteLine(v.Manufacturer);
Console.ReadLine();
//Second case
foreach (Vehicle v in queries1)
Console.WriteLine(v.Manufacturer);
Console.ReadLine();
}
public static IEnumerable<Car> GetCar()
{
for(int i=0; i<2; i++)
{
Car car = new Car();
if(i == 0)
{
car.Manufacturer = "BMW";
car.Model = "7 series";
}
else
{
car.Manufacturer = "Honda";
car.Model = "Civic";
}
yield return car;
}
}
public static IEnumerable<Bike> GetBike()
{
for (int i = 0; i < 2; i++)
{
Bike bike = new Bike();
if (i == 0)
{
bike.Manufacturer = "Harley";
bike.Model = "NightRod";
}
else
{
bike.Manufacturer = "Enfield";
bike.Model = "Bullet";
}
yield return bike;
}
}
}
public class Vehicle
{
public Vehicle()
{
}
public int NoOfWheels;
public string Manufacturer;
public string Model;
}
public class Car : Vehicle
{
public Car():base()
{
NoOfWheels = 4;
}
}
public class Bike : Vehicle
{
public Bike() : base()
{
NoOfWheels = 2;
}
}
Union does not append to an existing enumeration, instead, it returns a NEW enumeration, yielding the combined results of both.
In your first example, you are calling union and then discarding the resulting enum.
You could have done:
queries = queries.Union(GetBike());
In your first case you aren't using the result of the Union.
The code queries.Union(GetBike()); does not modify queries.
You could call it like this and get the result you're looking for:
//First case
foreach (Vehicle v in queries.Union(GetBike()))
Console.WriteLine(v.Manufacturer);
Console.ReadLine();
I tend to dislike code like queries = queries.Union(GetBike());, but that is valid.
Related
I want to make an application where I can type a word and then a number. After typing all I want I want an output of them sorted alphabetical. The problem is that the words sort but the numbers don't sort with them because they are 2 different lists. How can i merge this 2 lists? I don't want to add them together like AddRange I need the out put like Console.WriteLine ("x{0}+" "+"{1}", numbers, words.
I've tried words.Sort(); but it just sorted the words and not both. So how can I merge the 2 lists?
The question is probably clear but in case you need some code there it is:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace zutaten
{
namespace mengen
{
class Program
{
static int hello;
static List<string> zutaten = new List<string>();
static List<int> mengen = new List<int>();
public static int memo;
public static int d = 1;
static List<string> zusammen = new List<string>();
static public bool Main(string[] args)
{
bool fertig = false;
while (!fertig)
{
var wert = Console.ReadLine();
if (wert != "f")
{
if (Gleich(zutaten, mengen))//zutaten.Count == mengen.Count)
{
if (KeineZutaten(wert))//int.TryParse(wert, out int keineZutat))
{
KeineZutatenAussage(wert);
}
else
{
if (Beinhaltet(wert)) //zutaten.Contains(wert)
{
Removen(wert);
// int index = zutaten.IndexOf(wert);
// zutaten.RemoveAt(index);
// mengen.RemoveAt(index);
}
//-------
Zutathinzufügen(wert);
//zutaten.Add(wert);
}
}
else
{
if (ParseMenge(wert, out memo))//int.TryParse(wert, out int menge))
{
Mengehinzufügen(memo);// mengen.Add(menge);
}
else
{
Mengepluseins(mengen);
//mengen.Add(1);
//--------
if (Beinhaltet(wert))
{
Removen(wert);
// int index = zutaten.IndexOf(wert);
// zutaten.RemoveAt(index);
// mengen.RemoveAt(index);
}
//------
Zutathinzufügen(wert);
//zutaten.Add(wert);
}
}
}
else
{
fertig = Fertigt();
if (!Gleich(zutaten, mengen))
{
Mengepluseins(mengen);
}
Forschleife(zutaten);
//for (int i = 0; i < zutaten.Count; i++)
//{
// Console.WriteLine("x{0} {1}", mengen[i], zutaten[i]);
//}
}
}
}
public static string MeineMethode()
{
return "string";
}
public static bool Gleich(List<string> variable1, List<int> variable2)
{
return variable1.Count == variable2.Count;
}
public static bool KeineZutaten(string wert1)
{
return int.TryParse(wert1, out hello);
}
public static void KeineZutatenAussage(string wert2)
{
Console.WriteLine("{0} ist keine Zutat", wert2);
}
public static bool Beinhaltet(string hulu)
{
return zutaten.Contains(hulu);
}
public static void Removen(string wertt)
{
int index = zutaten.IndexOf(wertt);
zutaten.RemoveAt(index);
mengen.RemoveAt(index);
}
public static void Zutathinzufügen(string werttt)
{
zutaten.Add(werttt);
}
// int index = zutaten.IndexOf(wert);
// zutaten.RemoveAt(index);
// mengen.RemoveAt(index);
//int.TryParse(wert, out int keineZutat
//zutaten.Add(wert);
public static bool ParseMenge(string wert1, out int var2)
{
return int.TryParse(wert1, out var2);
}
//int.TryParse(wert, out int menge))
public static void Mengehinzufügen(int var1)
{
mengen.Add(var1);
}
// mengen.Add(menge);
public static void Mengepluseins(List<int> mengen)
{
mengen.Add(d);
}
//mengen.Add(1);
public static bool Fertigt()
{
return true;
}
//fertig = true;
public static bool Mengeungleichzutaten(List<string> variable1, List<int> variable2)
{
return variable1.Count != variable2.Count;
}
//if (mengen.Count != zutaten.Count)
//{
// mengen.Add(1);
//}
public static void Forschleife(List<string> hey)
{
zutaten.Sort();
for (int i = 0; i < hey.Count; i++)
{
Console.WriteLine("x{0}"+" "+"{1}, mengen[i], zutaten[i]);
}
}
Input example :
Pork[Enter]
3[Enter]
Tomatoes[Enter]
6[Enter]
Potatoes[Enter]
2[Enter]
Expected output :
3x Pork
2x Potatoes
6x Tomatoes
Current output :
3x Pork
6x Potatoes
2x Tomatoes
Assuming these can be arrays, then you can use the native sort command to sort the two arrays according to one of them. This example sorts by the scores:
static void Main(string[] args)
{
int[] classScores = new int[]{30,50,25,39,62};
string[] studentNames = new string[]{"Jim","John","Mary","Peter","Sarah"};
Array.Sort(classScores, studentNames); // sort both according to scores
for (int i = 0; i < classScores.Length; i++)
{
Console.WriteLine(classScores[i] + " " + studentNames[i]);
}
}
You can use a SortedDictionary to store your values. The key being the product name, the value its quantity. This will be automatically sorted by key.
static SortedDictionary<string, int> GetProducts()
{
// Type of the
// key
// | Type of the
// | value
// | |
// v v
var Products = new SortedDictionary<string, int>();
while (true)
{
var product = "";
int quantity;
// First, ask the user to enter a product. If he enters nothing, ask again
while (product == "")
{
Console.WriteLine("Please enter a product name or f to finish");
product = Console.ReadLine();
if (product == "f")
return Products; // we are done, we can return the SortedDictionary
}
// Now, get the quantity
do
{
Console.WriteLine($"Please enter a quantity for {product}");
// Ask again if the user enters an invalid number
} while (!Int32.TryParse(Console.ReadLine(), out quantity));
// Store the informations in the SortedDictionary
Products[product] = quantity;
}
}
public static void Main()
{
// Get the products
var Products = GetProducts();
// Display them
foreach (var key in Products.Keys)
Console.WriteLine($"{Products[key]}x {key}");
}
Input :
Please enter a product name or f to finish
Pork
Please enter a quantity for Pork
3
Please enter a product name or f to finish
Tomatoes
Please enter a quantity for Tomatoes
6
Please enter a product name or f to finish
Potatoes
Please enter a quantity for Potatoes
2
Please enter a product name or f to finish
f
Output :
3x Pork
2x Potatoes
6x Tomatoes
I have an enum with 30 items in it. Each item has a corresponding function with the same name. I would like to be able to call the function by referencing the enum at a certain position.
So if the value at enum[0] = Foo, I would like to be able to call Foo(string bar) by using something like enum(0)("foobar")
In the end the point is I am running each function as a task like so:
enum Test { AA, BB, CC, DD ....}
tasks[0] = Task.Run(() => { prices[0] = AA("a string"); });
tasks[1] = Task.Run(() => { prices[1] = BB("a string"); });
tasks[2] = Task.Run(() => { prices[2] = CC("a string"); });
//for 30 tasks
What I would like to do is something along the lines of:
enum Test { AA, BB, CC, DD ....}
for (int i = 0; i < 30; i++)
{
tasks[i] = Task.Run(() => { prices[i] = (Test)i("a string"); });
}
Task.WaitAll(tasks.ToArray());
Is this something that is even possible?
EDIT:
The enum relates to controls on a form so i have an array of textboxs, label and a array of prices that is populated with the results of the functions:
enum Dealers { Dealer1, Dealer2 ... Dealer29, Dealer30 };
static int noOfDealers = Enum.GetNames(typeof(Dealers)).Length;
decimal[] prices = new decimal[noOfDealers];
TextBox[] textBox = new TextBox[noOfDealers];
Label[] boxes = new Label[noOfDealers];
for (int i = 0; i < noOfDealers; i++)
{
textBox[i] = Controls.Find("txt" + (Dealers)i, true)[0] as TextBox;
boxes[i] = Controls.Find("box" + (Dealers)i, true)[0] as Label;
prices[i] = 0;
}
//RUN 30 TASKS TO POPULATE THE PRICES ARRAY
for (int i = 0; i < noOfDealers; i++)
{
textBox[i].Text = "£" + prices[i].ToString();
}
//LOOP THROUGH PRICES ARRAY AND FIND CHEAPEST PRICE, THEN COLOUR THE LABEL BACKGROUND GREEN FOR THE TEXT BOX WITH THE NAME AT ENUM VALUE WHATEVER I IS
I guess i am just trying to make my code as concise as possible, there is the potential for the amount of tasks to double and didn't want to end up with 60 lines to populate the tasks array
I would create dictionary and map enum to actions:
Dictionary<Test, Func<string,double>> actions = new Dictionary<Test, Func<string,double>>()
{
{Test.AA, (x) => { return 5;}},
{Test.BB, (x) => { return 15; }},
}; //x is your string
var res = actions[Test.AA]("hello");
I would strongly suggest using a built in construct - like an extension method and a simple switch:
public static int GetPriceWithString(this Test test, string str)
{
switch (test)
{
case Test.AA:
break;
case Test.BB:
break;
case Test.CC:
break;
case Test.DD:
break;
default:
throw new ArgumentOutOfRangeException(nameof(test), test, null);
}
}
then your loop looks almost the same:
for (int i = 0; i < 30; i++)
{
tasks[i] = Task.Run(() =>
{
prices[i] = ((Test)i).GetPriceWithString("a string");
});
}
What you want to do is possible with reflection, which can be a powerful tool - but ideally should only be used as a last resort, as it will hide what could be compile time errors, and cause less code readability.
Using a simple switch like this makes your code self-documented, so when you come back to this in a month you can quickly remember what the intention was.
How about using an array of delegates:
using System;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
private static int AA(string a) { return 0; }
private static int BB(string a) { return 1; }
private static int CC(string a) { return 2; }
private static Func<string, int>[] functions = new Func<string, int>[] { AA, BB, CC };
private static int[] prices = new int[functions.Length];
private static Task[] tasks = new Task[functions.Length];
static void Main(string[] args)
{
for (int i = 0; i < functions.Length; ++i)
tasks[i] = Task.Run(() => { prices[i] = functions[i]("a string"); });
Task.WaitAll(tasks);
}
}
}
An eg. speaks a lot more than words.
I used it in a winform so the this refers to win form.
I have assumed all your methods are public , have same signature & return the same type.
enum MyName { AA,BB,CC};
//Call this in one of your methods
string [] strVal= Enum.GetNames(typeof(MyName));
int x = CallFunction(strVal[0], "A");
int y = CallFunction(strVal[1], "h");
int z = CallFunction(strVal[1], "C");
//End Call this in one of your methods
int CallFunction(string strName,string strValue)
{
return Convert.ToInt32(this.GetType().InvokeMember(strName, BindingFlags.Public | BindingFlags.InvokeMethod|BindingFlags.Instance, null, this, new object[] { strValue }));
}
public int AA(string s)
{
return 1;
}
public int BB(string s)
{
return 2;
}
public int CC(string s)
{
return 3;
}
Another solution. I hope somebody will consider it as overkill :)
Create abstract class DealerBase.
public abstract class DealerBase
{
public string Name { get; }
public decimal Price { get; set; }
protected DealerBase(string name)
{
Name = name;
}
public abstract void UpdatePrice();
}
Then create classes for every dealers you have and implement own logic for UpdatePrice method.
public class Dealer1 : DealerBase
{
public Dealer1() : base("DealerOne") { }
public override void UpdatePrice()
{
//Calculate price
Price = DealerOneCalculationMethod();
}
}
public class Dealer2 : DealerBase
{
public Dealer2() : base("DealerTwo") { }
public override void UpdatePrice()
{
//Calculate price
Price = DealerTwoCalculationMethod();
}
}
And so on..
Then you just create collection of dealers which can be easily iterated
var dealers = new List<DealerBase>
{
new Dealer1(),
new Dealer2()
}
foreach(var dealer in dealers)
{
dealer.UpdatePrice();
}
You can loop dealers and generate textboxes, labels in the winforms.
But I suggest to use DataGridView where code will be tiny clearer.
First implement INotifyPropertyChanged interface in the base class DealerBase
public abstract class DealerBase : INotifyPropertyChanged
{
public string Name { get; }
protected decimal _Price;
public decimal Price
{
get { return _Price; }
set
{
if (Equals(_Price, value)) return;
_Price = value;
// next method will inform DataGridView about changes
// and update value there too
RaisePropertyChanged();
}
protected DealerBase(string name)
{
Name = name;
}
public abstract void UpdatePrice();
// Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
The in the Form you can create BindingList<DealerViewModelBase> and set it to DataGridView.DataSource
public class YourForm: Form
{
public YourForm()
{
InitializeComponent();
var dealers = new List<DealerBase>
{
new Dealer1(),
new Dealer2()
};
var bindSource = new BindingList<DealerBase>(dealers);
dataGridView.DataSource = bindSource;
}
// Add button which update prices for all dealers
private void ButtonUpdatePrices_Click(object sender, EventArgs e)
{
var dealers = (BindingList<DealerBase>)dataGridView.DataSource;
foreach (var dealer in dealers)
{
dealer.UpdatePrice();
// Because we call `RaisePropertyChanged` in
// setter of Price - prices will be automatically
// updated in DataGridView
}
}
}
Idea of this approach you put different logic of different dealers in the separated class which. Because all dealer classes will inherit from same abstract class you can add different dealers to the collection.
You already have hardcoded enums and correspondent method which you try to link together. This approach make using of dealers collection little bid easy
I have a list with 1000000 items and I need to figure out if a item is inside but by reference. Therefore I can't use Contains since Contains doesn't always match by reference (e.g. when list of type string). I tried list.Any(x => object.ReferenceEquals) but that is too slow.
Take a look here:
for(int i = 0; i < 1000000; i++)
{
if(does list contains this item anotherList[i])
{
list.Add(anotherList[i]);
}
}
How do I perform this really fast?
Use a dictionary with an IdendityEqualityComparer to get the key comparison in the dictionary to do a reference comparison. The main difference between this approach and yours is that you have an O(1) lookup, instead of an O(n) lookup that you get from having to go through an entire list for each item.
Put the following code inside a sample Console app project; it basically splits a master dictionary into two.
public class IdentityEqualityComparer<T> : IEqualityComparer<T> where T : class
{
public int GetHashCode(T value)
{
return RuntimeHelpers.GetHashCode(value);
}
public bool Equals(T left, T right)
{
return left == right; // Reference identity comparison
}
}
public class RefKeyType
{
public int ID { get; set; }
}
class Program
{
public static void Main()
{
var refDictionary = new Dictionary<RefKeyType, int>(1000000, new IdentityEqualityComparer<RefKeyType>());
var testDictionary = new Dictionary<RefKeyType, int>(1000000, new IdentityEqualityComparer<RefKeyType>());
var store = new Dictionary<RefKeyType, int>(1000000);
for (var i = 0; i < 1000000; i++)
{
var key = new RefKeyType() {ID = i};
refDictionary[key] = i;
//Load the test dictionary if I is divisible by 2
if (i%2 == 0)
{
testDictionary[key] = i;
}
}
foreach (var key in refDictionary.Keys)
{
int val;
if (!testDictionary.TryGetValue(key, out val))
{
store[key] = val;
}
}
Console.WriteLine("Master dictionary has " + refDictionary.Count);
Console.WriteLine("Test dictionary has " + testDictionary.Count);
Console.WriteLine("Store dictionary has " + store.Count);
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
Problem: I have 2 kinds of objects, lets call them Building and Improvement. There are roughly 30 Improvement instances, while there can be 1-1000 Buildings. For each combination of Building and Improvement, I have to perform some heavy calculation, and store the result in a Result object.
Both Buildings and Improvements can be represented by an integer ID.
I then need to be able to:
Access the Result for a given Building and Improvement efficiently (EDIT: see comment further down)
Perform aggregations on the Results for all Improvements for a given Building, like .Sum() and .Average()
Perform the same aggregations on the Results for all Buildings for a given Improvement
This will happen on a web-server back-end, so memory may be a concern, but speed is most important.
Thoughts so far:
Use a Dictionary<Tuple<int, int>, Result> with <BuildingID, ImprovementID> as key. This should give me speedy inserts and single lookups, but I am concerned about .Where() and .Sum() performance.
Use a two-dimensional array, with one dimension for BuildingIDs and one for ImprovementIDs, and the Result as value. In addition, build two Dictionary<int, int> that map BuildingIDs and ImprovementIDs to their respective array row/column indexes. This could potentially mean max 1000+ Dictionarys, will this be a problem?
Use a List<Tuple<int, int, Result>>. I think this may be the least efficient, with O(n) inserts, though I could be wrong.
Am I missing an obvious better option here?
EDIT: Turns out it is only the aggregated values (per Building and per Improvement) I am interested in; see my answer.
Generally, the Dictionary is most lookup efficent. The both lookup efficency and manipulation efficency is constant O(1), when accessed via key. This will help for access, the first point.
In the second and third you need to walk through all of the items O(n), so there is no way to speed it except you want to walk them through in specified order O(n*n) - then you can use SortedDictionray O(n), but you compromise the lookup and manipulation efficency (O(log n)).
So I would go with the 1st solution you post.
You could use a "dictionary of dictionaries" to hold the Result data, for example:
// Building ID ↓ ↓ Improvement ID
var data = new Dictionary<int, Dictionary<int, Result>>();
This would let you quickly find the improvements for a particular building.
However, finding the buildings that contain a particular improvement would require iterating over all the buildings. Here's some sample code:
using System;
using System.Linq;
using System.Collections.Generic;
namespace Demo
{
sealed class Result
{
public double Data;
}
sealed class Building
{
public int Id;
public int Value;
}
sealed class Improvement
{
public int Id;
public int Value;
}
class Program
{
void run()
{
// Building ID ↓ ↓ Improvement ID
var data = new Dictionary<int, Dictionary<int, Result>>();
for (int buildingKey = 1000; buildingKey < 2000; ++buildingKey)
{
var improvements = new Dictionary<int, Result>();
for (int improvementKey = 5000; improvementKey < 5030; ++improvementKey)
improvements.Add(improvementKey, new Result{ Data = buildingKey + improvementKey/1000.0 });
data.Add(buildingKey, improvements);
}
// Aggregate data for all improvements for building with ID == 1500:
int buildingId = 1500;
var sum = data[buildingId].Sum(result => result.Value.Data);
Console.WriteLine(sum);
// Aggregate data for all buildings with a given improvement.
int improvementId = 5010;
sum = data.Sum(improvements =>
{
Result result;
return improvements.Value.TryGetValue(improvementId, out result) ? result.Data : 0.0;
});
Console.WriteLine(sum);
}
static void Main()
{
new Program().run();
}
}
}
To speed up the second aggregation (for summing data for all improvements with a given ID) we can use a second dictionary:
// Improvment ID ↓ ↓ Building ID
var byImprovementId = new Dictionary<int, Dictionary<int, Result>>();
You would have an extra dictionary to maintain, but it's not too complicated. Having a few nested dictionaries like this might take too much memory though - but it's worth considering.
As noted in the comments below, it would be better to define types for the IDs and also for the dictionaries themselves. Putting that together gives:
using System;
using System.Linq;
using System.Collections.Generic;
namespace Demo
{
sealed class Result
{
public double Data;
}
sealed class BuildingId
{
public BuildingId(int id)
{
Id = id;
}
public readonly int Id;
public override int GetHashCode()
{
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as BuildingId;
if (other == null)
return false;
return this.Id == other.Id;
}
}
sealed class ImprovementId
{
public ImprovementId(int id)
{
Id = id;
}
public readonly int Id;
public override int GetHashCode()
{
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as ImprovementId;
if (other == null)
return false;
return this.Id == other.Id;
}
}
sealed class Building
{
public BuildingId Id;
public int Value;
}
sealed class Improvement
{
public ImprovementId Id;
public int Value;
}
sealed class BuildingResults : Dictionary<BuildingId, Result>{}
sealed class ImprovementResults: Dictionary<ImprovementId, Result>{}
sealed class BuildingsById: Dictionary<BuildingId, ImprovementResults>{}
sealed class ImprovementsById: Dictionary<ImprovementId, BuildingResults>{}
class Program
{
void run()
{
var byBuildingId = CreateTestBuildingsById(); // Create some test data.
var byImprovementId = CreateImprovementsById(byBuildingId); // Create the alternative lookup dictionaries.
// Aggregate data for all improvements for building with ID == 1500:
BuildingId buildingId = new BuildingId(1500);
var sum = byBuildingId[buildingId].Sum(result => result.Value.Data);
Console.WriteLine(sum);
// Aggregate data for all buildings with a given improvement.
ImprovementId improvementId = new ImprovementId(5010);
sum = byBuildingId.Sum(improvements =>
{
Result result;
return improvements.Value.TryGetValue(improvementId, out result) ? result.Data : 0.0;
});
Console.WriteLine(sum);
// Aggregate data for all buildings with a given improvement using byImprovementId.
// This will be much faster than the above Linq.
sum = byImprovementId[improvementId].Sum(result => result.Value.Data);
Console.WriteLine(sum);
}
static BuildingsById CreateTestBuildingsById()
{
var byBuildingId = new BuildingsById();
for (int buildingKey = 1000; buildingKey < 2000; ++buildingKey)
{
var improvements = new ImprovementResults();
for (int improvementKey = 5000; improvementKey < 5030; ++improvementKey)
{
improvements.Add
(
new ImprovementId(improvementKey),
new Result
{
Data = buildingKey + improvementKey/1000.0
}
);
}
byBuildingId.Add(new BuildingId(buildingKey), improvements);
}
return byBuildingId;
}
static ImprovementsById CreateImprovementsById(BuildingsById byBuildingId)
{
var byImprovementId = new ImprovementsById();
foreach (var improvements in byBuildingId)
{
foreach (var improvement in improvements.Value)
{
if (!byImprovementId.ContainsKey(improvement.Key))
byImprovementId[improvement.Key] = new BuildingResults();
byImprovementId[improvement.Key].Add(improvements.Key, improvement.Value);
}
}
return byImprovementId;
}
static void Main()
{
new Program().run();
}
}
}
Finally, here's a modified version which determines the time it takes to aggregate data for all instances of a building/improvement combination for a particular improvement and compares the results for dictionary of tuples with dictionary of dictionaries.
My results for a RELEASE build run outside any debugger:
Dictionary of dictionaries took 00:00:00.2967741
Dictionary of tuples took 00:00:07.8164672
It's significantly faster to use a dictionary of dictionaries, but this is only of importance if you intend to do many of these aggregations.
using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
namespace Demo
{
sealed class Result
{
public double Data;
}
sealed class BuildingId
{
public BuildingId(int id)
{
Id = id;
}
public readonly int Id;
public override int GetHashCode()
{
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as BuildingId;
if (other == null)
return false;
return this.Id == other.Id;
}
}
sealed class ImprovementId
{
public ImprovementId(int id)
{
Id = id;
}
public readonly int Id;
public override int GetHashCode()
{
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as ImprovementId;
if (other == null)
return false;
return this.Id == other.Id;
}
}
sealed class Building
{
public BuildingId Id;
public int Value;
}
sealed class Improvement
{
public ImprovementId Id;
public int Value;
}
sealed class BuildingResults : Dictionary<BuildingId, Result>{}
sealed class ImprovementResults: Dictionary<ImprovementId, Result>{}
sealed class BuildingsById: Dictionary<BuildingId, ImprovementResults>{}
sealed class ImprovementsById: Dictionary<ImprovementId, BuildingResults>{}
class Program
{
void run()
{
var byBuildingId = CreateTestBuildingsById(); // Create some test data.
var byImprovementId = CreateImprovementsById(byBuildingId); // Create the alternative lookup dictionaries.
var testTuples = CreateTestTuples();
ImprovementId improvementId = new ImprovementId(5010);
int count = 10000;
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < count; ++i)
byImprovementId[improvementId].Sum(result => result.Value.Data);
Console.WriteLine("Dictionary of dictionaries took " + sw.Elapsed);
sw.Restart();
for (int i = 0; i < count; ++i)
testTuples.Where(result => result.Key.Item2.Equals(improvementId)).Sum(item => item.Value.Data);
Console.WriteLine("Dictionary of tuples took " + sw.Elapsed);
}
static Dictionary<Tuple<BuildingId, ImprovementId>, Result> CreateTestTuples()
{
var result = new Dictionary<Tuple<BuildingId, ImprovementId>, Result>();
for (int buildingKey = 1000; buildingKey < 2000; ++buildingKey)
for (int improvementKey = 5000; improvementKey < 5030; ++improvementKey)
result.Add(
new Tuple<BuildingId, ImprovementId>(new BuildingId(buildingKey), new ImprovementId(improvementKey)),
new Result
{
Data = buildingKey + improvementKey/1000.0
});
return result;
}
static BuildingsById CreateTestBuildingsById()
{
var byBuildingId = new BuildingsById();
for (int buildingKey = 1000; buildingKey < 2000; ++buildingKey)
{
var improvements = new ImprovementResults();
for (int improvementKey = 5000; improvementKey < 5030; ++improvementKey)
{
improvements.Add
(
new ImprovementId(improvementKey),
new Result
{
Data = buildingKey + improvementKey/1000.0
}
);
}
byBuildingId.Add(new BuildingId(buildingKey), improvements);
}
return byBuildingId;
}
static ImprovementsById CreateImprovementsById(BuildingsById byBuildingId)
{
var byImprovementId = new ImprovementsById();
foreach (var improvements in byBuildingId)
{
foreach (var improvement in improvements.Value)
{
if (!byImprovementId.ContainsKey(improvement.Key))
byImprovementId[improvement.Key] = new BuildingResults();
byImprovementId[improvement.Key].Add(improvements.Key, improvement.Value);
}
}
return byImprovementId;
}
static void Main()
{
new Program().run();
}
}
}
Thanks for the answers, the test code was really informative :)
The solution for me turned out to be to forgo LINQ, and perform aggregation manually directly after the heavy calculation, as I had to iterate over each combination of Building and Improvement anyway.
Also, I had to use the objects themselves as keys, in order to perform calculations before the objects were persisted to Entity Framework (i.e. their IDs were all 0).
Code:
public class Building {
public int ID { get; set; }
...
}
public class Improvement {
public int ID { get; set; }
...
}
public class Result {
public decimal Foo { get; set; }
public long Bar { get; set; }
...
public void Add(Result result) {
Foo += result.Foo;
Bar += result.Bar;
...
}
}
public class Calculator {
public Dictionary<Building, Result> ResultsByBuilding;
public Dictionary<Improvement, Result> ResultsByImprovement;
public void CalculateAndAggregate(IEnumerable<Building> buildings, IEnumerable<Improvement> improvements) {
ResultsByBuilding = new Dictionary<Building, Result>();
ResultsByImprovement = new Dictionary<Improvement, Result>();
for (building in buildings) {
for (improvement in improvements) {
Result result = DoHeavyCalculation(building, improvement);
if (ResultsByBuilding.ContainsKey(building)) {
ResultsByBuilding[building].Add(result);
} else {
ResultsByBuilding[building] = result;
}
if (ResultsByImprovement.ContainsKey(improvement)) {
ResultsByImprovement[improvement].Add(result);
} else {
ResultsByImprovement[improvement] = result;
}
}
}
}
}
public static void Main() {
var calculator = new Calculator();
IList<Building> buildings = GetBuildingsFromRepository();
IList<Improvement> improvements = GetImprovementsFromRepository();
calculator.CalculateAndAggregate(buildings, improvements);
DoStuffWithResults(calculator);
}
I did it this way because I knew exactly which aggregations I wanted; if I required a more dynamic approach I would probably have gone with something like #MatthewWatson's Dictionary of Dictionaries.
Say I have a list of member, each of which is a custom object:
public class pail
{
public string milk;
public string water;
public string butter;
public string beer;
}
public class AddToPail()
{
private List<pail> _pailList = new List<pail>();
PSVM(String[] args)
{
for(int i = 0; i < 200; i++)
{
pail newPail = new Pail();
switch(i)
{
case 1:
{
newPail.milk = "This pail has milk";
}
break;
case 2:
{
newPail.butter = "This pail has butter";
}
break;
case 3:
{
newPail.water = "This pail has water";
}
break;
case 4:
{
newPail.beer = "This pail has beer";
}
break;
}
_pailList.Add(newPail);
}
foreach (pail thisPail in _pailList)
{
using (StreamWriter SW = new StreamWriter(#"C:\pail.txt")
{
if (!thisPail.milk.IsNullOrEmpty())
{
SW.WriteLine(thisPail.milk);
}
else if (!thisPail.butter.IsNullOrEmpty())
{
SW.WriteLine(thisPail.butter);
}
else if (!thisPail.beer.IsNullOrEmpty())
{
SW.WriteLine(thisPail.beer);
}
else if (!thisPail.water.IsNullOrEmpty())
{
SW.WriteLine(thisPail.water);
}
else
{
Console.Writeline("oops");
}
}
}
}
}
Say I want to set up a StreamWriter that only prints the true values without having to write a million if, else if, else statements... is there an easy way or library to do this in C#? I'm basically looking for a way to only print out true values in a neat, concise way. Does anyone have any advice as to how I should approach this?
Thank you very much!
EDIT
So the ultimate goal of this is that I have an object that has around 20 members. The object is automatically populated, and the populating script can leave some of the members empty. I'd like to be able to print the members in a CSV format, and not have to have 20 if statements to see if a particular member in the object has been instantiated before outputting via the streamwriter.
Edit 2
I changed my code to be a little closer to what I needed it to do. Sorry for the previous poor explanation.
I think you should refactor your program a little bit. For starters, I would use an enum for bucket contents:
public enum EBucketContents { Milk, Water, Butter, Beer };
Then, instead of having a list of booleans, you can use a dictionary:
var pail = Dictionary<EBucketContents,bool>();
Now it's a simple matter to only output the ones that are true:
foreach( var kvp in pail.Where( x => x.Value ) ) {
SW.WriteLine( "pail has " + kvp.Key.ToString().ToLower() )
}
If you just want to save some typing, use this extension method:
internal static class Extensions
{
public static void WriteLineIf(this TextWriter tw, bool condition, string text)
{
if (condition)
{
tw.WriteLine(text);
}
}
}
But it looks like only one of those bools can be true, since you're using else if blocks.
In that case, use and enum
internal enum Pail
{
Butter,
Milk,
Water,
Beer
}
Can you just use a Dictionary where the key is the field name and the value is the fields value. This way you don't need to check if the output is filled or not - you just output all fields
Your populating script can populate the dictionary keys only if they are set
Then your streamwriter can just go
foreach(KeyValuePair<string, string> kvp in fieldsDict)
sw.Write("Key: " + kvp.Key + ", Value: " + kvp.Value);
Or even just a list of string/or enum
e.g.
public class pail
{
public List<string> Fields = new List<string>();
}
public class AddToPail()
{
private List<pail> _pailList = new List<pail>();
PSVM(String[] args)
{
for(int i = 0; i < 200; i++)
{
pail newPail = new Pail();
switch(i)
{
case 1:
{
newPail.Fields.Add("This pail has milk");
}
break;
*** SNIP
Of course using a Dictionary could solve your problem , but I'm not really fond of this kind of solution, since it makes you lose some control over what you are putting in, e.g you could end up with a pail having airplanes... I'd refactor your code in something like this, trying to give every class its own responsabilities (BTW I don't like AddToPail as a class name, it's more a method name):
public class Pail
{
public string milk;
public string water;
public string butter;
public string beer;
private bool everythingEmpty = true;
public Pail(int i)
{
switch(i)
{
case 1:
{
milk = "This pail has milk";
everythingEmpty = false;
}
break;
case 2:
{
butter = "This pail has butter";
everythingEmpty = false;
}
break;
case 3:
{
water = "This pail has water";
everythingEmpty = false;
}
break;
case 4:
{
beer = "This pail has beer";
everythingEmpty = false;
}
break;
}
}
public void WriteToStream(StreamWriter SW)
{
if (everythingEmpty)
{
Console.Writeline("oops");
return;
}
WriteToStream(milk, SW);
WriteToStream(butter, SW);
WriteToStream(beer, SW);
WriteToStream(water, SW);
}
public static void WriteToStream(string content, StreamWriter SW)
{
if (!content.IsNullOrEmpty())
{
SW.WriteLine(content);
}
}
}
public class AddToPail()
{
private List<pail> _pailList = new List<pail>();
PSVM(String[] args)
{
for(int i = 0; i < 200; i++)
{
pail newPail = new Pail(i);
_pailList.Add(newPail);
}
foreach (pail thisPail in _pailList)
{
using (StreamWriter SW = new StreamWriter(#"C:\pail.txt")
{
thisPail.WriteToStream(SW);
}
}
}
}