I just wonder if it's possible to make it better (and I guess there are many, many better ways to do it). So the question is how to return true if in my list are 4 types of components. My list may contain 1000 of them , but I just need to return true if there are 4 objects of different types (CPU, GPU, RAM, PowerSupply). These objects are children of abstract class Component and contain property of enum type.
public bool IsWorking()
{
bool hasCPU = AllComponents.Any(component => component.CompType == ComponentsType.CPU);
bool hasGPU = AllComponents.Any(component => component.CompType == ComponentsType.GPU);
bool hasPowerSupply = AllComponents.Any(component => component.CompType == ComponentsType.PowerSupply);
bool hasRAM = AllComponents.Any(component => component.CompType == ComponentsType.RAM);
bool? hasAllNeededComp // JUST WONDERING HOW TO MAKE IT BETTER
}
It depends on what you mean by "better". But if you want to make this as fast as possible, you should just use a foreach loop like this:
public bool IsWorking()
{
bool hasCPU = false;
bool hasGPU = false;
bool hasPowerSupply = false;
bool hasRAM = false;
foreach(var component in AllComponents)
{
if(component.CompType == ComponentsType.CPU) hasCPU = true;
else if(component.CompType == ComponentsType.GPU) hasGPU = true;
else if(component.CompType == ComponentsType.PowerSupply) hasPowerSupply = true;
else if(component.CompType == ComponentsType.RAM) hasRAM = true;
if(hasCPU && hasGPU && hasPowerSupply && hasRAM) return true;
}
return false;
}
Not as fancy as some of these other solutions, but this will at most iterate through the list once.
Funny linq query :
new [] { ComponentsType.CPU, ComponentsType.GPU, ComponentsType.PowerSupply, ComponentsType.RAM }
.Except(AllComponents.Select(component => component.CompType))
.Any() == false;
You can group by CompType to get distinct ones, then add a where clause including all the needed components and count the result :
AllComponents.GroupBy(c => c.CompType)
.Select(c => c.Key)
.Where(c => c == ComponentsType.CPU
|| c == ComponentsType.GPU
|| c == ComponentsType.PowerSupply
|| c == ComponentsType.RAM)
.Count() == 4;
To be a bit cleaner, you can list the needed components and use Contains() :
var requirements = new List<ComponentsType>
{
ComponentsType.CPU,
ComponentsType.GPU,
ComponentsType.PowerSupply,
ComponentsType.RAM
};
return AllComponents.GroupBy(c => c.CompType)
.Select(c => c.Key)
.Where(c => requirements.Contains(c))
.Count() == 4;
I think it is readable:
public bool IsWorking()
{
return AllComponents.Any(component => component.CompType == ComponentsType.CPU)
&& AllComponents.Any(component => component.CompType == ComponentsType.GPU)
&& AllComponents.Any(component => component.CompType == ComponentsType.PowerSupply)
&& AllComponents.Any(component => component.CompType == ComponentsType.RAM);
}
A more simpler code:
public bool IsWorking()
{
ComponentsType[] requiredComps = new[] { ComponentsType.CPU, ComponentsType.GPU, ComponentsType.PowerSupply, ComponentsType.RAM };
foreach (var requiredComp in requiredComps)
{
if (!AllComponents.Any(component => component.CompType == requiredComp))
return false;
}
return true;
}
Related
At the moment I get the number of possible combinations (Factorial of the number of check-boxes) and write that many if statements, something like:
Assuming I have 3 check-boxes:
if (IncludeIncomingCalls && !IncludeOutgoingCalls && !IncludeExternalCalls)
{
return _callsData.Where(x => x.IncomingCall && !x.OutgoingCall && !x.ExternalCall);
}
if (!IncludeIncomingCalls && IncludeOutgoingCalls && !IncludeExternalCalls)
{
return _callsData.Where(x => !x.IncomingCall && x.OutgoingCall && !x.ExternalCall);
}
if (!IncludeIncomingCalls && !IncludeOutgoingCalls && IncludeExternalCalls)
{
return _callsData.Where(x => !x.IncomingCall && !x.OutgoingCall && x.ExternalCall);
}
if (IncludeIncomingCalls && IncludeOutgoingCalls && !IncludeExternalCalls)
{
return _callsData.Where(x => x.IncomingCall && x.OutgoingCall && !x.ExternalCall);
}
if (IncludeIncomingCalls && !IncludeOutgoingCalls && IncludeExternalCalls)
{
return _callsData.Where(x => x.IncomingCall && !x.OutgoingCall && x.ExternalCall);
}
if (!IncludeIncomingCalls && IncludeOutgoingCalls && IncludeExternalCalls)
{
return _callsData.Where(x => !x.IncomingCall && x.OutgoingCall && x.ExternalCall);
}
Even though this will meet the requirement I don't see it as an optimal solution considering that the number of check-boxes might increase in the future and the number of the combinations could get massive.
I was wandering if there is a known pattern when it comes to filtering lists based on selected check-boxes?
Compare the Boolean value with each field.
Try this:
return _callsData.Where(x => x.IncomingCall == IncludeIncomingCalls && x.OutgoingCall == IncludeOutgoingCalls && x.ExternalCall== IncludeExternalCalls);
Try this :
return _callsData.Where(x => x.IncomingCall==IncludeIncomingCalls && x.OutgoingCall==IncludeOutgoingCalls && x.ExternalCall==IncludeExternalCalls);
Note that you can compose the IQueryable. You can add additional Where clauses as needed.
var result = callsData.Select(x => x);
if (IncludeIncomingCalls) {
result = result.Where(x => x.IncomingCall);
}
else {
result = result.Where(x => !x.IncomingCall);
}
if (IncludeOutgoingCalls) {
result = result.Where(x => x.OutgoingCall);
}
else {
result = result.Where(x => !x.OutgoingCall);
}
if (IncludeExternalCalls) {
result = result.Where(x => x.ExternalCall);
}
else {
result = result.Where(x => !x.ExternalCall);
}
return result;
I just show this as a general pattern. For your usecase, the solution of Ubiquitous Developers is easier to read and understand.
But if the condition is more complex than just a bit flag, this pattern might come in handy. Just as an example:
if (ShowOnlyActive) {
result = result.Where(x => x.State == CallState.Active);
}
else {
result = result.Where(x => x.State == CallState.Deleted || x.State == CallState.Inactive);
}
Off topic, just to further illustrate this general concept: adding additional clauses to an IQueryable can be used to refactor parts of a query to helper or extension methods, for example to implement paging.
public interface IPageableQuery {
// The page size (i.e. the number of elements to be displayed).
// The method processing the Query will Take() this number of elements.
int DisplayLength { get; set; }
// The number of elements that have already been displayed.
// The method processing the Query will Skip() over these elements.
int DisplayStart { get; set; }
}
public static IQueryable<T> ApplyPaging<T>(this IQueryable<T> entries, IPageableQuery query)
where T : class {
if (query.DisplayStart >= 0 && query.DisplayLength > 0) {
return entries.Skip(query.DisplayStart).Take(query.DisplayLength);
}
return entries;
}
You could use the following pattern:
//some checkboxes
CheckBox chkA = ...
CheckBox chkB = ...
CheckBox chkC = ...
//build up your filters
var filters = new List<Predicate<SomeEntity>>();
filters.Add(e => chkA.Checked && e.IsA);
filters.Add(e => chkB.Checked && e.IsB);
filters.Add(e => chkC.Checked && e.IsC);
//And now simply apply the filters
var entities = ... //some enumerable of SomeEntity
var filteredEntities = entities.Where(e => filters.All(filter => filter(e)));
Note that this will only work correctly if IsA, IsB and IsC are excluding conditions, but this seems to be the set up you currently have.
I am developing an Asp.Net MVC Application and i am new to Linq and CodeFirst. In my controller this is the action that i have written:
public ActionResult Filter(int? PaperType , int? PaperGram , int? Brand)
{
var FilteredResult ;
if (PaperType.HasValue && PaperGram.HasValue && Brand.HasValue) {
FilteredResult = db.Stocks.where(m => m.PaperType == PaperType && m.PaperGram == PaperGram && m.Brand == Brand);
}
else if (PaperType.HasValue && PaperGram.HasValue) {
FilteredResult = db.Stocks.where(m => m.PaperType == PaperType && m.PaperGram == PaperGram);
}
else if (PaperType.HasValue && Brand.HasValue) {
FilteredResult = db.Stocks.where(m => m.PaperType == PaperType && m.Brand == Brand);
}
// and ifs continue to last
/*
.
.
.
.
*/
else {
FilteredResult = db.Stocks;
}
return View(FilteredResult);
}
But i know that this is not the best way to do in Linq and Codefirst. So, can you give a better solution to this problem?
You can do this:
FilteredResult = db.Stocks.where(m => (m.PaperType == PaperType || !PaperType.HasValue)
&& (m.PaperGram == PaperGram || !PaperGram.HasValue)
&& (m.Brand == Brand || !Brand.HasValue));
What you want to avoid is code duplication.
Create your original IQueriable and then add your where clauses when necessary
public ActionResult Filter(int? PaperType, int? PaperGram, int? Brand)
{
var FilteredResult FilteredResult = db.Stocks.AsQueryable();
if(PaperType.HasValue)
{
FilteredResult = FilteredResult.where(m => m.PaperType == PaperType);
if(PaperGram.HasValue)
FilteredResult = FilteredResult.where(m => m.PaperGram == PaperGram );
if ( Brand.HasValue)
FilteredResult = FilteredResult.where(m => m.Brand == Brand);
}
return View(FilteredResult);
}
You can just assign all elements to list and then filter every element in each if condition
IEnumerable<Stock> filteredResult = db.Stocks.AsQueryable();
if (PaperType.HasValue)
{
filteredResult = filteredResult.Where(m => m.PaperType == PaperType);
}
if (PaperGram.HasValue)
{
filteredResult = filteredResult.Where(m => m.PaperGram== PaperGram);
}
if (Brand.HasValue)
{
filteredResult= filteredResult.Where(m => m.Brand== Brand);
}
return View(FilteredResult.ToList());
Just trying to get a cleaner code on a delete method. I need to delete records from a database if a certain column value matches one of two columns in another table.
Is there a better way to delete multiple records, with a "OR"-like expression, so that I can have only one for each loop instead of the following two?
public static void DeleteStageById(int StageId, int ApplicationId)
{
using (IPEntities ip = IPEntities.New())
{
var stage = ip.mkStages;
var stageCultures = ip.appObjectCultures;
var stageStates = ip.mkStatesInStages;
foreach (var stageCulture in stageCultures.Where(sC => sC.ObjectCultureId == stage.Where(s => s.StageId == StageId && s.ApplicationId == ApplicationId).FirstOrDefault().OCId_Name))
{
stageCultures.DeleteObject(stageCulture);
}
foreach (var stageCulture in stageCultures.Where(sC => sC.ObjectCultureId == stage.Where(s => s.StageId == StageId && s.ApplicationId == ApplicationId).FirstOrDefault().OCId_Description))
{
stageCultures.DeleteObject(stageCulture);
}
...
ip.SaveChanges();
}
}
my linq would look like this one
var stage = ip.mkStages;
var stageCultures = ip.appObjectCultures;
var stageStates = ip.mkStatesInStages;
//store this result into a temp variable so it only needs to run once
var temp = stage.Where(s => s.StageId == StageId && s.ApplicationId == ApplicationId).FirstOrDefault();
if (temp != null)
{
foreach (var stageCulture in stageCultures.Where(sC => sC.ObjectCultureId == temp.OCId_Name || sC.ObjectCultureId == temp.OCId_Description))
{
stageCultures.DeleteObject(stageCulture);
}
...
ip.SaveChanges();
}
I recommend avoiding confusing expressions, but here you go:
foreach (var stageCulture in stageCultures.Where(sC => {
var v = stage.Where(s => s.StageId == StageId && s.ApplicationId == ApplicationId).FirstOrDefault();
return sC.ObjectCultureId == v.OCId_Name || sC.ObjectCultureId == v.OCId_Description;
})
{
stageCultures.DeleteObject(stageCulture);
}
Simply put I was wondering if this is possible?
if (Descriptionsearch.Checked)
{
searchResult = searchResultBuilder(a.Description == textBox.Text))
}
else if (titleSearch.Checked)
{
searchReuslt = searchResultBuilder(a.title == textBox.Text))
}
As you can see I am simply sending a condition of a variable that has not yet been initialized but will be at the time of use.
private List<int> searchResultBuilder(Func<bool> condition)
{
foreach (var element in currentPosition.Where(a => condition()))
{
searchResults.Add(currentPosition.IndexOf(element));
}
return searchResults;
}
I simply wanted to know if there is a way to do this.
since people are asking this is the for loop from my original code
foreach(var element in main.currentPosition.Where(a => (a.key != null && main.msgSigCollection1.msgSig[(int)a.key].Description.IndexOf(searchTextBox.Text, StringComparison.OrdinalIgnoreCase) >= 0) || (a.value != null && main.msgSigCollection2.msgSig[(int)a.value].Description.IndexOf(searchTextBox.Text, StringComparison.OrdinalIgnoreCase) >= 0)))
{
searchResults.Add(main.currentPosition.IndexOf(element));
}
where currentPosition is a List<int?,int?>
You need to specify a lambda syntax to achieve what you want.
Ex: (input parameters) => expression
if(Descriptionsearch.checked == true)
searchReuslt = searchResultBuilder(a => a.Description == textBox.Text))
else if(titleSearch.checked == true)
searchReuslt = searchResultBuilder(a => a.title == textBox.Text))
MSDN
It's hard to tell based on your question, but I think what you want is to be passing lambdas, which is not what you're doing currently.
Something like this seems closer to what you want:
if(Descriptionsearch.checked == true)
searchReuslt = searchResultBuilder(a => a.Description == textBox.Text))
else if(titleSearch.checked == true)
searchReuslt = searchResultBuilder(a => a.title == textBox.Text))
private List<int> searchResultBuilder<T>(Func<T, bool> condition){
var searchResults = new List<int>();
foreach (var element in currentPosition.Where(condition))
{
searchResults.Add(currentPosition.IndexOf(element));
}
return searchResults;
}
In truth though, your question should state what you're trying to accomplish, not just how you're attempting to get there. There probably is a much easier way to accomplish everything you're trying to do here with LINQ.
I am filtering a collection and I perform 2 filters that are the same but for different fields.
There must be a way I can reduce the duplication of code here?
The checks are whether a date has been entered, and whether it is before a cut off date entered by the user.
public override IList<Company> Search()
{
var list = CacheObjects.Subcontractors;
this.ApplicationFormReturnedCheck(ref list);
this.ApplicationFormSentCheck(ref list);
}
private void ApplicationFormReturnedCheck(ref IList<Subcontractor> list)
{
if (this.ApplicationFormNotReturnedFlag == true && this.ApplicationFormReturned != null)
{
list =
list.Where(x => x.ApplicationFormReturned == null || x.ApplicationFormReturned < this.ApplicationFormReturned).ToList();
}
else if (this.ApplicationFormNotReturnedFlag == true)
{
list = list.Where(x => x.ApplicationFormReturned == null).ToList();
}
else if (this.ApplicationFormReturned != null)
{
list = list.Where(x => x.ApplicationFormReturned < this.ApplicationFormReturned).ToList();
}
}
private void ApplicationFormSentCheck(ref IList<Subcontractor> list)
{
if (this.ApplicationFormNotSentFlag == true && this.ApplicationFormSent != null)
{
list =
list.Where(x => x.ApplicationFormSent == null || x.ApplicationFormSent < this.ApplicationFormSent).ToList();
}
else if (this.ApplicationFormNotSentFlag == true)
{
list = list.Where(x => x.ApplicationFormSent == null).ToList();
}
else if (this.ApplicationFormSent != null)
{
list = list.Where(x => x.ApplicationFormSent < this.ApplicationFormSent).ToList();
}
}
I would suggest you can do something as simple as have some instances of Func<Subcontractor,bool> which cover your various scenarios. This is the type of Func that the Where method expects
To demonstrate let me take one of your methods and show you how:
private void ApplicationFormReturnedCheck(ref IList<Subcontractor> list)
{
var isFormReturned = new Func<Subcontractor,bool>(
x => x.ApplicationFormReturned != null);
var isBeforeDate = new Func<Subcontractor,bool>(
x => x.ApplicationFormReturned < this.ApplicationFormReturned);
var isFormReturnedOrBeforeDate= new Func<Subcontractor,bool>(
x => isFormReturned(x) || isFormReturnedBeforeDate(x));
if (this.ApplicationFormNotReturnedFlag == true && this.ApplicationFormReturned != null)
{
list = list.Where(isFormReturnedOrBeforeDate).ToList();
}
else if (this.ApplicationFormNotReturnedFlag == true)
{
list = list.Where(isFormReturned).ToList();
}
else if (this.ApplicationFormReturned != null)
{
list = list.Where(isBeforeDate).ToList();
}
}
The other method you've shown, although having similar logic, uses a different set of variables. (ApplicationFormSent in place of ApplicationFormReturned). The way I see it you have two options
Duplicate the above within the other method, using the differing variable names
Use a more complex method whereby you have these 3 Func's outside of the scope of each method, and able to distinguish which variables (*Sent or *Returned) to use.
The problem with 2. above is that as your perceved "reuse" goes up, the readability of your code goes down.
In all honesty, I see no major problem with your original code! Its clear, its pretty concise and its easy to see what it's doing. By wrapping all this logic up with predicates (which could conceivably be elsewhere in the class), you're making it harder to read and harder to maintain.
A Func(T, TResult) can be used to encapsulate common predicate methods. In your case, the Func would need to be initilialized in the constructor since you are using instance members in the filter. Ex:
private readonly Func<Subcontractor, bool> _pred;
public Subcontractor()
{
_pred = x => x.ApplicationFormReturned == null || x.ApplicationFormReturned < this.ApplicationFormReturned;
}
private void ApplicationFormReturnedCheck( ref IList<Subcontractor> list )
{
if( this.ApplicationFormNotReturnedFlag == true && this.ApplicationFormReturned != null )
{
list = list.Where( _pred ).ToList();
}
else if( this.ApplicationFormNotReturnedFlag == true )
{
list = list.Where( x => x.ApplicationFormReturned == null ).ToList();
}
else if( this.ApplicationFormReturned != null )
{
list = list.Where( x => x.ApplicationFormReturned < this.ApplicationFormReturned ).ToList();
}
}