Performance tuning for linq/lambda expression - c#

I have some line of code as below.
string result = listDetails
.Where(filename => filename.Contains(fullname)).FirstOrDefault().Split('\\')
.Where(name => name.Contains(nameGivenToSearch)).FirstOrDefault();
if (result.Contains("sd"))
// Do something
While running a tool for checking the performance it shows the performance warning in above statement.
I want to make this more performance feasible. I have heard that nested lambda expression is slower in executing.
Please suggest something to counter this or any link that will be helpful to solve the problem.

Try caching the data searched (or all of it, before your app starts) into the Dictionary<,>.

I would rewrite it as:
string result = listDetails.FirstOrDefault(filename => filename.Contains(fullname));
if (result != null)
result = result.Split('\\').FirstOrDefault(name => name.Contains(namegiventosearch));
if (result != null && result.Contains("sd"))
{
//do task
}
I don't think there is much room for performance improvement.

If my assumption is correct that the list contains paths to files I think the main problem is that you split the path to get the filename. Use functions Path.GetDirectoryName and Path.GetFileName instead. Searching a list of max 1000 string entries should not be to processor heavy
if (listDetails.Where(x => { if(Path.GetDirectoryName(x).Contains(fullname))
{
var file = Path.GetFileName(x);
return file.Contains(namegiventosearch) && file.Contains("sd");
}
else
{
return false;
}
}).FirstOrDefault() != null)
{
// do Task
}
enter code here

Related

Linq - check if the any of the values present in an array exists in the database table

I am exploring if there is a way to check if any of the values present in an array are present in a table without using a loop.
So even if 1 value is present in the table the method should return true, if none of the values are present then it should return the default false.
I am thinking like the below, below is not a working code just an idea.. so wanted to check if there is an efficient way to achieve this.
public bool CheckTable(string strList)
{
bool IsPresent = False;
String[] Arr = strList.split(',');
foreach(string str in Arr)
{
var x = DBContext.TableEmployees.where(e => e.Location == str).select(e => e.Location);
if(x == null)
{
isPresent = false;
}
else
{
isPresent = true;
}
}
}
Like this:
public bool CheckTable(string strList)
{
string[] strs = strList.Split(',');
return DBContext.TableEmployees.Any(e => strs.Contains(e.Location));
}
Take a read of https://www.microsoftpressstore.com/articles/article.aspx?p=2225065&seqNum=4 for more background on IN/EXISTS and how LINQ expressions are mapped to them.
When working with LINQ it's always wise to be mindful that if your expression can not be understood and translated completely into sql it might not run entirely at the server. Sometimes LINQ to sql will download data and rummage through it locally - the "store execution" section of https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/query-execution has some info (the whole document is worth reading, actually - deferred execution is a common gotcha too)

Combine two expressions using LINQ?

