How to Refactor LINQ - c#

How can I refactor this code?
Is it possibel to make the aktuelKurs og kursFagenFor in the same line?
EDIT 2
if (aktiekurser != null)
{
int idDato = aktiekurser.Last().IdDato;
for (int i = 0; i < antalDage; i++)
{
aktuelKurs = (from a in aktiekurser
where a.IdDato == idDato - i
select a.Lukkekurs
).Sum();
kursDagenFor = (from a in aktiekurser
where a.IdDato == idDato - (i + 1)
select a.Lukkekurs
).Sum();
gnsOp += aktuelKurs > kursDagenFor ? aktuelKurs :0m;
}
}

This isn't very efficient. First, you query each individual sum separately and, second, in each iteration you calculate a sum that was also calculated in the previous iteration.
You can make this much more efficient by querying al required sums in one grouping query:
var aktuelKurs = from a in aktiekurser
where a.IdDato >= idDato - 1 + antalDage
group a by a.IdDato into grp
select grp.Sum(x => x.Lukkekurs);
Now you have a list of decimals of which you have to determine if elements are greater than their predecessors and Sum the results according to your rule:
var gnsOp = aktuelKurs.Zip(aktuelKurs.Skip(1),
(prev,act) => act > prev ? act :0m).Sum()

Related

C# LINQ - SkipWhile() in reverse, without calling Reverse()?

In this code:
for (e = 0; e <= collection.Count - 2; e++)
{
var itm = collection.Read()
var itm_price = itm.Price
var forwards_satisfied_row = collection
.Skip(e + 1)
.SkipWhile(x => x.Price < ex_price)
.FirstOrDefault();
var backwards_satisfied_row = collection
.Reverse()
.Skip(collection.Count - e)
.SkipWhile(x => x.Price < ex_price)
.FirstOrDefault();
}
Suppose the collection contains millions of items and a Reverse() is too expensive, what would be the best way to achieve the same outcome as 'backwards_satisfied_row' ?
Edit:
For each item in the collection, it should find the first preceding item that matches the SkipWhile predicate.
For context I'm finding the distance a price extrema (minima or maxima) is from a horizontal clash with the price. This gives a 'strength' value for each Minima and Maxima, which determines the importance of it, and to help marry it up with extremas of a similar strength.
Edit 2
This chart shows the data in the reproc code below, note the dip in the middle at item #22, this item has a distance of 18.
Bear in mind this operation will be iterated millions of times.
So I'm trying not to read into memory, and to only evaluate the items needed.
When I run this on a large dataset r_ex takes 5 ms per row, whereas l_ex takes up to a second.
It might be tempting to iterate backwards and check that way, but there could be millions of previous records, being read from a binary file.
Many types of searches like Binary search wouldn't be practical here, since the values aren't ordered.
static void Main(string[] args)
{
var dict_dists = new Dictionary<Int32, Int32>();
var dict = new Dictionary<Int32, decimal> {
{1, 410},{2, 474},{3, 431},
{4, 503},{5, 461},{6, 535},
{7, 488},{8, 562},{9, 508},
{10, 582},{11, 522},{12, 593},
{13, 529},{14, 597},{15, 529},
{16, 593},{17, 522},{18, 582},
{19, 510},{20, 565},{21, 492},
{22, 544},{23, 483},{24, 557},
{25, 506},{26, 580},{27, 524},
{28, 598},{29, 537},{30, 609},
{31, 543},{32, 612},{33, 542},
{34, 607},{35, 534},{36, 594},
{37, 518},{38, 572},{39, 496},
{40, 544},{41, 469},{42, 511},
{43, 437},{44, 474},{45, 404},
{46, 462},{47, 427},{48, 485},
{49, 441},{50, 507}};
var i = 0;
for (i = 0; i <= dict.Count - 2; i++)
{
var ele = dict.ElementAt(i);
var current_time = ele.Key;
var current_price = ele.Value;
var is_maxima = current_price > dict.ElementAt(i + 1).Value;
//' If ele.Key = 23 Then here = True
var shortest_dist = Int32.MaxValue;
var l_ex = new KeyValuePair<int, decimal>();
var r_ex = new KeyValuePair<int, decimal>();
if (is_maxima)
{
l_ex = dict.Reverse().Skip(dict.Count - 1 - i + 1).SkipWhile(x => x.Value < current_price).FirstOrDefault();
r_ex = dict.Skip(i + 1).SkipWhile(x => x.Value < current_price).FirstOrDefault();
}
else
{ // 'Is Minima
l_ex = dict.Reverse().Skip(dict.Count - 1 - i + 1).SkipWhile(x => x.Value > current_price).FirstOrDefault();
r_ex = dict.Skip(i + 1).SkipWhile(x => x.Value > current_price).FirstOrDefault();
}
if (l_ex.Key > 0)
{
var l_dist = (current_time - l_ex.Key);
if ( l_dist < shortest_dist ) {
shortest_dist = l_dist;
};
}
if (r_ex.Key > 0)
{
var r_dist = (r_ex.Key - current_time);
if ( r_dist < shortest_dist ) {
shortest_dist = r_dist;
};
}
dict_dists.Add(current_time, shortest_dist);
}
var dist = dict_dists[23];
}
Edit: As a workaround I'm writing a reversed temp file for the left-seekers.
for (i = file.count - 1; i >= 0; i += -1)
{
file.SetPointerToItem(i);
temp_file.Write(file.Read());
}
You could make it more efficient by selecting the precedent of each item in one pass. Lets make an extension method for enumerables that selects a precedent for each element:
public static IEnumerable<T> SelectPrecedent<T>(this IEnumerable<T> source,
Func<T, bool> selector)
{
T selectedPrecedent = default;
foreach (var item in source)
{
if (selector(item)) selectedPrecedent = item;
yield return selectedPrecedent;
}
}
You could then use this method, and select the precedent and the subsequent of each element by doing only two Reverse operations in total:
var precedentArray = collection.SelectPrecedent(x => x.Price < ex_price).ToArray();
var subsequentArray = collection.Reverse()
.SelectPrecedent(x => x.Price < ex_price).Reverse().ToArray();
for (int i = 0; i < collection.Count; i++)
{
var current = collection[i];
var precedent = precedentArray[i];
var subsequent = subsequentArray[i];
// Do something with the current, precedent and subsequent
}
No need to do .Reverse() and then FirstOrDefault(), just use LastOrDefault(). Instead of Skip(collection.Count - e) use .Take(e) elements
var backwards_satisfied_row = collection
.SkipWhile(x => x.Price < ex_price) //Skip till x.Price < ex_price
.Skip(e+1) //Skip first e+1 elements
.LastOrDefault(); //Get Last or default value
You can make your code more efficient by storing collection and then just get FirstOrDefault() and LastOrDefault() for forwards_satisfied_row and backwards_satisfied_row respectively.
like,
for (e = 0; e <= collection.Count - 2; e++)
{
var itm = collection.Read()
var itm_price = itm.Price
var satisfied_rows = collection
.SkipWhile(x => x.Price < ex_price)
.Skip(e + 1)
.ToList();
var forwards_satisfied_row = satisfied_rows.FirstOrDefault();
var backwards_satisfied_row = satisfied_rows.LastOrDefault();
}

