How to look at multiple conditions with a LINQ statement - c#

I'm currently using a linq statement to move items from one list to another based of a condition, but i need to add a second condition and i think i'm having a syntax issue
What I'm currently doing:
var selected = Generatorlist.CallDataGeneratorsDone
.Where(item => item.SkillNumber == SkillNumber)
.ToList();
Generatorlist.CallDataGeneratorsDone = Generatorlist.CallDataGeneratorsDone
.Except(selected)
.ToList();
Generatorlist.CallDataGeneratorsNotDone.AddRange(selected);
What I'm trying to do:
var selected = Generatorlist.CallDataGeneratorsDone
.Where((item => item.SkillNumber) & (item => item.CallServer))
.ToList();
Generatorlist.CallDataGeneratorsDone = Generatorlist.CallDataGeneratorsDone
.Except(selected)
.ToList();
Generatorlist.CallDataGeneratorsNotDone.AddRange(selected);
I'm having an issue because you can't us & in a Lambda expression. I'm looking for some assistance in figuring out a way to accomplish this.

You just need to remove the second declaration of item => (it only needs to be defined once):
var selected = Generatorlist.CallDataGeneratorsDone
.Where(item => item.SkillNumber && item.CallServer)
.ToList();

You can chain Where calls together like so...
var selected = Generatorlist.CallDataGeneratorsDone
.Where(item => item.SkillNumber)
.Where(item => item.CallServer)
.ToList();

If I don't miss something, it should be && instead of & in the same expression like this:
var selected = Generatorlist.CallDataGeneratorsDone
.Where(item => item.SkillNumber && item.CallServer)
.ToList();
This is assuming that both SkillNumber and item.CallServer are of Boolean. Otherwise you will need to write acomplete predicate like item.SkillNumber = 'blah blah'.

The solution in your case would be:
Generatorlist.CallDataGeneratorsNotDone
.AddRange(Generatorlist.CallDataGeneratorsDone
.Except(Generatorlist.CallDataGeneratorsDone
.Where(item => item.SkillNumber && item.CallServer))
.ToList())

Related

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();

dynamic filters in asp.net mvc 4

I want to do something like this:
var list = db.Respaldoes.Where(x => x.type == "Backup");
if (condicion1)
list.Where(x => x.entity == "GIELCJLT03");
if (condicion2)
list.Where(x => x.activity_type == "0");
I don't know if something like this is possible, I get the list but the filters are never applied.
You could try something like this:
var list = db.Respaldoes.Where(x => x.type == "Backup");
if (condicion1)
list = list.Where(x => x.entity == "GIELCJLT03");
if (condicion2)
list = list.Where(x => x.activity_type == "0");
Initially the list when the Where clause, x => x.type == "Backup", will be executed it will give you the initial list you refer to. Then, if the condicion1 is true, you will make a second fitering and you will assign the result to list. There again you have deffered execution. Only when list will be requested to be consumed by another piece of your code will be executed -hence the name deffered execution. The same holds for the second condicion and so on.
Where returns an IEnumerable<T>, which defers the execution until a later action forces the query to be resolved. If you want to resolve your query, immediately execute it with ToList().
In addition, you are not actually doing anything with your filtered list. By reassigning the filtered list to your original list object, it will update the collection with the filter, removing objects that do not match your predicate.
var list = db.Respaldoes.Where(x => x.type == "Backup");
if (condicion1)
list = list.Where(x => x.entity == "GIELCJLT03").ToList();
if (condicion2)
list = list.Where(x => x.activity_type == "0").ToList();

C# LINQ getting duplicates from a list

