Use LINQ variables out of context - c#

How can i use "x" variable in if block? I want to return Id field of x if statement is true.
if (UserList.Any(x => x.Type == (int)UserType.SuperUser))
{
return x.Id;
}

You can't access the "LINQ variable" outside the lambda expression.
What you want to do is to get the Id property of any element x that satsify the condition x.Type == (int)UserType.SuperUser.
In this case, replace your Any() by FirstOrDefault().
var item = UserList.FirstOrDefault(x => x.Type == (int)UserType.SuperUser);
if(item != null)
return item.Id;
FirstOrDefault(x => condition on x) return the first element that satsify the condition and null if no element satisfy the condition.
If you want to get the Id value of all elements that satisfy the condition, use the Where and Select extention methods:
return UserList.Where(x => x.Type == (int)UserType.SuperUser).Select(x => x.Id);
Where(x => condition(x)) returns an IEnumerable that contain all elements that satisfy the condition if you prefere to get a "normal" list, add ToList() before ;.

How many matches are you expecting : i.e. how many users in your list do you expect to have the SuperUser type?
If you expect there to be exactly one user that matches (so having zero matching users or more than one matching user would be an error), then use Single:
return UserList.Single(x => x.Type == (int)UserType.SuperUser).Id;
(Note this will throw an exception if there isn't exactly one matching user; alternatively you can use SingleOrDefault and then test for null.)
If you expect there to be one or more and you just want to return the first one:
return UserList.First(x => x.Type == (int)UserType.SuperUser).Id;
(Note this will throw an exception if there isn't exactly one matching user; alternatively you can use FirstOrDefault and then test for null.)
If you expect there to be one or more and you want to return an array of all the Ids of all the matching users:
return UserList.Where(x => x.Type == (int)UserType.SuperUser).Select(u => u.Id).ToArray();

Just to add to the nice answers already posted.
To make the last code sample from Sam Holloways answer look like you are using the x directly you could rewrite that to the SQL-like syntax (don't remember what that is called):
IEnumerable<int> userIds = from x in UserList
where x.Type == UserType.SuperUser
select x.Id;
You can use the count method on userIds to determine if there's anyone satisfying your condition.
Since you are returning the id, and I guess the id is an int and you'll have to return something anyway, and that I guess you would return 0 if it's not there you could use:
return UserList.Where(x => x.Type == UserType.SuperUser).Select(x => x.Id).FirstOrDefault();

Related

Using Linq when getting value from nested list

I need to set a variable's value to the value of a property that is nested in several Lists. Each list only has one item except for one list. I'm trying to do this:
var myValue = myListA[0].myListB[0].myListC[0].
myListD.Where(x => x.Name == "Misc Expenses").myListE[0].price;
This produces a compile time error that says myListD does not contain a definition for myListE. What is the correct syntax?
After the .Where clause, you need to to .First() (or .ToList()) in order to apply the Where clause:
var myValue = myListA[0].myListB[0].myListC[0].
myListD.Where(x => x.Name == "Misc Expenses").First().myListE[0].price;
Technically, though, you can replace that .Where with .First directly, too:
var myValue = myListA[0].myListB[0].myListC[0].
myListD.First(x => x.Name == "Misc Expenses").myListE[0].price;

How to write a LINQ If Else statement that does not return any value(even a null) if check evaluates to false?

I am writing a LINQ query that is performing a join on two tables, Artists and Groups (musical groups). The query is checking for artists that belong to a specific group and returning them as a list which I will iterate through and print to the console.
The problem is that I'm forced to write an else statement together with my if check and return something. I'm returning null currently, but want to skip that step entirely as the null values show up (as empty values) when I'm printing out my list. I've tried to use the .Distinct() method but it will still leave one null in my list.
var Beatles = Artists.Join(
Groups,
artist => artist.GroupId,
group => group.Id,
(artist, group) =>
{
if(artist.GroupId == 1)
{
return artist.ArtistName;
}
else{
return ;
}
})
.ToList()
.Distinct();
if(Beatles.Any())
{
System.Console.WriteLine("Here are all the members of the Beatles");
foreach(var person in Beatles)
{
System.Console.WriteLine(person);
}
};
You could simply use Where to filter.
var Beatles =
(from a in Artists
join g in Groups on a.GroupId equals g.Id
where a.GroupId == 1
select a.ArtistName).ToList();
And I couldn't figure out why are you applying join, it is completely redundant in this case.
var Beatles = Artists.Where(x => x.GroupId == 1).Select(x => x.ArtistName).ToList();
I've tried to use the .Distinct() method but it will still leave one
null in my list.
I assume that you use Distinct because of filtering the null values. But It is not purpose of Distinct. It is completely misusage.

Proper Syntax for Extension Method Query

I am trying to do something similar to my previous post, except I am using extension methods instead of LINQ. I get an error telling me that && cannot be used, so how would I search within a table using two strings entered by the user?
var query = (App.DBConnection.Table<Notes>().Where(
c => c.Note.Contains(textBox1.Text) && c => c.Note.Contains(textBox2.Text))).Single();
TextBox_Results.Text = query.Note;
Remove the second lambda operator c =>
var query = App.DBConnection.Table<Notes>()
.Where(c => c.Note.Contains(textBox1.Text)
&& c.Note.Contains(textBox2.Text)))
.Single();
Apart from that, i would use FirstOrDefault instead of Single. The latter throws an InvalidOperationException if there are no elements or if there are more than one. The former just returns null if no item matches the predicate in the Where.
You don't need to declare the c variable again
Where(c => c.Note.Contains(textBox1.Text) && c => c.Note.Contains(textBox2.Text)))
should be
Where(c => c.Note.Contains(textBox1.Text) && c.Note.Contains(textBox2.Text)))