var EXPEarners =
from victor in ins.BattleParticipants
where victor.GetComponent<TotalEXP>() != null
select victor;
foreach (GameObject victor in EXPEarners)
{
victor.GetComponent<TotalEXP>().value += EXPGain;
}
I'm new to LINQ and I would like some help. Is there a way to combine these two blocks of code so I don't have to call GetComponent() twice? (I'm using Unity.) Perhaps introduce a temporary variable and use a foreach loop instead? But the whole purpose of using LINQ was to avoid the foreach.
Also, is there a way to inject methods in between the LINQ statements, like call a void method before I select the final result, in case I want to do something "in between?"
There are a number of ways you could do this, but one small alteration to your query would get you to a single call:
First, get rid of the null check and simply return a map of victor and component:
var EXPEarners =
from victor in ins.BattleParticipants
select new {
victor,
component = victor.GetComponent<TotalEXP>()
};
Then, loop over each pair, adding the experience points if the component isn't null:
foreach (var participant in EXPEarners)
{
// can do something with participant.victor here
if (participant.component != null)
participant.component.value += EXPGain;
}
You could of course shorten this code up quite a bit, but if you do need to do something in between, you have the opportunity.
You could try this alternative:
// Dosomething for every item in the list
ins.BattleParticipants.All(gameObject => Reward(gameObject, EXPGain));
Then you write a method to perform "Reward", which can be as complex as you like
static bool Reward(GameObject gameObject, int EXPGain)
{
TotalEXP exp = gameObject.GetComponent<TotalEXP>();
if (exp != null)
{
exp.value += EXPGain;
return true;
}
return false;
}
And if you want, you can chain these, so for example you can also call a "Bonus" for all those you rewarded (where Reward returned true)
// Reward all EXPGain in the list then give them a Bonus
ins.BattleParticipants.Where(gameObject => Reward(gameObject, EXPGain)).All(gameObject => Bonus(gameObject, BONGain));
Then you write a method to perform "Bonus"
static bool Bonus(GameObject gameObject, int BONGain)
{
SomeOther soc = gameObject.GetComponent<SomeOther>();
if (soc != null)
{
soc.value += BONGain;
return true;
}
return false;
}
If you only want to increment TotalEXP value and you don't use a retrived GameObject somewhere else you can use let and retrive the collection of TotalEXP:
var TotalEXPs =
from victor in ins.BattleParticipants
let component = victor.GetComponent<TotalEXP>()
where component != null
select component;
foreach (TotalEXP expin TotalEXPs)
{
exp.value += EXPGain;
}
Otherwise, you can see #Cᴏʀʏ answer where you can retrive GameObject and it TotalEXP
Try searching for the "let" statement on LINQ. Maybe it can help you.

Iterating through two Lists and checking the item value of the first list to the item values of the second list with a lambda expression

So in the program I'm trying to run I receive two lists, one with objects that contain an id in string format (looks something like "bb_b1203322") and one list with the id's(which in this place is only named "b1203322" for reasons unknown) and a description of the actually id's meaning.
var forms = await _tRepository.GetAllFormsAsync(lastUpdate);
var formDefinitions = await _deRepository.GetAllFormDefintionsAsync();
foreach (var form in forms)
{
foreach (var def in formDefinitions)
{
if (form.SetupFormName.Contains(def.BLKID))
form.SetupFormName = def.DESCR;
}
}
return forms;
Now this piece of code does exactly what I want it to, but I'd rather have it as a lambda expression because ... reasons :)
Now I've tried several different things but with my current knowledge of lambda expressions I can't get it to work.
Try this code. Note that you can use it if formDefinitions with suitable DESCR always exists.
forms.ForEach(f => f.SetupFormName = formDefinitions.FirstOrDefault(fd =>
f.SetupFormName.Contains(fd.DESCR)).DESCR);
This code uses a bit of LINQ to find the definition:
foreach(var form in forms)
{
var def = formDefinitions.FirstOrDefault(x => form.SetupFormName.Contains(x.DESCR));
if(def != null)
form.SetupFormName = def.DESCR
}
As you can see, it's not really saving all that much code.
Please note:
As Jon correctly comments, the behavior of this code is a bit different from your original one. This code uses the first occurrence if there are multiple and your code uses the last occurrence.
If this is actually a use case for your code, replace FirstOrDefault with LastOrDefault.
Extending the code above, you can do something like this:
foreach(var tuple in forms.Select(x => new { Form = x,
Definition =
formDefinitions.FirstOrDefault(y =>
x.SetupFormName.Contains(y.DESCR)) })
.Where(x => x.Definition != null))
{
tuple.Form.SetupFormName = tuple.Definition.DESCR;
}
But as you can see, this gets messy real quick.

While loop returning AFTER exit (IEnumerable involved) causing bounds error

I know there are many ways to skin this cat, but I'm having some issues with a basic while loop that exits properly, but returns (and increases the counter) upon returning an IEnumerable.ToList() from the parent function. Basically what I'm trying to do is search a list of individual keywords and filter down the result set until I get only items that contain ALL keywords.
It exits the loop, and then as soon as the return is hit, it goes BACK into the loop with the i variable incremented another place (higher than the count variable), so broken[i] throws an outside of bounds error.
While I know there are better ways to execute the search, I'm also very interested in WHY the code is going back into an exited loop, incrementing again, and causing the error - and how to fix it. This is very strange, I've never seen this before. Is it because the .ToList is forcing an enumeration back up the chain?
Code:
public IEnumerable<Item> GetItemsForSearch(string search=null)
{
ParserDataContext data = new ParserDataContext();
if (search != null && search != "")
{
string[] broken = search.Split(' ');
IEnumerable<Item> watches = data.Items.Where(x => x.Title.ToLower().Contains(broken[0].ToLower())).OrderByDescending(x => x.DateListed).ThenByDescending(x => x.ID);
int i = 1;
int count = broken.Count();
while (i < count)
{
if (broken[i] != null)
item = items.Where(x => x.Title.ToLower().Contains(broken[i].ToLower()));
i++;
}
return items.Take(50).ToList();
}
}
While I know there are better ways to execute the search, I'm also very interested in WHY the code is going back into an exited loop, incrementing again, and causing the error
The first thing is to understand what is actually going on: the code does not go back into a loop that has exited, it merely uses the value of the variable i at a wrong point in time, i.e. after it has been incremented. This is a known feature: the compiler captures i of broken[i] directly, without making its copy.
Apparently, this feature has been annoying enough for Microsoft to announce a fix for a common case of this error in C# 5.0 (specifically, closing over the loop variable in foreach loop).
and how to fix it.
For users of C# 4.5, a simple fix is to declare a temporary variable instead of using i. The compiler will capture the value of the temporary, making the behavior as you expect:
while (i < count) {
if (broken[i] != null) {
var tmp = broken[i].ToLower();
items = items.Where(x => x.Title.ToLower().Contains(tmp));
}
i++;
}
}
A better approach would be collecting all the broken words together, and then running a selection in a single shot, like this:
var allBroken = broken.Where(b => b != null).Select(ToLower).ToList();
var items = items
.Select(item => new {Item = item, Title = item.Title.ToLower()})
.Where(pair => allBroken.Any(broken => pair.Title.Contains(broken)))
.ToList();

