I was writing a simple LINQ query which compares rows of a table with itself. Simply put, there are two datagrids on a form, the first one shows some rows and the second one is filled whenever a row is selected in the first grid with the following condition:
Find those rows that have the same code as the selected and their reception time differs less than 30 second (I mean the reception time of shown rows in the second grid must be sooner than reception time of selected row).
I have written the following code:
Call_Log selected = (Call_Log)dataGrid1.SelectedItem;
var subData = from cLog in callLog
where cLog.Check_Create == 1 &&
EntityFunctions.DiffSeconds(selected.ReceptionTime,cLog.ReceptionTime) < 30 &&
selected.CustomerCode == cLog.CustomerCode &&
selected.CalledID == cLog.CalledID &&
selected.ID != cLog.ID
select cLog;
But it returns some rows which differ more than 30 seconds with the selected row. How can I fix this?
Comment:
In the above code, I need to have those rows (cLog) which differ less than 30 seconds from selected row.
Thanks
In case of DiffSeconds return negative value more than 30 seconds, your validation return true.
try compare after taking Math.abs as
Math.Abs(EntityFunctions.DiffSeconds(selected.ReceptionTime,cLog.ReceptionTime)) < 30
Try this.
var subData = from cLog in callLog
let diffInTicks = selected.ReceptionTime.Ticks - cLog.ReceptionTime.Ticks
let thirtySecTicks = new TimeSpan(0, 0, 0, 30, 0).Ticks
where cLog.Check_Create == 1 &&
diffInTicks < thirtySecTicks &&
selected.CustomerCode == cLog.CustomerCode &&
selected.CalledID == cLog.CalledID &&
selected.ID != cLog.ID
select cLog;
Related
I have mssql table as the given image. I want to know how can I return rows from the table tblSpace that the sum of "Available" rows is greater than or equal to given value using Linq. Also there is a order of filling from 1 to 5. Thanks
Table capture and data:> tblSpace
What I've tried so far
1). If I pass 9,000 as value it should return only Vienguard row (9,000 < 10,000)
2). If I pass 23,000 as value it should return both Vienguard and Halestorm rows (23,000 < 10,000+15,000)
3). if I pass 35,000 as value it should return Vienguard, Halestorm and Quarts rows (35,000 < 10,000+15,000+20,000)
Alright so I think you need something like this?
var sum = 0;
var result = tblSpace.OrderBy(x => x.Available).TakeWhile(x => (sum += x.Available) <= value).ToList();
EDIT
In case you want an extra value if it exceeds you add
var result = tblSpace.OrderBy(x => x.Available).TakeWhile(x => (sum += x.Available) - x.Available < value).ToList();
I have a following code:
var tempResults = new Dictionary<Record, List<Record>>();
errors = new List<Record>();
foreach (Record record in diag)
{
var code = Convert.ToInt16(Regex.Split(record.Line, #"\s{1,}")[4], 16);
var cond = codes.Where(x => x.Value == code && x.Active).FirstOrDefault();
if (cond == null)
{
errors.Add(record);
continue;
}
var min = record.Datetime.AddSeconds(downDiff);
var max = record.Datetime.AddSeconds(upDiff);
//PROBLEM PART - It takes around 4,5ms
var possibleResults = cas.Where(x => x.Datetime >= min && x.Datetime <= max).ToList();
if (possibleResults.Count == 0)
errors.Add(record);
else
{
if (!CompareCond(record, possibleResults, cond, ref tempResults, false))
{
errors.Add(record);
}
}
}
variable diag is List of Record
variable cas is List of Record with around 50k items.
The problem is that it's too slowly. The part with the first where clause needs around 4,6599ms, e.g. for 3000 records in List diag it makes 3000*4,6599 = 14 seconds. Is there any option to optimize the code?
You can speed up that specific statement you emphasized
cas.Where(x => x.Datetime >= min && x.Datetime <= max).ToList();
With binary search over cas list. First pre-sort cas by Datetime:
cas.Sort((a,b) => a.Datetime.CompareTo(b.Datetime));
Then create comparer for Record which will compare only Datetime properties (implementation assumes there are no null records in the list):
private class RecordDateComparer : IComparer<Record> {
public int Compare(Record x, Record y) {
return x.Datetime.CompareTo(y.Datetime);
}
}
Then you can translate your Where clause like this:
var index = cas.BinarySearch(new Record { Datetime = min }, new RecordDateComparer());
if (index < 0)
index = ~index;
var possibleResults = new List<Record>();
// go backwards, for duplicates
for (int i = index - 1; i >= 0; i--) {
var res = cas[i];
if (res.Datetime <= max && res.Datetime >= min)
possibleResults.Add(res);
else break;
}
// go forward until item bigger than max is found
for (int i = index; i < cas.Count; i++) {
var res = cas[i];
if (res.Datetime <= max &&res.Datetime >= min)
possibleResults.Add(res);
else break;
}
Idea is to find first record with Datetime equal or greater to your min, with BinarySearch. If exact match is found - it returns index of matched element. If not found - it returns negative value, which can be translated to the index of first element greater than target with ~index operation.
When we found that element, we can just go forward the list and grab items until we find item with Datetime greater than max (because list is sorted). We need to go a little backwards also, because if there are duplicates - binary search will not necessary return the first one, so we need to go backwards for potential duplicates.
Additional improvements might include:
Putting active codes in a Dictionary (keyed by Value) outside of for loop, and thus replacing codes Where search with Dictionary.ContainsKey.
As suggested in comments by #Digitalsa1nt - parallelize foreach loop, using Parallel.For, PLINQ, or any similar techniques. It's a perfect case for parallelization, because loop contains only CPU bound work. You need to make a little adjustments to make it thread-safe of course, such as using thread-safe collection for errors (or locking around adding to it).
Try adding AsNoTracking in the list
The AsNoTracking method can save both execution times and memory usage. Applying this option really becomes important when we retrieve a large amount of data from the database.
var possibleResults = cas.Where(x => x.Datetime >= min && x.Datetime <= max).AsNoTracking().ToList(); //around 4,6599ms
There a few improvements you can make here.
It might only be a minor performance increase but you should try using groupby instead of where in this circumstance.
So instead you should have something like this:
cas.GroupBy(x => x.DateTime >= min && x.DateTime <= max).Select(h => h.Key == true);
This ussually works for seaching through lists for distinct values, but in you case I'm unsure if it will provide you any benefit when using a clause.
Also a few other things you can do throughout you code:
Avoid using ToList when possible and stick to IEnumerable. ToList performs an eager evaluation which is probably causing a lot of slowdown in your query.
use .Any() instead of Count when checking if values exist (This only applies if the list is IEnumerable)
I am new in C# and decided to use the modulo operator.
I have a case where im using a foreach loop to go through a collection of 5300 items.
I am using the modulo operator to do something every 1000th item.
Something like:
if(i % 1000 = 0)
{
//Do something
}
I am hitting the if statement every 1000th but did not consider the last 300 items, which i also need to take into account.
The purpose is to loop through the last 300, if there is any, after i hit the last 1000th item.
Thanks!
Try following
if( i % 1000 == 0 || 5300 - i < 1000)
In this article Test Run K-Means++ he use C# code and Roulette Wheel Selection to get next Centroid
there is a code that implement Roulette Wheel Selection
while (sanity < data.Length * 2)
{
cumulative += dSquared[ii] / sum;
if (cumulative >= p && used.Contains(ii) == false)
{
newMean = ii; // the chosen index
used.Add(newMean); // don't pick again
break; // WHY BREAK ?? THERE IS ANOTHER BIGGER CUMULATIVE VALUES
}
++ii; // next candidate
if (ii >= dSquared.Length)
ii = 0; // past the end
++sanity;
}
but why break when meet first true condition in here :
if (cumulative >= p && used.Contains(ii) == false)
why not looping until index 19 ???
N : 20 item
Random value = 0,817325359590969
I compare the result from the code with Excel : Result if not stop at index 16
can anyone explain to me the answer of this question ?
Because you want a weighted random sampling.
Otherwise, you would always choose the last value.
i am trying to check condition before entering into it but it is entering in wrong condition
my conditions are ,
if (Target.Tables[0].Rows[0]["T_B_CX"].ToString() == "0" && Convert.ToInt64(Target.Tables[0].Rows[0]["T_B_C"]) >= 100000)
if (Target.Tables[0].Rows[0]["T_B_CX"].ToString() != "0" && Convert.ToInt64(Target.Tables[0].Rows[0]["T_B_C"]) > 10000000)
the values are,
T_B_CX = 0 and T_B_C = 2500000000
it must enter the fist condition i mentioned but it is entering in second condition???
Hopes for your suggestion thanks in advance
you can convert to int and do the comparison as below
if (Convert.ToInt(Target.Tables[0].Rows[0]["T_B_CX"].ToString()) == 0 && Convert.ToInt64(Target.Tables[0].Rows[0]["T_B_C"]) >= 100000)
may be when we get ToString of cell value it returns not exactly string equal to "0", debug and see which value you get for Target.Tables[0].Rows[0]["T_B_CX"].ToString()
There is no code between the two conditions, so the first one is taken as expected, then the second one is evaluated till the != 0.
Try to write something like this
// Convert everything just one time here
int tbcx = Convert.ToInt32(Target.Tables[0].Rows[0]["T_B_CX"]);
long tbc = Convert.ToInt64(Target.Tables[0].Rows[0]["T_B_C"]);
if(tbcx == 0 && tbc >= 100000)
// code here
else if(tbcx != 0 && tbc > 2500000000)
// code here
Also try to avoid the conversion of an integer value to a string and then check against a string.
It makes no sense. If an integer is stored in that table then convert it to an integer and check against an integer.