How Do I Check To See If I'm Out Of Bounds In An If Statement?

I have a loop that is trying to sort and organize data.
for (int a = 0; a < Combos.Count; a++)
{
//Largest to smallest
if (Combos.Count - a >= 1)
{
if (scores[a + 1] != null)
{
Combos.Add(Combos[a]);
Combos.RemoveAt(a);
scores.Add(scores[a]);
scores.RemoveAt(a);
}
}
}
I want to execute the nested if statement whenever its valid, which in java i typically use == null, however that seems to not work. Is there an exception I can use or something to check if it's out of bounds?
Instead of this
if (scores[a + 1] != null)
You check the Count(list) or Length(array):
if (a + 1 <= scores.Count)
It's not clar what you're trying to do here, but i guess there are much easier ways
//Largest to smallest
For example with LINQ:
var orderedCombos = Combos.Zip(scores, (c, s) => new{ Combo = c, Score = s})
.OrderByDescending(x => x.Score)
.Select(x => x.Combo)
.ToList();
(but you should really store both informations in the same class or at least don't link via index)

What is best and rapid way for calculate this query?

I'm beginner in c# and linq ,write this query in c#:
var query1 = (from p in behzad.Customer_Care_Database_Analysis_Centers
select p).ToArray();
for (Int64 i = 0; i < query1.Count(); i++)
{
var query2 = (from tt in behzad.Customer_Care_Database_Analysis_DETAILs
where tt.fileid == FILE_ID && tt.code_markaz ==query1[i].code_markaz //"1215" //query1[i].code_markaz.ToString().Trim() //&& tt.code_markaz.ToString().Trim() == query1[i].code_markaz.ToString().Trim()
select new
{
tt.id
}).ToArray();
if (query2.Count() > 0)
{
series1.Points.Add(new SeriesPoint(query1[i].name_markaz, new double[] { query2.Count() }));
counter += 15;
}
}//end for
but up code is very slow,i have about 1000000 Customer_Care_Database_Analysis_Centers and about 20 million record into the Customer_Care_Database_Analysis_DETAILs table,which is best query for up code?thanks.
Your current code first gets a lot of records into memory, then executes a new query for each record - where you only use the count of items, even though you again get everything.
I think (untested) that the following will perform better:
var query = from center in behzad.Customer_Care_Database_Analysis_Centers
join details in behzad.Customer_Care_Database_Analysis_DETAILs
on center.code_markaz equals details.code_markaz
where details.fileid == FILE_ID
where details.Any()
select new { Name = center.name_markaz, Count = details.Count()};
foreach(var point in query)
{
series1.Points.Add(new SeriesPoint(point.Name, new double[] { point.Count };
counter += 15;
}
Instead of a lot of queries, execute just one query that will get just the data needed
Instead of getting everything into memory first (with ToArray()), loop through it as it arrives - this saves a lot of memory

Delimit string and put it in listbox

I have a string like:
one,one,one,one,two,two,two,two,three,three,three,three,four,four,four,four,...
and I'd like to delimit it after every fourth comma and store it into a list box, like this:
one,one,one,one,
two,two,two,two,
three,three,three,three,
four,four,four,four,
...
What should be appropriate way to do this? Should I supposed to use regex to somehow delimit this string?
Thanks
Linqless alternative;
int s = 0, n = 0, len = inputString.Length;
for (var i = 0; i < len; i++) {
if (inputString[i] == ',' && ++n % 4 == 0 || i == len - 1) {
aListBox.Items.Add(inputString.Substring(s, i - s + 1));
s = i + 1;
}
}
This LINQ breaks your input into individual strings by delimiting on the comma, then uses an index in the Select method to group four items together at a time, then finally joins those four items into a single string again.
var input = "one,one,one,one,two,two,two,two,three,three,three,three"; // and so on
var result = input.Split(',')
.Select((s, i) => new {s, i})
.GroupBy(pair => pair.i / 4)
.Select(grp => string.Join(",", grp.Select(pair => pair.s)) + ",");
The result is a collection of strings, where the first one (based on your input) is "one,one,one,one,", then the second is "two,two,two,two," and so on...
From there, it's just a matter of setting it as the DataSource, ItemsSource or similar, depending on what technology you're using.

Histrogram with Linq

Is there a way to do a segmented histrogram with linq? I've seen several examples where you can count the number of occurances of a particular object. Is it possible to create a linq based historgram that counts the number of occurances of a series of objects between two values?
I don't know how you would group by a range of items to create the buckets neccesary for a histogram? Suppose a start bound and a width are used to create the range.
You would need to iterate through the number array and group each number by whether it was <= Upper Bound and > Lower Bound. Then you would just sum each group. I have no idea how to accomplish the group by part
Something like this?
Random randF = new Random();
List<double> nums = new List<double>();
for (int i = 0; i < 100000; i++)
{
nums.Add(randF.NextDouble()*100);
}
double fromXF = 30;
double toXF = 80;
int groupCount = 10; // number of groups between values
var histF = from i in nums
let groupKeyF = ((i-fromXF)/(toXF-fromXF)*groupCount) // even distribution of "groupCount" groups between fromXF and toXF, simple math, really
where groupKeyF >= 0 && groupKeyF < groupCount // only groups we want
let groupKey = (int)groupKeyF // clamp it to only group number
group i by groupKey into gr // group it according to group number
orderby gr.Key
select new { Value = gr.Key, Count = gr.Count() };
foreach (var item in histF)
{
Console.WriteLine("Group number: " + item.Value + ", Count: " + item.Count);
}
You could do something like:
var groups = input.GroupBy(x => (int)((x.value - start)/width));
which creates an integer value for each bar and groups by that.

Categories