Twice this month, I've had to create a total a bunch of records of a class. (two different classes) and this will happen again. It seems to me that there should be an easy way to do this using reflection for any class without having to code a totaling routine for each class.
Consider:
private class ThisAndThat
{
public int This { get; set; }
public float That { get; set; }
public double TheOther { get; set; }
public string Whatever { get; set; }
}
As my code rumbles along, I create a bunch of these but I also need a totaling routine. Something like the AddToTotal() listed below,m where the numbers are added and the string is ignored.
List<ThisAndThat> _Discovered = new List<ThisAndThat>();
ThisAndThat _Total = new List<ThisAndThat>;
while( !Finished )
{
ThisAndThat CurrentOne = GetAnotherOne();
_Discovered.Add( CurrentOne );
AddToTotal( _Total, CurrentOne );
}
Obviously the numeric three properties in this sample class are easy to code, but I just did one with 60 numeric members. I fumbled around with reflection for a while but could not come up with a routine.
Reflection can absolutely do this. It's not too difficult. Here's an example using the class you provided:
var tat = new ThisAndThat();
tat.This = 1;
tat.That = 2.0F;
tat.TheOther = 3.0;
tat.Whatever = "Whatever";
var type = typeof(ThisAndThat);
var properties = type.GetProperties();
double total = 0.0;
foreach (System.Reflection.PropertyInfo pi in properties)
{
switch (pi.PropertyType.ToString())
{
case "System.Int32": //int
total += (int) pi.GetValue(tat, null);
break;
case "System.Double":
total += (double) pi.GetValue(tat, null);
break;
case "System.Single": //float
total += (float) pi.GetValue(tat, null);
break;
}
}
MessageBox.Show(total.ToString());
Note that my sample only works with Properties. If you have Fields that you need totaled, you'll have to use the GetFields method on the Type.
You should also be aware of handling other numeric types as well such as Int64, etc...
Is this what you're looking for?
ThisAndThat thisThatSum = 0;
foreach(ThisAndThat tat in _Discovered)
{
thisThatSum.This += tat.This;
// do the same for other fields
}
I'm sure a linq, way exists too, but I'd have to like do research and stuff to get that to you
Linq to sql makes this very easy for anything IEnumerable
ThisAndThat item1 = new ThisAndThat();
ThisAndThat item2 = new ThisAndThat();
item1.TheOther = 1.00;
item2.TheOther = 2.00;
_Discovered.Add(item1);
_Discovered.Add(item2);
var amount = from p in _Discovered
select p.TheOther;
Console.WriteLine("Amount total is {0}", amount.Sum());
Related
I have a method which I call CalculatePopularityScore. It exists on a Story object. The Story object has a field which is an ICollection of Comment objects.
public virtual ICollection<Comment> Comments { get; set; }
The Comment object has another collection of Reply objects.
My method looks at the story, loops through the comments associated with that story, and if the story's comments has replies, adds up that total. That, along with some other fields, gives me a very (and I stress this) very rudimentary algorithm of a story's popularity.
public double CalculateStoryPopularityScore()
{
if (Comments == null) throw new ArgumentException("Comments can't be null");
if (Comments.Count < 0) throw new ArgumentException("Comments can't be less than zero.");
int ReplyCountSum = 0;
double ReplyScore;
double CommentScore;
double InsightfulVoteScore;
double UsefulVoteScore;
double viewCount;
foreach (var comment in Comments)
{
int replyCount;
if (comment.Replies == null)
{
throw new ArgumentNullException("Replies cannot be null");
}
if (comment.Replies.Count() == 0)
{
replyCount = 0;
} else
{
replyCount = comment.Replies.Count();
}
ReplyCountSum += replyCount;
}
ReplyScore = ReplyCountSum * 4;
CommentScore = Comments.Count() * 4;
InsightfulVoteScore = InsightFulVoteCount * 3;
UsefulVoteScore = UsefulVoteCount * 2;
viewCount = ViewCount;
double PopularityScore = CommentScore + ReplyScore + InsightfulVoteScore + + UsefulVoteScore + viewCount;
return PopularityScore;
}
This seems to work fine. Now, what I'd like to do is take this method and apply it to a number of stories (i.e. a list).
I currently have this method written. It has not yet implemented another loop to look through the replies to the comments collection of a story. I know nested loops are considered bad and slow. What would be the most efficient way to look at the list of stories, then the list of comments in each story, add up those replies, and calculate a story's popularity score?
public void CalculateStoryPopularityScore(List<Story> stories)
{
if (stories == null) throw new ArgumentException("Stories can't be null");
double CommentScore;
double InsightfulVoteScore;
double UsefulVoteScore;
double PopularityScore;
double ViewCount;
foreach (var story in stories)
{
CommentScore = story.Comments.Count() * 4;
InsightfulVoteScore = story.InsightFulVoteCount * 3;
UsefulVoteScore = story.UsefulVoteCount * 2;
ViewCount = story.ViewCount;
PopularityScore = CommentScore + InsightfulVoteScore + UsefulVoteScore + ViewCount;
story.PopularityScore = PopularityScore;
}
}
Use SelectMany
var commentCount = story.Comments.Count();
// count all replies to all comments for a story
var replyCountSum = story.Comments
.SelectMany(c => c.Replies)
.Count();
Apply to a collection of stories:
stories.Select(s => new
{
Story = s,
CommentCount = s.Comments.Count(),
ReplyCount = s.Comments.SelectMany(c => c.Replies).Count(),
});
Unless I'm missing something, all the scores you're calculating with a separate method can instead be written as a public read-only (calculated) property of the Story class. The reply count can be obtained by using SelectMany (which is used to flatten lists of lists into a single list) and then getting the Count property:
public class Story
{
public List<Comment> Comments { get; set; }
public int InsightFulVoteCount { get; set; }
public int UsefulVoteCount { get; set; }
public int ViewCount { get; set; }
public int PopularityScore
{
get
{
return
(Comments?.Count ?? 0) * 4 +
(Comments?.SelectMany(comment => comment.Replies).Count() ?? 0) * 4 +
InsightFulVoteCount * 3 +
UsefulVoteCount * 2 +
ViewCount;
}
}
}
public class Comment
{
public List<string> Replies { get; set; }
}
In case you're not familiar with the null-conditional operator (?.), it returns null if the left operand (the object) is null before accessing the right operand (property or method of the object). If the left side is not null, then the property/method value is returned.
Then the null-coalescing operator (??) evaluates the left operand (which is the result of the property or method access) and, if it's null, it returns the right operand ('0' in our case).
Basically this simplifies the code. You don't have to do:
var score = 0;
if (Comments != null) score = Comments.Count;
You can just do:
var score = Comments?.Count ?? 0;
I want to return an object as the result of a calculation in a method but I get "Cannot implicitly convert type 'ConsoleApp2Iteration.LoanOut' to 'double". The "return new LoanOut" is underlined. What is going wrong? I need the three outputs (InterestPaymentAnnual etc.) as input to other calculations elsewhere.
public class Loan
{
> initialisers here
public double LoanCalc()
{
double nper = LoanYrs * NprPrYr;//payment terms per yr
`double EffectiveRate = Math.Pow(1 + (InterestPct + ContribPct),
(Math.Pow(NprPrYr, -1))) - 1;`
//interest per payment term
double Interest_Contribution = InterestPct + ContribPct;
double length = nper;
double InterestPaymentP1 = 0;
{
Pymnt = Financial.Pmt(EffectiveRate, nper, LoanNPV, 0, 0);
LoanOutstanding = LoanNPV;
for (int i = 1; i <= length; i++)
{
// code block removed for clarity
if (i % NprPrYr != 0)
// testing for full years as derived calculations use input inyears
{
InterestPaymentAnnual += Interest_ContributionUSD;
RePymntAnnual += RePymnt;
}
else
{
InterestPaymentAnnual += Interest_ContributionUSD;
RePymntAnnual += RePymnt;
// new object which containts annual interest payment, annual repayment and
// remaining loan amount
return new LoanOut(InterestPaymentAnnual, RePymntAnnual,
LoanOutstanding);
//initialisation before new payment cycle
InterestPaymentAnnual = 0;
RePymntAnnual = 0;
}
}
}
return InterestPymntP1;
}
}
public class LoanOut
{
public double InterestPaymentAnnual { get; private set; }
public double RePymntAnnual { get; private set; }
public double LoanOutstanding { get; private set; }
double InterestPymntP1 = 0;
double Repayment = 0;
double DebtRemaining = 0;
public LoanOut(double InterestPaymentAnnual, double RePymntAnnual,
double LoanOutstanding)
{
this.InterestPaymentAnnual = InterestPymntP1;
this.RePymntAnnual = Repayment;
this.LoanOutstanding = DebtRemaining;
}
}
Your method is declared as returning a double and you have a statement
return new LoanOut(InterestPaymentAnnual, RePymntAnnual, LoanOutstanding);
Change the return type of your method to LoanOut:
public LoanOut LoanCalc()
As others have stated, you are trying to return both a LoanOut instance, and a double.
return new LoanOut(InterestPaymentAnnual, RePymntAnnual, LoanOutstanding); //Returning LoanOut Instance
and
return InterestPymntP1; //Returning double
if you need (or want) to gain two values from a method call, you should look at out parameters.
EG.
public LoanOut LoanCalc(out double InterestPayment)
{
//Your stuff here
}
You have to assign to InterestPayment for the calling code to use it.
Alternatively you can change your LoanOut class to also contain your 'InterestPayment' informaiton on that object. You would only NOT do that if the InterestPayment info doesn't relate to the LoanOut instance... which looking at it probably isn't the case.
Also, if you do fix this and keep the method structured as displayed in your question, You have unreachable code directly after the return new LoanOut line...
InterestPaymentAnnual = 0;
RePymntAnnual = 0;
will never get run
You have method public double LoanCalc() but you try to return new LoanOut(). So the error is correct, you can't convert LoanOut to double. Change the method signature to return LoanOut.
Also, if the pasted code formatting is what you have in your editor I would strongly suggest to reformat your code to make it more readable.
If i'm reading this right, you want to return a LoanOut but you're getting the error that you are trying to convert from LoanOut to double. Your method, however specifically says that you'll be returning a double:
public double LoanCalc()
I would try replacing double with LoanOut like this:
public LoanOut LoanCalc()
Does that help?
I've the following classes;
public class PricePlan
{
public string Name { get; set; }
public List<Price> Prices { get; set; }
public PricePlan()
{
Prices = new List<Price>();
}
}
public class Price
{
public DateTime Date { get; set; }
public decimal Rate { get; set; }
public bool Free { get; set; }
public Price()
{
Free = false;
}
}
And then the following to populate the object and list;
PricePlan oPricePlan = new PricePlan();
oPricePlan.Name = "Standard Rate Plan";
Price oPrice;
DateTime oDate = DateTime.Today;
for (int x = 1; x < 10; x++)
{
oPrice = new Price();
oPrice.Date = oDate.AddDays(x);
oPrice.Rate = 10 * x;
oPricePlan.Prices.Add(oPrice);
}
oPrice = new Price();
oPrice.Date = oDate.AddDays(11);
oPrice.Rate = 10;
oPricePlan.Prices.Add(oPrice);
The sample data might be:
02/01/2013,10,False
03/01/2013,20,False
04/01/2013,30,False
05/01/2013,40,False
06/01/2013,50,False
07/01/2013,60,False
08/01/2013,70,False
09/01/2013,80,False
10/01/2013,90,False
12/01/2013,10,False
Using
oPricePlan.Prices.Min(r => r.Rate)
I get get the Min value for the Rate or IndexOf[] can return the first instance. However, I'm wanting to return X number of lowest rates. For example how can I set the following;
For 1 Min rate (two rates might have the same Min) in the system, set it to 0 zero and the Free bool to true
For 2 Min rates (that might be the same), set it to 0 zero and the Free bool to true
So basically I'm wanting to find the lowest X number of rates, change the actual lowest rates found, and set the Free bool flag to true.
Should I look at using LINQ, or is their a preferred way ?
int numberOfItems = 1;
var orderedPrices = oPricePlan.Prices.OrderBy(x => x.Rate).ToList();
decimal targetRate = orderedPrices[numberOfItems - 1].Rate;
foreach (var price in orderedPrices.TakeWhile(x => x.Rate <= targetRate))
{
price.Rate = 0;
price.Free = true;
}
Edit: The above is based on selecting a targetRate based on numberOfItems, and then setting all items less than or equal to that to 0 (which might be numberOfItems or a little more items). Originally I had:
For your example input, this code will select one of the items with a rate of 10 (it'll be whichever happened to come first in oPricePlan.Prices since OrderBy is stable). That is, it is the number of items, not the number of distinct rates. I think that's what you're asking for; otherwise a solution like Tim Schmelter's is right.
int numberOfItems = 1;
foreach (var price in oPricePlan.Prices.OrderBy(x => x.Rate).Take(numberOfItems))
{
price.Rate = 0;
price.Free = true;
}
You could use OrderBy + GroupBy, Take and a loop:
var priceGroups = oPricePlan.Prices
.OrderBy(p => p.Rate) // order by rate ascending
.GroupBy(p => p.Rate) // group by rate
.First() // use the lowest price-rate group only
.Take(2); // change 2 to 1 if you only want to modify one price in this min-group
foreach (Price price in priceGroups)
{
price.Rate = 0;
price.Free = true;
}
Just quite confused with the piece of code below.
I have class like below
public class Counter
{
public Double NormalCounter { get; set; }
public Double SATCounter { get; set; }
public Double SUNCounter { get; set; }
}
in my main class i have method to do some calculation to fill the counter
Counter CountHrs = GetBookedHours(resourceBkg, PrevEvent);
var lstExpResult = new List<string> {CountHrs.NormalCounter.ToString(),
CountHrs.SATCounter.ToString(),
CountHrs.SUNCounter.ToString()};
UpdateBooking(bookingSesid, lstExpResult);
Just assume i have the value like below in the counter
NormalCounter =4
SATCounter=10
SUNCounter=6
am trying to add in to string list and update the database.is that the right way to do ? or any other options i have please.
my update booking method is below to give clear idea.
public static bool UpdateBooking(string BookingSesid,List<string>HoursByRate)
{
SchedwinEntities db = new SchedwinEntities();
string value = string.Empty;
for (int i = 0; i < 5; i++)
{
string str = " ";
if (i < HoursByRate.Count())
{
str = HoursByRate[i];
value += str + ((char)13).ToString() + ((char)10).ToString();
}
}
var query =
from SEVTs in db.SEVTs
where
SEVTs.SESID.Trim() == BookingSesid//SESID
select SEVTs;
foreach (var SEVTs in query)
{
SEVTs.USER3 = value;//value
}
try
{
db.SaveChanges();
return true;
}
catch (UpdateException ex)
{
return false;
}
}
Rather than passing a list of strings that represent doubles, you should pass a list of key-value pairs, construct a parametersized statement from them, and use the list of key-value-pairs to bind parameter values, like this:
class SqlParamBinding {
string Name {get;set;}
object Value {get;set;}
}
var lstExpResult = new List<SqlParamBinding> {
new SqlParamBinding { Name = "NormalCounter", Value = CountHrs.NormalCounter}
, new SqlParamBinding { Name = "SATCounter", Value = CountHrs.SATCounter}
, new SqlParamBinding { Name = "SUNCounter", Value = CountHrs.SUNCounter}
};
UpdateBooking(bookingSesid, lstExpResult);
Now that lstExpResult separates names from values, your UpdateBooking code could format the SQL expression as
WHERE NormalCounter=#NormalCounter AND SATCounter=#SATCounter AND ...
and then bind #NormalCounter, #SATCounter, and #SUNCounter to the values passed in the lstExpResult list.
If it is going to list of Counter classes then have List instead of List. Looks like you might be having bunch of Counter objects that might be getting updated or sent to the database.
Counter c = new Counter();
c. NormalCounter = 4
c.SATCounter = 10
c.SunCounter = 10
List<Counter> listCounter = new List<Counter>
listCounter.Add(c);
Code is more maintainable and readable.
If you are sending one object at a time, then no need of list at all. You can pass in the counter object to your UpdateMethod and parse it while updating the database.
class FxRate {
string Base { get; set; }
string Target { get; set; }
double Rate { get; set; }
}
private IList<FxRate> rates = new List<FxRate> {
new FxRate {Base = "EUR", Target = "USD", Rate = 1.3668},
new FxRate {Base = "GBP", Target = "USD", Rate = 1.5039},
new FxRate {Base = "USD", Target = "CHF", Rate = 1.0694},
new FxRate {Base = "CHF", Target = "SEK", Rate = 8.12}
// ...
};
Given a large yet incomplete list of exchange rates where all currencies appear at least once (either as a target or base currency): What algorithm would I use to be able to derive rates for exchanges that aren't directly listed?
I'm looking for a general purpose algorithm of the form:
public double Rate(string baseCode, string targetCode, double currency)
{
return ...
}
In the example above a derived rate would be GBP->CHF or EUR->SEK (which would require using the conversions for EUR->USD, USD->CHF, CHF->SEK)
Whilst I know how to do the conversions by hand I'm looking for a tidy way (perhaps using LINQ) to perform these derived conversions perhaps involving multiple currency hops, what's the nicest way to go about this?
First construct a graph of all your currencies:
private Dictionary<string, List<string>> _graph
public void ConstructGraph()
{
if (_graph == null) {
_graph = new Dictionary<string, List<string>>();
foreach (var rate in rates) {
if (!_graph.ContainsKey(rate.Base))
_graph[rate.Base] = new List<string>();
if (!_graph.ContainsKey(rate.Target))
_graph[rate.Target] = new List<string>();
_graph[rate.Base].Add(rate.Target);
_graph[rate.Target].Add(rate.Base);
}
}
}
Now traverse that graph using recursion:
public double Rate(string baseCode, string targetCode)
{
if (_graph[baseCode].Contains(targetCode)) {
// found the target code
return GetKnownRate(baseCode, targetCode);
}
else {
foreach (var code in _graph[baseCode]) {
// determine if code can be converted to targetCode
double rate = Rate(code, targetCode);
if (rate != 0) // if it can than combine with returned rate
return rate * GetKnownRate(baseCode, code);
}
}
return 0; // baseCode cannot be converted to the targetCode
}
public double GetKnownRate(string baseCode, string targetCode)
{
var rate = rates.SingleOrDefault(fr => fr.Base == baseCode && fr.Target == targetCode);
var rate_i rates.SingleOrDefault(fr => fr.Base == targetCode && fr.Target == baseCode));
if (rate == null)
return 1 / rate_i.Rate
return rate.Rate;
}
Disclaimer: This is untested. Further, I'm sure this isn't the most performant approach to solve the problem (O(n) I think), but I believe it will work. There are a number of things you could add to improve the performance (e.g. saving every new combined rate calculation would eventually turn this into an effective O(1))
Wouldn't it be simpler to just have a list of all conversions to a single currency and then use that for any conversion? So something like (with USD as the base currency):
var conversionsToUSD = new Dictionary<string, decimal>();
public decimal Rate ( string baseCode, string targetCode )
{
if ( targetCode == "USD" )
return conversionsToUSD[baseCode];
if ( baseCode == "USD" )
return 1 / conversionsToUSD[targetCode];
return conversionsToUSD[baseCode] / conversionsToUSD[targetCode]
}
Now, this assumes that algebra is perfectly communicative. I.e., if I convert to EUR->USD->GBP I'll get the same as converting from EUR->GBP. That might not actually be the case in reality in which case, you would need every supported permutation.
Interesting problem!
First off, stay clear from double / floating point arithmetic. The .NET Decimal type should be quite sufficient and provide better precision! Such improved precision may be particularly important given the fact that the calculation of derived Fx rates requires a chain of multiple operations.
Another remark is that it is probably off-limits to introduce a simpler/shorter list of Exchange rates, whereby the Target is always the same [real or fictitious] currency. I'm assuming here that we should use the listed rate when available.
So figuring out derived rates should become a [simplified] network solution, whereby
Given a Base and Target currencies, we identify all the shortest pathes (from Base to Target), given the authoritative (non derived) rates in the list. (We can hope that the shortest path would be 2, in all cases, but this may not be the case given very esoteric currencies).
for each of these shortest paths (I think it would be ludicrous to also consider longer pathes), we perform the simple arithmetic conversion, and...
hopefully confirm that these derived rates are all within a nominal margin of conversion error and therefore take the average of these rates
raise some alert... or just make a lot of money by using making a circular path and raking in the differential ;-)
I have no idea what that "double currency" is for... i'll just ignore it.
Attempt: List<List<FxRate>> res = Rates("EUR", "CHF"); yields {EUR-USD, USD-CHF}.
Looks promising! :)
public class FxRate
{
public string Base { get; set; }
public string Target { get; set; }
public double Rate { get; set; }
}
private List<FxRate> rates = new List<FxRate>
{
new FxRate {Base = "EUR", Target = "USD", Rate = 1.3668},
new FxRate {Base = "GBP", Target = "USD", Rate = 1.5039},
new FxRate {Base = "USD", Target = "CHF", Rate = 1.0694},
new FxRate {Base = "CHF", Target = "SEK", Rate = 8.12}
// ...
};
public List<List<FxRate>> Rates(string baseCode, string targetCode)
{
return Rates(baseCode, targetCode, rates.ToArray());
}
public List<List<FxRate>> Rates(string baseCode, string targetCode, FxRate[] toSee)
{
List<List<FxRate>> results = new List<List<FxRate>>();
List<FxRate> possible = toSee.Where(r => r.Base == baseCode).ToList();
List<FxRate> hits = possible.Where(p => p.Target == targetCode).ToList();
if (hits.Count > 0)
{
possible.RemoveAll(hits.Contains);
results.AddRange(hits.Select(hit => new List<FxRate> { hit }));
}
FxRate[] newToSee = toSee.Where( item => !possible.Contains(item)).ToArray();
foreach (FxRate posRate in possible)
{
List<List<FxRate>> otherConversions = Rates(posRate.Target, targetCode, newToSee);
FxRate rate = posRate;
otherConversions.ForEach(result => result.Insert(0, rate));
results.AddRange(otherConversions);
}
return results;
}
Comments?
PS: you can get the cheaper convertion with double minConvertion = res.Min(r => r.Sum(convertion => convertion.Rate));
The most straight-forward algorithm would probably just be like Dijkstra's shortest path or something on a graph you generate using that list. Being that you don't know beforehand how long the path will be, this isn't really a problem that can be elegantly solved by a LINQ query. (Not that it's not possible, it's just probably not what you should pursue.)
On the other hand, if you know that there is a path from any currency to any other, and that there is only one possible conversion between any two currencies on the list (ie, if USD > EUR and USD > CHF exist, then EUR > CHF doesn't exist or you can ignore it), you can simply generate something like a doubly linked list and traverse. Again though, this isn't something that can be elegantly solved through LINQ.
Generate all of them and cache them. Given initial set this function will generate all existing pairs (inside same list) without graphs or recursion, by simple expanding initial list as it iterates.
public static void CrossRates(List<FxRate> rates)
{
for (int i = 0; i < rates.Count; i++)
{
FxRate rate = rates[i];
for (int j = i + 1; j < rates.Count; j++)
{
FxRate rate2 = rates[j];
FxRate cross = CanCross(rate, rate2);
if (cross != null)
if (rates.FirstOrDefault(r => r.Ccy1.Equals(cross.Ccy1) && r.Ccy2.Equals(cross.Ccy2)) == null)
rates.Add(cross);
}
}
}
This utility function will generate individual cross rate.
public static FxRate CanCross(FxRate r1, FxRate r2)
{
FxRate nr = null;
if (r1.Ccy1.Equals(r2.Ccy1) && r1.Ccy2.Equals(r2.Ccy2) ||
r1.Ccy1.Equals(r2.Ccy2) && r1.Ccy2.Equals(r2.Ccy1)
) return null; // Same with same.
if (r1.Ccy1.Equals(r2.Ccy1))
{ // a/b / a/c = c/b
nr = new FxRate()
{
Ccy1 = r2.Ccy2,
Ccy2 = r1.Ccy2,
Rate = r1.Rate / r2.Rate
};
}
else if (r1.Ccy1.Equals(r2.Ccy2))
{
// a/b * c/a = c/b
nr = new FxRate()
{
Ccy1 = r2.Ccy1,
Ccy2 = r1.Ccy2,
Rate = r2.Rate * r1.Rate
};
}
else if (r1.Ccy2.Equals(r2.Ccy2))
{
// a/c / b/c = a/b
nr = new FxRate()
{
Ccy1 = r1.Ccy1,
Ccy2 = r2.Ccy1,
Rate = r1.Rate / r2.Rate
};
}
else if (r1.Ccy2.Equals(r2.Ccy1))
{
// a/c * c/b = a/b
nr = new FxRate()
{
Ccy1 = r1.Ccy1,
Ccy2 = r2.Ccy2,
Rate = r1.Rate * r2.Rate
};
}
return nr;
}