C#: What technic is that code?

I would like to know:
What technic is that code?
Can you rewrite that code to make it more readable, because I do
not completely understand its meaning.
Paragraph para = CaretPosition.Paragraph;
var matchedRun = para.Inlines.FirstOrDefault(inline =>
{
Run run = inline as Run;
return (run != null && run.Text.EndsWith(inputText));
}) as Run;
if (matchedRun != null)
{
}
I'd say a more readable version would be:
var matchedRun = para.Inlines
.OfType<Run>()
.FirstOrDefault(r => r.Text.EndsWith(intputText));
OfType filters the input sequence on the given type (Run) and FirstOrDefault finds the first Run instance whose Text property ends with the given input (or null if none was found).
It's Linq. Do you know the "var" keyword? It's a type that the compiler knows but the programmer doesn't want to write.
The re-written code without using Linq is
Paragraph para = CaretPosition.Paragraph;
Run matchedRun = null;
foreach (var inl in para.Inlines)
{
Run run = inl as Run;
if( (run != null) && run.Text.EndsWith(inputText))
{
matchedRun = run;
break;
}
}
if (matchedRun != null)
{
}
Note that I converted "inline" to "inl". It's not a keyword in C# but Stackoverflow makes it look like one.
Also note that it's even LESS readable once you get accustomed to Linq!
This code appears to be related to the RichTextBox class in the .NET Framework.
The CaretPosition.Paragraph.Inlines is a collection of "Inlines" that make up the body of the paragraph.
The code is basically looking for any Inlines that are of type Run, and setting the value of matchedRun to that first instance if there are any. The FirstOrDefault method is simply a convenient way to look inside a collection of objects and retrieve the first element or a default value if the collection is empty.
As far as readability, if you are familiar with LINQ syntax, it isn't too bad to wade through that code, though I do personally find the example code provided by Lee to be a bit more readable - mostly because of the use of another LINQ expression: OfType
it's called "THE MIGHTY LINQ TECHNIQUE" :)
Jokes apart
it is Linq method to get the First element from the collection or return default value.
var matchedRun = para.Inlines.FirstOrDefault(inline =>{ Run run = inline as Run; return (run != null && run.Text.EndsWith(inputText));}) as Run;
=> is called the Lambda techique for shorthand delagate decalaration
so you can read it as
Find 1st object from the para.Inlines collection where that object EndsWith some user suplied value or retrun default value if no match found
if you don't want to use this technique which actually reduces lot of code so you can try below equibvalent code
Paragraph para = CaretPosition.Paragraph;
var matchedRun = null;
foreach (var inl in para.Inlines)
{ Run run = inl as Run;
if ((run != null) && run.Text.EndsWith(inputText))
{ matchedRun = run; break; }
}
Now you can decide which is better to write

Categories