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.
Related
I've got a list as a result of some pixel math like:
List<double> MList = new List<double>(new double[]{ 0.002, 0.123, 0.457, 0.237 ,0.1});
I would like to use Linq, to retrieve from that list, all indexes of items below a value, so if the value to compare against is 0.15 it sould result the folowing indexes :
0,1,4
List<double> MClose = new list<double>();
double compare = 0.15;
List<double> MClose = MList.Where(item => item < compare).Select((item,index) => index);
I hope so far so good, then i would like to use this gained index, to use against another list. That's a list made out of RGB values, to build a new list only out of values selected by that index.
class RGB{int r;int g; int b}
list<RGB>=colors = new RGB(){new RGB[,,]{{10,10,2},{13,11,2},{15,16,17},{33,13,2},{35,116,117}}};
I don't have used Linq a lot, and I wonder if this could be coded trough Linq, maybe even a one liner ?, i'm curious how small an answers could get.
And (would Linq be fast for pixel editing), i'm dealing width convulsion maps here usually 3x3 to 64x64 pixels of data.
List<double> MClose = MList.Where(item => item < compare).Select((item,index) => index);
First you've defined MClose to be a List<double> but your final .Select((item,index) => index) will return an IEnumerable<int> - which isn't a List but a collection that can be iterated over. Use var to automatically infer the type of MClose, and use .ToList() so that the result of the iteration is only evaluated once and brought into memory:
var MClose = MList.Where(item => item < compare).Select((item,index) => index).ToList();
Then you can use the .Where clause with indexes:
var filteredColors = colors.Where((c,index)=> MClose.Contains(index)).ToList();
Use .Contains() to filter only those indexes that you've got in MClose.
You need to reorder your linq methods. First call Select then When:
List<double> MList = new List<double>(new double[] { 0.002, 0.123, 0.457, 0.237, 0.1 });
double compare = 0.15;
var idx = MList.Select((x, i) => new {x, i})
.Where(x => x.x < compare)
.Select(x => x.i)
.ToArray();
Now in idx you will have [0, 1, 4]
Some explanations: after your are applying Where method, your indices will differ from originals. So first you need to save original index, then you may filter MList
I would like to use Linq, to retrieve from that list, all indexes of items below a value, so if the value to compare against is 0.15 it sould result the folowing indexes : 0,1,4
You can get an index of the element by using IndexOf()
List<double> list = new List<double> { 0.002, 0.123, 0.457, 0.237, 0.1 };
List<int> indexes = list
.Where(q => q < 0.15)
.Select(q => list.IndexOf(q))
.ToList();
I hope so far so good, then i would like to use this gained index, to
use against another list. That's a list made out of RGB values, to
build a new list only out of values selected by that index.
Does not make much sense for me.
i want to do the following i have variables stored in an int array called Straight i want to use Linq and get all the values when divided by 4 return 0 i tried this but it will only give me some bool variables and I'm not sure why
var a = Straight.Select(o => o % 4==0).ToArray();
any help is appreciated also i want to note that I'm still learning c# and Linq is something completely new to me
also i want to be able to check the length of the variable
The part you're looking for is Where and not Select.
var a = Straight.Where(o => (o % 4) == 0).ToArray();
Select projects your list into a new returns type which in the case of the expression (o%4) == 0 is boolean.
Where returns you the same object that fulfill the desired expression.
You need Where, not Select
var a = Straight.Where(o => o % 4 == 0).ToArray();
Select creates a projection. In your example, it turns each element of Straight into a bool.
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.
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.