how to filter entity type framework object by its child object value properties?

I have an entity framework object called batch, this object has a 1 to many relationship to items.
so 1 batch has many items. and each item has many issues.
I want to filter the for batch items that have a certain issue code (x.code == issueNo).
I have written the following but Im getting this error:
items = batch.Select(b => b.Items
.Where(i => i.ItemOrganisations
.Select(o => o
.Issues.Select(x => x.Code == issueNo))));
Error 1:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<bool>>' to 'bool'
Error 2:
Cannot convert lambda expression to delegate type 'System.Func<Ebiquity.Reputation.Neptune.Model.Item,bool>' because some of the return types in the block are not implicitly convertible to the delegate return type
Select extension method needs a lambda expression that returns a boolean, but the inner o.Issues.Select returns an IEnumerable of boolean to the outer Select(o => o which result in the exception you're getting.
Try using Any instead which verifies that at least one element verifies the condition:
items = batch.Select(
b => b.Items.Where(
i => i.ItemOrganisations.Any(
o => o.Issues.Any(x => x.Code == issueNo)
)
)
);
If I understand correctly, you're trying to select through multiple layers of enumerables. In those cases you need SelectMany which flattens out the layers, not Select. LINQ's syntax sugar is made specifically to make SelectMany easier to reason about:
var items = from item in batch.Items
from org in item.ItemOrganizations
from issue in org.Issues
where issue.Code == issueNo
select item;
The compiler translates that into something like this:
var items = batch.Items
.SelectMany(item => item.ItemOrganizations, (item, org) => new {item, org})
.SelectMany(#t => #t.org.Issues, (#t, issue) => new {#t, issue})
.Where(#t => #t.issue.Code == issueNo)
.Select(#t => #t.#t.item);
You can always wrap this in a Distinct if you need to avoid duplicate items:
var items = (from item in batch.Items
from org in item.ItemOrganizations
from issue in org.Issues
where issue.Code == issueNo
select item).Distinct();
It's hard to tell what you're trying to do based on your code but I think you're looking for something like this;
var issue = batch.Select(b => b.Items).Select(i => i.Issues).Where(x => x.Code == issueNo).Select(x => x).FirstOrDefault();
The above query will return the first issue where the Issues Code property is equal to issueNo. If no such issue exists it will return null.
One problem (the cause of your first error) in your query is that you're using select like it's a where clause at the end of your query. Select is used to project an argument, when you do Select(x => x.Code == issueNo) what you're doing is projecting x.Code to a bool, the value returned by that select is the result of x.Code == issueNo, it seems like you want that condition in a where clause and then you want to return the issue which satisfies it which is what my query is doing.
items = from b in batch.Include("Items")
where b.Items.Any(x=>x.Code==issueNo)
select b;
You're getting lost in lambdas. Your LINQ chains are all embedded in each other, making it harder to reason about. I'd recommend some helper functions here:
static bool HasIssueWithCode(this ItemOrganization org, int issueNo)
{
return org.Issues.Any(issue => issue.Code == issueNo);
}
static bool HasIssueWithCode(this Item items, int issueNo)
{
return items.ItemOrganizations.Any(org => org.HasIssueWithCode(issueNo));
}
Then your answer is simply and obviously
var items = batch.Items.Where(item => item.HasIssueWithCode(issueNo));
If you inline these functions, the result is the exact same as manji's (so give manji credit for the correct answer), but I think it's a bit easier to read.

how to select an item from generic list by linq

I have a LINQ query which contains a method GetInstanceForDatabase()
principlesList.Select(p => p.GetInstanceForDatabase()).ToList()
where
List<PrincipleInstance>() principlesList = ...
// (contains list of principle like "Manual Trades", "OPM", "Flora")
GetInstanceForDatabase() is a method which takes all other info about a principle (like manual trades).
My problem is that I want to sort out only principle like only "Manual Trades".
I want to put a where clause. I tried but it is fails.
To get a single item use:
query.First(x => x.property == "Manual Trades");
// or
query.FirstOrDefault(x => x.property == "Manual Trades");
var list = p.GetInstanceForDatabase().where(x => x.propertyName == "Manual Trades").ToList();
I'm sure you're GetInstanceForDatabase needs to return your collection that you then filter for the 'Manual Trades' but I can't really tell how you get your list of PrincipalInstances from the question.
This is the correct syntax of using Where in LINQ
principlesList.Select(p => p.GetInstanceForDatabase()).Where(p => p.SomeProperty == "SomeValue").ToList();

Categories