Lambda Function to Compare 2 Lists With Multiple Criteria - c#

I have a lambda function that I need to use to compare 2 lists and return those records from one list that are in the second list. The lambda function has 4 comparisons. The first three search within the original list, while the 4th is the part that does the comparison.
IEnumerable<IssueDataModel> keywordissues = _adoSqlService.Search_Keyword(searchText);
IEnumerable<IssueDataModel> returnedIssues = issueList;
returnedIssues = issueList.Where(s => s.issueId.ToLower().Contains(searchText.ToLower()) ||
s.issueTitle.ToLower().Contains(searchText.ToLower()) ||
s.issueDescription.ToLower().Contains(searchText.ToLower()) ||
s.issueId.Intersect(keywordissues.Select(y => y.issueId))
);
return returnedIssues;
The problem I'm having is the part were I do the "intersect". I'm getting the following error:
string does not contain a definition for 'Intersect' and the best extension method overload 'Queryable.Intersect<string>' requires a receiver of type 'IQueryable<string>'
How can I perform this short of doing this on the SQL side?

Related

Cannot be inferred from the usage. Try specifying the type arguments explicitly

I want to make a projection as a performance wise but the select part returns an anonymous type and I can't to make required mapping.
var jobDegreesQuery = _context.JOBDEGREEs.AsQueryable().Select(d=> new {d.DEGREE_CODE,d.DEGREE_NAME });
if (!String.IsNullOrWhiteSpace(name))
jobDegreesQuery = jobDegreesQuery.Where(c => c.DEGREE_NAME.Contains(name));
var jobDegreeDTOs = jobDegreesQuery
.ToList()
.Select(Mapper.Map<JOBDEGREE, JobDegreeDTO>); //The error
The type arguments for method 'Enumerable.Select(IEnumerable, Func)' cannot be
inferred from the usage. Try specifying the type arguments explicitly.
How can I do the projection and map to DTO Successfully ?
As I understand you want to map JOBDEGREEs to JobDegreeDTO. You are first selecting it as anonymous type, so I think AutoMapper can not map because you are giving anon. type.
Change your code as below it will perform better:
IQueryable<JOBDEGREEs> jobDegreesQuery = _context.JOBDEGREEs; // it is already queryable
if (!String.IsNullOrWhiteSpace(name))
jobDegreesQuery = jobDegreesQuery.Where(c => c.DEGREE_NAME.Contains(name));
var jobDegreeDTOs = jobDegreesQuery
//.Select(d=> new {d.DEGREE_CODE,d.DEGREE_NAME }) // do you need this?
.Select(d => Mapper.Map<JOBDEGREE, JobDegreeDTO>(d)); // here you can give any expression
.ToList()
What is the result of your ToList()? It is a List of objects of some anonymous class, that contains data extracted from your sequence of JobDegrees
Whenever you want to use Enumerable.Select on a sequence of objects, you'll first have to name an identifier that represents one element of your sequence. This identifier is the part before the =>. After the => you'll write the code to return one object using this input identifier.
This is a difficult way to say something like:
IEnumerable<Person> myPersons = ...
var firstNames = myPersns.Select(person => person.FirstName);
Here the person before the => represents one item of your collection of Persons. Hence person seems a proper name for this identifier.
If you want you can use any identifier to identify a person, although not all identifiers will improve readability:
var firstNames = myPersns.Select(x => x.FirstName);
When using LINQ and entity framework it is good practice to identify collections with plural nouns and elements of collections with singular nouns.
After the => you write some code that uses this input person to return exactly one object. In this example the FirstName of the person.
Back to your question
The result of your ToList is a sequence of objects with a DegreeCode and a DegreeName.
If you want to convert every object in your sequence into one other object (this is called projection), you'll have to identify one object of your sequence before the '=>'.
For example
...ToList()
.Select(extractedDegreeData => ...)
Here, every extractedDegreeData corresponds with one element of your list.
Now what do you want to do with one such extractedDegreeData? You want to return the return value of Mapper.Map<JOBDEGREE, JobDegreeDTO>(extractedDegreeData).
Therefore your code should be like:
...ToList()
.Select(extractedDegreeData => Mapper.Map<JOBDEGREE, JobDegreeDTO>(extractedDegreeData));
Advice:
While constructing your LINQ query, don't use functions like ToList, or any other functions that does not return IEnumerable<TResult>, it is a waste of processing power. What if after your Select you would have put Take(2)? What a waste to create the complete list of 1000 elements if you only wanted the first two!
Therefore functions like ToList, FirstOrDefault, Max, Count should always be the last in your linq query.
Finally: dbContext.JobDegrees is a DbSet<JobDegree>, which implements IQueryable<JobDegree>, hence there is no need to use AsQueryable.

Querying using Lambda Expression for exists within a list

