I have a query that I want to substitue the foreach with linq because the foreach is so slow
how can I write all this codein one query
this is my code:
ret = new List<ReportData>();
foreach (var item in Number)
{
string A = item.Substring(0, 11);
string B = item.Substring(14, 2);
string C = item.Substring(19, 11);
string D = item.Substring(33);
ret1 = (from a in Report
where a.A == A && a.B == B && a.C == C && a.D == D && Filter.Type.Contains(a.Y)
select new ReportData
{
X = a.X,
Y = a.Y,
});
if (ret1 != null && ret1.ToList().Count > 0)
{
ret.AddRange(ret1);
}
}
As already mentioned in the comments, LINQ will not make a foreach any faster; if you have to iterate the entire collection, then foreach will be faster than LINQ.
There is no need to check for null or if any results exists in the inner LINQ statement; just add the range since the LINQ query will return an Enumerable.Empty<ReportData> if nothing is returned from the query.
if (ret1 != null && ret1.ToList().Count > 0)
{
ret.AddRange(ret1);
}
// becomes
ret.AddRange(ret1);
Assumming Number is a collection of string, make sure there are no duplicates:
foreach (string item in Number.Distinct())
If Number is a large list, thousands of items or more, then consider using a Parallel.ForEach.
Linq will just enumerate the collection just like a foreach would, but you might see some benefit from a join:
var items = Number.Select( item => new {
A = item.Substring(0, 11),
B = item.Substring(14, 2),
C = item.Substring(19, 11),
D = item.Substring(33),
});
var ret = (from a in Report
join i in items
on new {a.A, a.B, a.C, a.D} equals new {i.A, i.B, i.C, i.D}
where Filter.Type.Contains(a.Y)
select new ReportData
{
X = a.X,
Y = a.Y,
});
Related
I have a array (with type of Object) which I'm extracting from a System.Collections.ArrayList. And I'm now trying to cast this object to a int[] so i can use it to compare it with another int[].
Note: I'm currently using .Net Framework 7.0.0
var query = (from el in l
where el.ID_Seal_From != "" && el.ID_Seal_From != null
select new
{
conn = el.Conn_from,
seal = el.ID_Seal_From
}).ToList();
var query2 = (from el in l
where el.ID_Seal_To != "" && el.ID_Seal_To != null
select new
{
conn = el.Conn_to,
seal = el.ID_Seal_To
}).ToList();
var res = query.Concat(query2).ToList();
ArrayList arrLi = new();
List<int> indexOfEqualElements = new();
for (int i = 0; i < res.Count; i++)
{
for (int j = 0; j < res.Count; j++)
{
if (res[i].seal.CompareTo(res[j].seal) == 0)
{
indexOfEqualElements.Add(j);
}
}
if (Contains(arrLi, indexOfEqualElements.ToArray()) == -1) //to avoid multiple entries
{
arrLi.Add(indexOfEqualElements.ToArray());
}
indexOfEqualElements.Clear();
}
In the "contains" call I'm trying to compare the Elements. For this case i need to avoid, that two equal arrays get added to the list. Because afterwards i need this distinct dataset to continue
I'm sorry for not trying to understand all the code above, but you simply can cast all the values of an ArrayList to an IEnumerable like this:
var arrayList = new ArrayList(2)
{
1,
2
};
var integerEnumerable = arrayList
.Cast<int>();
Feel free to add an .ToArray(), if you like an int[] instead.
ArrayList is deprecated. If you wanted a list you should use List<int>. In this instance, it seems you actually need something like a HashSet<int>.
But to just get distinct elements you can simply use DistinctBy.
var query = (
from el in l
where el.ID_Seal_From != "" && el.ID_Seal_From != null
select new
{
conn = el.Conn_from,
seal = el.ID_Seal_From
});
var query2 = (
from el in l
where el.ID_Seal_To != "" && el.ID_Seal_To != null
select new
{
conn = el.Conn_to,
seal = el.ID_Seal_To
});
var res = query.Concat(query2).DistinctBy(el => el.seal);
I have a linq which is inside a for loop,im adding the results to a list using addRange() but it will add whole thing in a single set,for example my first loop result has 16 items,the second has 10 items,...i want them to be added to list like this then i can see in list how many and which items has been added on each query
public List<statisticsDaily> dailyStat(List<string> id,string dtFrom,string dtTo)
{
List<StatisticsDaily> rsltofquery = new List<StatisticsDaily>();
for (int i = 0; i < id.Count; i++)
{
var rslt = (from d in db.statDaily
join s in db.masterData on d.m_turbine_id equals s.m_turbine_id
where d.m_turbine_id == IPAddress.Parse(id[i]) && d.m_date >= frm && d.m_date <= to
select new StatisticsDaily
{
m_wind_speed = d.m_wind_speed,
Date = d.m_date.ToString("yyyy-MM-dd"),
name = s.turbine_name,
Production = d.m_energy_prod,
Availability = d.m_corrected_av
}
).AsEnumerable().OrderBy(s => s.Date).ToList();
rsltofquery.AddRange(rslt);
}
You need to have collection of collections like List<List<StatisticsDaily>>.
So yours code will be:
public List<List<statisticsDaily>> dailyStat(List<string> id,string dtFrom,string dtTo)
{
List<List<StatisticsDaily>> rsltofquery = new List<List<StatisticsDaily>>();
for (int i = 0; i < id.Count; i++)
{
var rslt = (from d in db.statDaily
join s in db.masterData on d.m_turbine_id equals s.m_turbine_id
where d.m_turbine_id == IPAddress.Parse(id[i]) && d.m_date >= frm && d.m_date <= to
select new StatisticsDaily
{
m_wind_speed = d.m_wind_speed,
Date = d.m_date.ToString("yyyy-MM-dd"),
name = s.turbine_name,
Production = d.m_energy_prod,
Availability = d.m_corrected_av
}).AsEnumerable().OrderBy(s => s.Date).ToList();
rsltofquery.Add(rslt);
}
}
If you want to use all elements, not in parts, you can use SelectMany:
var x = dailyStat(id, dtFrom, dtTo);
foreach (var e in x.SelectMany(d => d)) ...
I'm having a problem with performance in my code.
The method below is used to create a comparative score of companies from the whole country based some rules:
public List<object> GetCNAEBRCycleChart(int VisitId)
{
List<object> result = new List<object>();
Visit visit = Context.Visit.Find(VisitId);
Company company = visit.Company;
var CNAE = company.MainEconomicCNAE.IdentifyCNAE;
string[] Themes = new string[5];
Themes[0] = "Finance";
Themes[1] = "Market";
Themes[2] = "Organization";
Themes[3] = "Planning";
Themes[4] = "People";
int count = 0;
List<Visit> listVisitCNAECountry = (from vis in Context.Visit
where vis.Company.MainEconomicCNAE.IdentifyCNAE.StartsWith(CNAE)
&& vis.Order == 1
select vis
).ToList();
double[] Values = new double[5];
Values[0] = 0;
Values[1] = 0;
Values[2] = 0;
Values[3] = 0;
Values[4] = 0;
foreach (var vis in listVisitCNAECountry)
{
count = 0;
var visitIdCompany = vis.Id;
var diagnostic = Context.Visit.Find(visitIdCompany).Diagnostic;
if (diagnostic != null)
{
foreach (var itemTheme in Themes)
{
var TemaAux = itemTema;
int QtQuestion = (from itemForm in Context.FormItem
join tipo in Context.FormItemType on itemForm.FormItemTypeId equals tipo.Id
join itemForm2 in Context.FormItem on itemForm.FormItemParentId equals itemForm2.Id
join itemForm3 in Context.FormItem on itemForm2.FormItemParentId equals itemForm3.Id
where itemForm3.Name == TemaAux && tipo.Name == "Pergunta"
select itemForm
).Count();
var sumAnswerCompany = (from alter in Context.Alternative
join itemForm in Context.FormItem on alter.FormItemId equals itemForm.Id
join itemForm2 in Context.FormItem on itemForm.FormItemParentId equals itemForm2.Id
join itemForm3 in Context.FormItem on itemForm2.FormItemParentId equals itemForm3.Id
join answer in Context.Answer on itemForm.Id equals answer.FormItemId
where answer.AlternativeId == alter.Id &&
answer.DiagnosticId == diagnostico.Id && itemForm3.Name == TemaAux
select alter.Value
).AsEnumerable().Sum();
double scoreCompany = //Some calculations
Values[count] += scoreCompany;
count++;
}
}
}
count = 0;
foreach (var val in Values)
{
Values[count] = //more calculations
count++;
}
var model = new { NameCategory = "CNAE in Country", Value1 = Values[0], Value2 = Values[1], Value3 = Values[2], Value4 = Values[3], Value5 = Values[4] };
result.Add(model);
return result;
}
The problem is that, with the actual CNAE, the list listVisitCNAECountry gets 16000+ elements, which make for terrible performance.
In my localhost environment it's taking 30min+ and I don't even know where to begin to actually improve the performance.
The biggest problem is that I really need all those iterations to make the calculations right.
If anyone has any ideas, please, help me.
The first thing to change is:
var sumAnswerCompany = ( /* complex query */
).AsEnumerable().Sum();
This is bad; instead of issuing select sum(...) as a database query, it instead will have to select the column(s) and return all the rows required, which may be a huge amount of bandwidth.
Instead, do the sum at the database and just bring back one number:
var sumAnswerCompany = ( /* complex query */
).Sum();
However, frankly I'd suggest writing the entire thing in raw SQL using joins and grouping from the original data. Sometimes LINQ isn't your best tool.
I am using following code in c# as below.
var result = (from o in db.tblOrderMasters
join od in db.tblOrderDetails on o.order_id equals od.orderdetails_orderId
join c in db.tblCountryMasters on o.order_dCountry equals c.country_id
join cu in db.tblCustomers on o.order_Custid equals cu.cust_id
where o.order_id == orderid && o.order_active == true && o.order_IsDeleted == false && (o.order_status == 2)
select new
{
Code = o.order_code,
Name = o.order_dFirstName + " " + o.order_dLastName,
Quantity = od.Quantity,
[...snip...]
}).ToList();
var Qresult = result;
try
{
foreach (var r in result)
{
if (r.Quantity > 1)
{
for (int i = 1; i < r.Quantity; i++)
{
Qresult.Add(r);
}
}
}
}
Collection was modified; enumeration operation may not execute.
As i read other answers related to this error they are saying that you cant modify a list while iterating it, but as in my code i am not modifying the result list instead i am changing a new list which is Qresult and iterating the main result list so why this error is coming ?
one more thing i want to mention here that when i use quickwatch i can see that in result item also added by this line
Qresult.Add(r);
but i am adding items in Qresult so why item added to result
You write :
var Qresult = result;
foreach (var r in result) //result is Qresult
{
..
Qresult.Add(r);
}
it's the same like write:
foreach (var r in Qresult)
{
..
Qresult.Add(r);
}
So you change actually collection
So you want to clone this anonymous type Quantity-times. Since you cannot modify the collection in the foreach (and assigning the list to a different variable doesn't create a copy), you could use this Linq:
var Qresult = result
.SelectMany(o => Enumerable.Range(1, o.Quantity)
.Select(i => new {
Code = o.order_code,
Name = o.order_dFirstName + " " + o.order_dLastName,
Quantity = od.Quantity,
[...snip...]
}
)).ToList();
Qresult is the same object as result - both references the same object in memory. Do not use anonymous object and create new List, as
var Qresult = new List<YourObject>();
try
{
foreach (var r in result)
{
if (r.Quantity > 1)
{
for (int i = 1; i < r.Quantity; i++)
{
Qresult.Add(r);
}
}
}
}
I'm using 2 similar LINQ queries to return a result, the only difference is the where clause (&& s.OptIn == "Yes"). Is there a way to execute this with only one query?
Instead of having a result of
A 2
B 3
and another result of
A 1
B 1
I want to have
A 2 1
B 3 1
Here's the LINQ:
var result = from s in pdc.ScanLogs
from e in pdc.Exhibits
from ce in pdc.ClientEvents
where s.ExhibitID == e.ExhibitID
&& e.ClientEventID == ce.ClientEventID
group 1 by new { ce.EventID } into d
select new {
EventID = d.Key.EventID,
Count = d.Count()
};
var result = from s in pdc.ScanLogs
from e in pdc.Exhibits
from ce in pdc.ClientEvents
where s.ExhibitID == e.ExhibitID
&& e.ClientEventID == ce.ClientEventID
&& s.OptIn == "Yes"
group 1 by new { ce.EventID } into d
select new {
EventID = d.Key.EventID,
Count = d.Count()
};
You can supply a predicate in the Count method. An example is below:
List<int> list = new List<int> { 1, 2, 3, 4, 5 };
var counts = new { CountAll = list.Count(), CountEven = list.Count(i => i % 2 == 0) };
Console.WriteLine(counts.CountEven);
A similar query written for Linq-To-Entities also worked and produced working SQL.
I haven't fully reconstructed your sample, but you should be able to rework it to something like this.
var result = from s in pdc.ScanLogs
from e in pdc.Exhibits
from ce in pdc.ClientEvents
where s.ExhibitID == e.ExhibitID
&& e.ClientEventID == ce.ClientEventID
group new { s, e, ce } by new { ce.EventID } into d
select new
{
EventID = d.Key.EventID,
Count = d.Count(),
CountOptIn = d.Count(item => item.s.OptIn == "Yes")
};
IQueryable<ScanLog> scanlogs = pdc.ScanLogs;
if (filter) scanlogs = scanlogs.Where(...);
var result = from s in scanlogs
...