I have a list of objects that have a string, and int and another int.
I want to be able to create a list of all the objects that have a duplicate string.
Here is what I have so far:
MyObject duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.ToList();
The error I am getting is that I cannot implicitly convert the type System.Collections.Generic.List<string, MyObject> to MyObject
var duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.SelectMany(g=>g)
.ToList();
you need to write
List<MyObject> duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.ToList();
You could use ToLookup to make a nice data structure with all the info you need
var objectsByString = allMyObjects.ToLookup(o => o.MyString);
This will return a Lookup<string, MyObject>. You can get the duplicate strings using:
var duplicateStrings = objectsByString.Where(l => l.Count()>1).Select(l => l.Key);
which will return a IEnumerable<string> with the duplicate strings. And, for each duplicate you can access the actual objects that have duplicates using something like this:
string duplicateKey = duplicateStrings.First();
var duplicateObjects = objectsByString[duplicateKey]
which returns a IEnumerable<MyObject> with the items that have that string.
There are several problem, the first is a List-of-MyObject cannot be assigned to MyObject, so let's use var to ignore this for a second.
var duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.ToList();
Now, the type of duplicates is List<IGrouping<string, MyObject>> (despite the incorrectly reported error message). Whoops, gotta get rid of (or write to code to account for) the groups!
var duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.SelectMany(g => g)
.ToList();
Now the type of duplicates is List<MyObject>, after having selected every ("selected many") object from every group with more than one item. Better, but this still isn't an MyObject. Well, that's fine: fix the declared type of the variable (that var was previously automatically doing)..
List<MyObject> duplicates = /* same as before */;
Or leave var to do it's thing and if an IEnumerable<MyObject> is fine, simply omit the ToList:
var duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.SelectMany(g => g);
Go forth and iterate thy duplicates!

C# XML Fetching

My XML looks so:
<sensor>
<data>26.7</data>
<data>54.53</data>
<log>false</log>
</sensor>
To retrieve the data fields in a list, I use:
List<string> list = xml.Root.Descendants("sensor").Elements("data").Select(element => element.Value).ToList();
And it works well.
Sometimes the XML looks like this (log = true):
<sensor>
<data>26.7</data>
<data>54.53</data>
<log>true</log>
</sensor>
And I want to ignore these values. How can I achieve this?
I tried this:
var lastUser = xml.Element("response").Descendants("sensor")
.First(u => u.Element("data") != null
&& u.Element("log").Value == "false");
But I only can retrieve of course only the first value.
Why do you use First if you don't want to select the first element? Use Where instead.
var data = xml.Element("response")
.Descendants("sensor")
.Where(x => ((string)x.Element("log")) == "false")
.Elements("data")
.Select(x => x.Value)
.ToList();
Why not use .Where instead of .First and you should be getting an array back?
Use casting nodes to bool or to double:
var data= xml.Element("response").Descendants("sensor")
.Where(s => (bool)s.Element("log"))
.Elements("data")
.Select(d => (double)d)
.ToList(); // produces List<double>
And yes #Daniel is right - First returns only first element from sequence which matches your conditions.
Also consider to use XPath
var data = xdoc.XPathSelectElements("response/sensor[log='true']/data")
.Select(d => (double)d)
.ToList();

Remove duplicates of a List, selecting by a property value in C#?

I have a list of objects that I need some duplicates removed from. We consider them duplicates if they have the same Id and prefer the one whose booleanValue is false. Here's what I have so far:
objects.GroupBy(x => x.Id).Select(x => x.Where(y => !y.booleanValue));
I've determined that GroupBy is doing no such grouping, so I don't see if any of the other functions are working. Any ideas on this? Thanks in advance.
You can do this:
var results =
from x in objects
group x by x.Id into g
select g.OrderBy(y => y.booleanValue).First();
For every Id it finds in objects, it will select the first element where booleanValue == false, or the the first one (if none of them have booleanValue == false).
If you prefer fluent syntax:
var results = objects.GroupBy(x => x.Id)
.Select(g => g.OrderBy(y => y.booleanValue).First());
Something like this should work:
var result =
objects.GroupBy(x => x.Id).Select(g =>
g.FirstOrDefault(y => !y.booleanValue) ?? g.First())
This assumes that your objects are of a reference type.
Another possibility might be to use Distinct() with a custom IEqualityComparer<>.
This partially answers the question above, but I justed need a really basic solution:
objects.GroupBy(x => x.Id)
.Select(x => x.First())
.ToArray();
The key to getting the original object from the GroupBy() is the Select() getting the First() and the ToArray() gets you an array of your objects, not a Linq object.

Categories

Resources