undefined number of Or-operation on List - c#

I have a List with n-entries. List<MyClass> result
And I have another List with n-Filter options List<string> filters
What i want is to return the result List filtered by the other List.
For an AND-operation its easy like this:
foreach (var filter in filters)
{
results = results.Where(x => x.Result == filter);
}
But how to code for an OR-Operation?

You can use Wherein combination with Any in this case:
results = results.Where(x => filters.Any(f => f == x.Result));
https://msdn.microsoft.com/library/bb534972(v=vs.110).aspx
Others ways:
//Contains, see DAXaholic's post
results = results.Where(x => filters.Contains(x.Result));
https://msdn.microsoft.com/library/bhkz42b3(v=vs.110).aspx
//List Extension method 'Exists'
results = results.Where(x => filters.Exists(f => f == x.Result));
https://msdn.microsoft.com/library/bfed8bca(v=vs.110).aspx

Something like this should work for you
results.Where(x => filters.Contains(x.Result))

Related

Linq Groupby Not Grouping in c#

Below i have a snippet of code which outputs a list of Appointments based on clients, some clients can have more than one appointment but the latest one is the one that needs to be outputted for said client
the output is not grouping at all and for some reason i cannot figure why the heck not
foreach (ClientRecord client in clients)
{
List<ReturnRecord> records = db.Appointments
.AsNoTracking()
.Include(rec => rec.Property)
.Include(rec => rec.Property.Address)
.Include(rec => rec.AppointmentType)
.ToList()
.Where(rec => rec.ClientID == client.ID)
.Select(rec => new ReturnRecord
{
ClientName = $"{client.FirstNames} {client.Surnames}",
PropertyAddress = $"{rec.Property.Address.FormattedAddress}",
AppStatus = $"{rec.AppointmentStatus.Name}",
StockStatus = $"{rec.Property.Stocks.FirstOrDefault().StockStatus.Name}",
LastUpdated = rec.LastUpdated
})
.ToList();
returnList.AddRange(records);
}
returnList.GroupBy(rec => rec.PropertyAddress);
return Ok(returnList);
here is an attachment of the screen grab of the output
You need to assign result of GroupBy() to variable:
returnList = returnList.GroupBy(rec => rec.PropertyAddress).ToList();
Make sure to actually use the new IEnumerable that the .GroupBy() Method returned.
If you want to return a List you need to use a workaround:
Get the IEnumerable<IGrouping<int, ReturnRecord>> from the .GroupBy()
Use .SelectMany() to select all elements and save them into an IEnumerable
Now you can convert your IEnumerable into a List with .List()
Example:
// Longer Alternative
IEnumerable<IGrouping<int, ReturnRecord>> groups = resultList
.GroupBy((rec => rec.PropertyAddress);
IEnumerable<ReturnRecord> result = groups.SelectMany(group => group);
List<ReturnRecord> listResult = result.ToList();
return Ok(listResult);
// Shorter Alternative
IEnumerable<IGrouping<int, ReturnRecord>> groups = resultList
.GroupBy((rec => rec.PropertyAddress);
IEnumerable<ReturnRecord> result = groups.SelectMany(group => group);
return Ok(result.ToList());

Using linq to filter sharepoint-lists with sublists

I have a problem that I can't fix with my rather small LINQ knowledge.
I have a collection of lists that contain a list of fields.
I need only the lists with the properties hidden == false and that got fields with the description "Special Field".
I tried the following approaches... none of them worked:
clientContext.Load(listCollection,
lists => lists
.Where(list => list.Hidden == false)
.SelectMany(list => list.Fields)
.Where(field => field.Description == "Special Field"));
and
var listQuery = from list in listCollection.Where(l => l.Hidden == false)
from field in list.Fields
where field.Description == "Special Field"
select list;
and
var listQuery2 = listCollection
.SelectMany(lists => listCollection)
.Where(l => l.Hidden == false)
.SelectMany(fields => fields.Fields)
.Where(f => f.Description == "Special Field"
all followed by
var result = clientContext.LoadQuery(listQuery2);
clientContext.ExecuteQuery();
None of them worked.
I get the following exception (for the last query but message is similar to the other querys):
The query expression 'value(Microsoft.SharePoint.Client.ListCollection).SelectMany(lists => value(docma.Library.Classes.SharepointDataConnector+<>c__DisplayClass56_0).listCollection).Where(l => (l.Hidden == False)).SelectMany(fields => fields.Fields)' is not supported.
Does anybody clue what I am doing wrong or how to get it to work?
Do I need to use 2 queries?
Performance is important.
Thanks in advance.
As the error says Enumerable.SelectMany is not supported by LINQ to SharePoint provider
To retrieve the following data
I need only the lists with the properties hidden == false and that got
fields with the description "Special Field".
you could utilize the following query via ClientContext.LoadQuery method:
var lists = ctx.Web.Lists;
var result = ctx.LoadQuery(lists.Where(list => list.Hidden == false).Include(l => l.Fields.Where(f => f.Description == "Special Field")));
ctx.ExecuteQuery();
or
var lists = ctx.Web.Lists;
var result = ctx.LoadQuery(lists.Where(list => list.Hidden == false).Include(l => l.Title,l => l.Fields.Where(f => f.Description == "Special Field").Include( f => f.Title, f => f.Description)));
ctx.ExecuteQuery();
where you could specify what properties needs to be returned like List.Title, Field.Title and Field.Description is this case
Update
To return only lists which contains a specific field, the following filter could be applied:
var foundLists = result.Where(l => l.Fields.Count > 0).ToList();

Dynamic linq query that Contains ALL from another list

I want to be able to find all orders with items that contain BOTH apples and oranges that I have in a list.
var itemToFind = new List<string>()
{
"apples",
"cookies"
};
How can I rewrite this so that Contains is dynamic?
This returns what I want but how do I make it loop through my list so that it is dynamic?
var query = result
.Where(o => o.OrderItems
.Any(i => i.Item.Name.Contains("apples")))
.Select(x => x)
.Where(y => y.OrderItems
.Any(b => b.Item.Name.Contains("cookies"))).ToList();
// returns 2 orders
Try something like this:
result.Where(o => o.OrderItems.Any(i => itemToFind.All(itf => i.Item.Name.Contains(itf)))).ToList()
This seems to work but not sure if that is the best way.
foreach (var item in listFacets)
{
// append where clause within loop
result = result
.Where(r => r.RecipeFacets
.Any(f => f.Facet.Slug.Contains(item)));
}

Trying to simplify LINQ expression

I'm trying to simplify a LINQ expression but no matter what i try I'm unable to get it to work
var filterProfileIds = filter.Profiles.Select(s => s.ProfileId);
var newList = new List<FileMedia>();
foreach (var item in filterProfileIds)
{
newList.AddRange(query.Where(w => w.Profiles.Select(s => s.ProfileId).Contains(item)));
}
newList.AddRange(query.Where(w => !w.Profiles.Any()));
query = newList.AsQueryable();
query is of type "FileMedia" and has a relation to Profiles.
So what i want is all the results from the query that has the same profiles that filter.profiles has AND i also want all the results from the query that doesnt have any profiles at all.
Try as the below:
var filterProfileIds = filter.Profiles.Select(s => s.ProfileId);
query = query.Where(w =>
!w.Profiles.Any() ||
w.Profiles.Any(i => filterProfileIds.Contains(i.ProfileId))
).ToList();
If I understand correctly the requirement, you could use a combination of Any and All extension methods like this:
query = query.Where(m => !m.Profiles.Any() ||
filterProfileIds.All(id => m.Profiles.Any(p => p.ProfiledId == id)));
This is if you wish to get the items with exact the same profiles as the filter.
If you indeed want to get the item with any profile contained in the filter, then you could use this instead:
query = query.Where(m => !m.Profiles.Any() ||
m.Profiles.Any(p => filterProfileIds.Contains(p.ProfiledId));
Maybe something like this:
query = (from item in filter.Profiles.Select(s => s.ProfileId)
from fileMedia in query
where fileMedia.Profiles.Select(q => q.ProfileId).Contains(item)
select fileMedia).
Concat(query.Where(w => !w.Profiles.Any())).AsQueryable();

Use List<Tuple<int, int>> to return data in Linq

Given:
List<int> myList;
If I wanted to return data where the record ID was contained in this list I would simply do:
var q = db.Table.Where(c=> myList.Contains(c.ID));
However, given:
List<Tuple<int, int>> myList;
How would I write a Linq query to return records where both conditions are met? With one data point I would write:
var q = db.Table.Where(c=>
c.ID == myList.Item1
&& c.AnotherValue == myList.Item2);
How would I convert the above statement to work on a List<Tuple<int, int>>?
A Tuple is a structure that can't not be translated to sql by your Linq Provider. A solution could be making a switch to Linq to Objects
var q = db.Table.AsEnumerable()
.Where(c=> myList.Any(tuple => c.ID == tuple.Item1 &&
c.AnotherValue == tuple.Item2));
But the bad thing about this solution is that you're going to load all the rows from that table to filter in memory.
Another solution could be using Linqkit:
var predicate = PredicateBuilder.False<Table>();
foreach (string t in myList)
{
predicate = predicate.Or(c =>c.ID == t.Item1 && c.AnotherValue == t.Item2));
}
db.Table.AsExpandable().Where(predicate);
You will find more info about this last solution in this link
var q = db.Table.AsEnumerable().Where(c => myList.Any(tuple => c.ID == tuple.Item1 &&
c.AnotherValue == tuple.Item2));
With Any you can check if there is at least one element in myList the matches your condition.
But as #octaviocci pointed out, this is not translatable to SQL, so you would need to call AsEnumerable() before and do the filtering locally, which may not be what you want if there are a lot of irrelevant records.
Here is some sample code that illustrates one approach:
DataTable dt = new DataTable("demo");
// hydrate your table here...
List<Tuple<int, int>> matches = new List<Tuple<int, int>>();
Func<List<Tuple<int,int>>, DataRow, bool> RowMatches = (items, row) => {
var rowValue1 = (int)row["Id"];
var rowValue2 = (int)row["SomeOtherValue"];
return items.Any(item => item.Item1 == rowValue1 && item.Item2 == rowValue2);
};
var results = dt.Rows.Cast<DataRow>().Where(r => RowMatches(matches, r));
Console.WriteLine(results.Any());
See code below:
List<Tuple<int, int>> myList;
var set = new HashSet(myList);
var q = db.Table.AsEnumerable().Where(c=> set.Contains(new Tuple(c.ID, c.AnotherValue)));
Note that hash set is used to performance-optimize the execution of Where clause for large myList.
Since Tuple cannot be used in Linq to Entities, you can try something like this:
List<int> items1 = myList.Select(t => t.Item1).ToList();
List<int> items2 = myList.Select(t => t.Item2).ToList();
var q = db.Table.GroupBy(m => { m.ID, m.AnotherValue })
.Where(g => items1.Contains(g.Key.ID) &&
items2.Contains(g.Key.AnotherValue))
.SelectMany(g => g);

Categories

Resources