in my polynomial class all the terms consist of a List of tuples (double, uint), representing the coefficient and the exponent; a real and a natural number. The +operator implementation works great, but I was wondering if I could avoid to write two times grouping.Sum(s => s.Item1) It somehow feels not good, but I can't seem find a way to circumvent it.
Here is the code:
public static tuplePolynomial operator +(tuplePolynomial tp1, tuplePolynomial tp2)
{
tuplePolynomial Result = new tuplePolynomial();
Result.Terms =
(
from t in tp1.Terms.Concat(tp2.Terms)
group t by t.Item2 into grouping
where grouping.Sum(s => s.Item1) != 0.0
select new Tuple<double, uint>(grouping.Sum(s => s.Item1), grouping.Key)
).ToList();
return Result;
}
I actually merge the two polymonial's terms and group the terms with the same exponents to sum them. I filter out the terms with zero exponents. Terms is of type List<Tuple<double,uint>>.
This is easy with the let clause:
from t in tp1.Terms.Concat(tp2.Terms)
group t by t.Item2 into grouping
let sum = grouping.Sum(s => s.Item1)
where sum != 0.0
select new Tuple<double, uint>(sum, grouping.Key)
You could just move your where condition outside and apply it after projecting the new tuples.
Then you only apply the Sum operator on each group once and filter the resulting zero sums out before you call ToList.
The code would look something like:
Result.Terms = ( ... ).Where(t => t.Item1 != 0).ToList();
Related
I have a LINQ statement that use average on grade, but the problem is that sometimes
grade gets like 3.777777 displayed, but I would like it to be 3.7 how can I truncate it in my linq statement?
this is my LINQ statement:
public List<CoreValueAndAverageGrade> GetAverageGradeForAllCoreValues2()
{
var answers = db.GoalCardQuestionAnswer
.Where(answer => answer.Grade.HasValue
&& (answer.SelectedQuestion.Question is CoreValueQuestion
&& (answer.SelectedQuestion.Question as CoreValueQuestion).SubjectType.Ignored_Statistic == false));
var groupedByCoreValue = answers.GroupBy(answer => (answer.SelectedQuestion.Question as CoreValueQuestion).CoreValue);
return groupedByCoreValue
.OrderBy(group => group.Key.Name)
.Select(group => new CoreValueAndAverageGrade
{
CoreValue = group.Key,
AverageGrade = group.Any() ? group.Average(answer => answer.Grade.Value) : 0
}).ToList();
Maby its possible to do it inside the controller in my action method?
var averageGrades = OfficeStatisticRepository.GetAverageGradeForAllCoreValues2();
var dataItems = (averageGrades.Select(averageGrade => averageGrade.AverageGrade).ToArray()); // here
You have three options here.
1) Round in the Linq query using Math.Truncate Just like you would use Average or Aggregate. as part of the Linq->SQL translation.
...
... ? group.Average(answer => Math.Truncate(10 * answer.Grade.Value) / 10) : 0M
...
Funny that the docs mention a System.Math.Truncate(decimal, int) method, but that it doesn't actually exist... Luckily you can just multiply and round. This will work fine for Decimals, but if your grade is a Double, it might cause new rounding issues because of the division.
2) Round the values in your Linq query after calling ToList using decimal.Round (be sure to pick the right rounding direction, for grades you won't want to use bankers rounding.
var groupedByCureValue = answers.GroupBy....ToList();
/* then in the next query use Math.Truncate or Math.Round as you
would otherwise, you can now use MidPointRounding if you want to
no sql translation is done as this is all executed in memory,
so you're free to use any framework method available to you. */
3) Keep the values unchanged and only display the rounded value in your UI using a display format such as F1 on the textbox/label/binding you're using to display the value. How you'll set this up is dependent on the display framework you're using. This will not combine values if there's a 3.77 and a 3.76 in the group.
is there any way i can Optimize this:
public static IEnumerable<IEnumerable<int>> GenerateCombinedPatterns
(IEnumerable<IEnumerable<int>> patterns1,
IEnumerable<IEnumerable<int>> patterns2)
{
return patterns1
.Join(patterns2, p1key => 1, p2key => 1, (p1, p2) => p1.Concat(p2))
.Where(r => r.Sum() <= stockLen)
.AsParallel()
as IEnumerable<IEnumerable<int>>;
}
If you're looking for every combination, use SelectMany instead, usually performed with multiple "from" clauses:
return from p1 in patterns1
from p2 in patterns2
let combination = p1.Concat(p2)
where combination.Sum() <= stockLen
select combination;
That's without any parallelism though... depending on the expected collections, I'd probably just parallelize at one level, e.g.
return from p1 in patterns1.AsParallel()
from p2 in patterns2
let combination = p1.Concat(p2)
where combination.Sum() <= stockLen
select combination;
Note that there's no guarantee as to the order in which the results come out with the above - you'd need to tweak it if you wanted the original ordering.
No point in making the query parallel at the very end. Update: Jon was right, my initial solution was incorrect and turns out my corrected solution was essentially the same as his.
public static IEnumerable<IEnumerable<int>> GenerateCombinedPatterns
(IEnumerable<IEnumerable<int>> patterns1,
IEnumerable<IEnumerable<int>> patterns2)
{
var parallel1 = patterns1.AsParallel();
return parallel1.SelectMany(p1 => patterns2.Select(p2 => p1.Concat(p2)))
.Where(r => r.Sum() <= stockLen);
}
I am not sure if CopyMost is the correct term to use here, but it's the term my client used ("CopyMost Data Protocol"). Sounds like he wants the mode? I have a set of data:
Increment Value
.02 1
.04 1
.06 1
.08 2
.10 2
I need to return which Value occurs the most "CopyMost". In this case, the value is 1. Right now I had planned on writing an Extension Method for IEnumerable to do this for integer values. Is there something built into Linq that already does this easily? Or is it best for me to write an extension method that would look something like this
records.CopyMost(x => x.Value);
EDIT
Looks like I am looking for the modal average. I've provided an updated answer that allows for a tiebreaker condition. It's meant to be used like this, and is generic.
records.CopyMost(x => x.Value, x => x == 0);
In this case x.Value would be an int, and if the the count of 0s was the same as the counts of 1s and 3s, it would tiebreak on 0.
Well, here's one option:
var query = (from item in data
group 1 by item.Value into g
orderby g.Count() descending
select g.Key).First();
Basically we're using GroupBy to group by the value - but all we're interested in for each group is the size of the group and the key (which is the original value). We sort the groups by size, and take the first element (the one with the most elements).
Does that help?
Jon beat me to it, but the term you're looking for is Modal Average.
Edit:
If I'm right In thinking that it's modal average you need then the following should do the trick:
var i = (from t in data
group t by t.Value into aggr
orderby aggr.Count() descending
select aggr.Key).First();
This method has been updated several times in my code over the years. It's become a very important method, and is much different than it use to be. I wanted to provide the most up to date version in case anyone was looking to add CopyMost or a Modal Average as a linq extension.
One thing I did not think I would need was a tiebreaker of some sort. I have now overloaded the method to include a tiebreaker.
public static K CopyMost<T, K>(this IEnumerable<T> records, Func<T, K> propertySelector, Func<K, bool> tieBreaker)
{
var grouped = records.GroupBy(x => propertySelector(x)).Select(x => new { Group = x, Count = x.Count() });
var maxCount = grouped.Max(x => x.Count);
var subGroup = grouped.Where(x => x.Count == maxCount);
if (subGroup.Count() == 1)
return subGroup.Single().Group.Key;
else
return subGroup.Where(x => tieBreaker(x.Group.Key)).Single().Group.Key;
}
The above assumes the user enters a legitimate tiebreaker condition. You may want to check and see if the tiebreaker returns a valid value, and if not, throw an exception. And here's my normal method.
public static K CopyMost<T, K>(this IEnumerable<T> records, Func<T, K> propertySelector)
{
return records.GroupBy(x => propertySelector(x)).OrderByDescending(x => x.Count()).Select(x => x.Key).First();
}
With the following data
string[] data = { "a", "a", "b" };
I'd very much like to find duplicates and get this result:
a
I tried the following code
var a = data.Distinct().ToList();
var b = a.Except(a).ToList();
obviously this didn't work, I can see what is happening above but I'm not sure how to fix it.
When runtime is no problem, you could use
var duplicates = data.Where(s => data.Count(t => t == s) > 1).Distinct().ToList();
Good old O(n^n) =)
Edit: Now for a better solution. =)
If you define a new extension method like
static class Extensions
{
public static IEnumerable<T> Duplicates<T>(this IEnumerable<T> input)
{
HashSet<T> hash = new HashSet<T>();
foreach (T item in input)
{
if (!hash.Contains(item))
{
hash.Add(item);
}
else
{
yield return item;
}
}
}
}
you can use
var duplicates = data.Duplicates().Distinct().ToArray();
Use the group by stuff, the performance of these methods are reasonably good. Only concern is big memory overhead if you are working with large data sets.
from g in (from x in data group x by x)
where g.Count() > 1
select g.Key;
--OR if you prefer extension methods
data.GroupBy(x => x)
.Where(x => x.Count() > 1)
.Select(x => x.Key)
Where Count() == 1 that's your distinct items and where Count() > 1 that's one or more duplicate items.
Since LINQ is kind of lazy, if you don't want to reevaluate your computation you can do this:
var g = (from x in data group x by x).ToList(); // grouping result
// duplicates
from x in g
where x.Count() > 1
select x.Key;
// distinct
from x in g
where x.Count() == 1
select x.Key;
When creating the grouping a set of sets will be created. Assuming that it's a set with O(1) insertion the running time of the group by approach is O(n). The incurred cost for each operation is somewhat high, but it should equate to near linear performance.
Sort the data, iterate through it and remember the last item. When the current item is the same as the last, its a duplicate. This can be easily implemented either iteratively or using a lambda expression in O(n*log(n)) time.
I have a list where i whant to change the value of a double property in that list if that property has decimals.
If x.Value has decimals, I want to change that value to take just the first decimal woithout rounding it.
I'm trying this but can't get it right:
(Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement)
var newList =
correctionQoutas.ToList()
.ForEach(x => x.Value%1 != 0 ? x.Value = Convert.ToDouble(string.Format("{0:0.0}", x)) : x.Value = x.Value);
EDIT:
correctionQoutas is a custom object that has four properties. Double StartTime, Double EndTime, Double Value and string Id.
You can't modify a collection while you're iterating it.
Here's a simple approach
var list=correctionQoutas.ToList();
for(int i=0; i<list.Count(); i++)
{
if(list[i].Value % 1 != 0)
{
list[i].Value = Convert.ToDouble(string.Format("{0:0.0}", list[i].Value)) ;
}
}
If you need to transform the elements, then you need to use the Select() LinQ function:
var newList =
correctionQoutas.Select(x => x.Value%1 != 0 ? x.Value = Convert.ToDouble(string.Format("{0:0.0}", x)) : x.Value = x.Value);
This gives you an IEnumerable, to freeze the contents and actually do the evaluation, you can use the ToList() function after the Select() function.
If you simply need to get the elements that are of a certain type, you can do this:
var subList = correctionQoutas.OfType<MyQuota>();
That would give the subset that are MyQuota or are directly assignable to that type. It's an easy way to filter out nulls.
Since you want to change the properties of the elements in your enumeration, you should not be using linq or List<T>.ForEach for that. (Your question does not sound like you want to copy the original objects). So simply iterate over that enumeration and set the new values:
foreach (var quota in correctionQoutas)
quota.Value = (int)(quota.Value * 10) / 10d;
Note that I cut the trailing decimals by simple arithmetic instead of converting the values to strings and back.
Since you save your result to a new List anyway, you can just Select the result instead:
Oh and % wont do you any good here anyway, your number would be converted to int first, if it works at all.
The check if you have decimal places is unneccessary too, since the output will be a double anyway.
// depending on what correctionQuotas is, you might be able to omit ToList()
var newList = correctionQoutas.Select(x => Math.Truncate(10 * x.Value) / 10).ToList();
Without digging into what your actual conversion function is doing. To use a linq statement to get a new List of CorrectionQuota you would do something like this:
var newList = correctionQoutas
.Select(x => Convert(x))
.ToList();
private CorrectionQouta Convert(CorrectionQouta x){
x.Value = x.Value % 1 != 0 ?
Convert.ToDouble(string.Format("{0:0.0}", x)) : x.Value);
return x;
}
Transform the results into what you want first and then make the new List.