In my project, I need to return a list of car data that does not match the model Id' provided in the array. I'm unsure on how I would go in getting my query to work. So far i have the following below:
var IdList = new List<int> { 60,61,62, 63, 64, 65 };
var query = Context.ManufacturersTable.Where(m => m.Date == date && m.CountryToship = country && m.ExportOnly == false);
if(query.Count() > 0)
query = query.Where(x => x.CarMoreInfoTable.CarModelTable.Where(f => IdList.Contains(f.ModelId))) //Cannot convert lambda expression to intended delegate type error here
return query
As you can see in the above query I have 3 tables all linked to each other. But please could some direct me on how I would query all data that does not contain the Id's in the given array?
Thank you
You have an error because second 'Where' is the issue here, first Where expects to delegate method return bool instead of IEnumerable. (Comment edit) And I think you have to negate:
IdList.Contains(f.ModelId)
Edit
If you need Car Data (I will assume that it is inside CarModel class) then you need change first Where to SelectMany (we would like to merge many collections into one) and negate the inner expression (with contains).
var result = query
.SelectMany(x => x.CarMoreInfoTable.CarModelTable
.Where(f => !IdList.Contains(f.ModelId))).ToList();

Compare two different types of list

I have 2 different types of list and need to compare it.both list have GUID's.
First I will show you the error.
Error 10 'System.Collections.Generic.List' does not contain a definition for 'Except' and the best extension method overload 'System.Linq.Queryable.Except TSource>(System.Linq.IQueryable TSource>, System.Collections.Generic.IEnumerable TSource>)' has some invalid arguments
My code
List<CarViewModel> _GetCarsBeforeMove = icarrepository
.GetList(x => x.CarId.Equals(interlst.Car2Id) && x.ModelId.Equals(interlst.Model2Id))
.Select(x => new CarViewModel
{
CarId = x.CardId, //This returns this type of Id's list 00000000-0000-0000-0000-000000000001
}).ToList();
I have another list,this list also have Guid's
List<Guid> _GetCarsAfterMove = new List<Guid>();
_GetCarsAfterMove .AddRange(interlst.IntLst); //List has values
Now, I wanted to compare above 2 Lists and get the difference
var MovedCars = _GetCarsAfterMove.Except(_GetCarsBeforeMove).ToList(); //Above error shows me here
The method Except expects an IEnumerable<Guid>, not an IEnumerable<CarViewModel>.
Change the last line to:
var MovedCars = _GetCarsAfterMove.Except(_GetCarsBeforeMove.Select(c => c.CarId)).ToList();

Lambda statement Intersect

Trying to work out this lambda query without doing a foreach etc. I currently have a database table with a column which contains a comma separated list of strings. It's basically a filter (it could for example be 'pants,tops,t-shirts,gloves'). In the function that queries the database is basically has a parameter that accepts a similar string.
I don't know if I'm just too tired at the moment and can't work it out but struggling. I know it will be Intersect but can't figure out the syntax.
Currently I have...
public static List<ItemListItem> GetItems(string filter = "")
{
var db = new dbConnection();
var results = (from i in db.Items
select i);
if (!string.IsNullOrEmpty (filter))
results = results.Where(x => x.Filters.Split(',').Intersect(filter.Split(',')) )
}
You need Enumerable.Any at the end of your Intersect like:
x.Filters.Split(',').Intersect(filter.Split(',')).Any()
So your query would be:
results = results.Where(x => x.Filters.Split(',')
.Intersect(filter.Split(','))
.Any());
Enumerable.Where would require an expression returning bool. Intersect would return an IEnumerable<T> and Enumerable.Where would not accept it. Adding Enumerable.Any would means return those rows where intersection resulted in Any row.

convert IQueryable<int> to <int>

I want to select my price level in database to compare with the an integer number. But It is error : Operator '==' cannot be applied to operands of type 'System.Linq.IQueryable' and 'int'.
This is my code :
if (Request.IsAuthenticated){
CustomerModels cm = new CustomerModels();
string userName = Page.User.Identity.Name;
var list_pricelevel = from c in cm.DataContext.Customers
where c.WebAccount == userName
select c.PriceLevel;
if (list_pricelevel == 3) {
Response.Write("Welcome");
}
}
var list_pricelevel
This is per definition not an int because more than one row can be returned.
I don't use SQL syntax (only lambda) but at the end you want the equivalent of a .FirstOrDefault or Single or First. Basically taking the first row.
replace:
if (list_pricelevel == 3)
with:
if (list_pricelevel.First() == 3)
as you can see here: http://msdn.microsoft.com/en-us/library/bb291976.aspx, if you are sure there is a result or use FirstOrDefault...
when you have the result from LinQ expression you will always have the list of result set.
So in your code when you are querying as below :
var list_pricelevel = from c in cm.DataContext.Customers
where c.WebAccount == userName
select c.PriceLevel;
The list_pricelevel will be in the form of List ie IQueryable list,
so you have to get only one element to check with one element
so use the below code :
if (list_pricelevel.Single() == 3)
{
Response.Write("Welcome");
}
or
if (list_pricelevel.First() == 3)
{
Response.Write("Welcome");
}
both the above code gives you only one result set value so you can equate with 3 for validation.
Here is my suggestion:
if (list_pricelevel.First() == 3)
{
Response.Write("Welcome");
}
This may rise a NullReferenceException in case there is no item in Customers satisfying where c.WebAccount == userName.
Explanation:
list_pricelevel is a IEnumerable of items satisfying your where clause.
You need get the first item from your collection.
if (list_pricelevel.First() == 3) {
Response.Write("Welcome");
}
That's the beauty of Linq that every query returns an IQueryable so you can defer getting the final result until you really decided what you want. On the other word you can execute a query over another query :)
So in order to get the real data out of a query you should execute a command over it that actually returns what you want.
In your case since you are expecting your query to return only one value any methods like "FirstOrDefault","Single" or "First" will do the trick

Categories

Resources