Equivalent to search within IEnumerable with LINQ - c#

Is there a shorter, elegant way to write the following using LINQ?
var actorName = string.Empty;
foreach (var myProperty in myProperties)
{
if (myProperty .PropertyName == "ActorName")
{
actorName = myProperty .PropertyValue;
break;
}
}

var actorName = myProperties
.FirstOrDefault(x => x.PropertyName == "ActorName")
.PropertyValue;
This would give a NPE if nothing could be found though (FirstOrDefault returns null as default).
To combat this, use two statements instead:
var actor = myProperties
.FirstOrDefault(x => x.PropertyName == "ActorName");
var actorName = actor == null ? string.Empty : actor.PropertyValue;

In addition to Jeroen's answer.. its safer to check for null first.. since FirstOrDefault returns null when there is nothing that matches:
var actor = myProperties
.FirstOrDefault(x => x.PropertyName == "ActorName");
if (actor != null)
actorName = actor.PropertyValue;

Pure LINQ version, for the hell of it, though I'd prefer Simon's answer.
var actorName = myProperties
.Where(x => x.PropertyName == "ActorName")
.Select(x => x.PropertyValue)
.Concat(new[]{ string.Empty })
.First();

Related

Why EF code is not selecting a single column?

I have used this to pick just a single column from the collection but it doesn't and throws casting error.
ClientsDAL ClientsDAL = new DAL.ClientsDAL();
var clientsCollection= ClientsDAL.GetClientsCollection();
var projectNum = clientsCollection.Where(p => p.ID == edit.Clients_ID).Select(p => p.ProjectNo).ToString();
Method:
public IEnumerable<Clients> GetClientsCollection(string name = "")
{
IEnumerable<Clients> ClientsCollection;
var query = uow.ClientsRepository.GetQueryable().AsQueryable();
if (!string.IsNullOrEmpty(name))
{
query = query.Where(x => x.Name.Contains(name));
}
ClientsCollection = (IEnumerable<Clients>)query;
return ClientsCollection;
}
As DevilSuichiro said in comments you should not cast to IEnumerable<T> just call .AsEnumerable() it will keep laziness.
But in your case it looks like you do not need that at all because First or FirstOrDefault work with IQueryable too.
To get a single field use this code
clientsCollection
.Where(p => p.ID == edit.Clients_ID)
.Select(p => p.ProjectNo)
.First() // if you sure that at least one item exists
Or (more safe)
var projectNum = clientsCollection
.Where(p => p.ID == edit.Clients_ID)
.Select(p => (int?)p.ProjectNo)
.FirstOrDefault();
if (projectNum != null)
{
// you find that number
}
else
{
// there is no item with such edit.Clients_ID
}
Or even simpler with null propagation
var projectNum = clientsCollection
.FirstOrDefault(p => p.ID == edit.Clients_ID)?.ProjectNo;

Lambda expression and class method

I have a problem like this.
List<Absent> absent = new List<Absent>();
Console.WriteLine("--------------------------");
Console.Write("Please enter a full name> ");
string temp_str = Console.ReadLine();
absent.Where(x => x.Name == temp_str).Run(x = x.WriteConsoleTable());
How can I run a method after doing a filtering?
Absent is a Class which has a Name variable and WriteConsoleTable method.
Seems like you're looking for the ForEach extension method but you'll first need to call ToList on the IEnumerable sequence returned from the Where clause.
absent.Where(x => x.Name == temp_str)
.ToList()
.ForEach(x => x.WriteConsoleTable());
or you can iterate over the collection using the foreach construct.
i.e.
foreach (var item in absent.Where(x => x.Name == temp_str))
item.WriteConsoleTable();
You can try below options.
var absences = absent.Where(x => x.Name == temp_str);
foreach(var abs in absences)
{
abs.WriteConsoleTable();
}
or if you are sure you only need first match
var absence = absent.FirstOrDefault(x => x.Name == temp_str);
if(absence != null)
{
absence.WriteConsoleTable();
}

Conditionally Insert Where Clause in LINQ Method Syntax with Select Many

Not sure if this possible using the LINQ method chain syntax or at all, but I would like to conditionally insert a where class in the chain if a parameter passed to the method is not null.
Here is the redundant code I would like to simplify:
public ICollection<Organization> getNetworkServiceRecipients(string serviceId = null)
{
ICollection<Organization> children = get_all_children();
if (serviceId != null)
{
return children.SelectMany(o => o.receives_these_services)
.Where(s => s.serviceId == serviceId)
.Select(o => o.serviceRecipient)
.Distinct()
.ToList();
}
else
{
return (children.SelectMany(o => o.receives_these_services)
.Select(o => o.serviceRecipient)
.Distinct()
.ToList());
}
}
I have been trying to insert the where clause programmatically based on whether serviceId is null or not. All of the answers I have found where based on the query syntax, but I couldn't translate. Any suggestions?
If you don't want to have it in the actual where query as dotnetom mentioned, you can do something like this:
public ICollection<Organization> getNetworkServiceRecipients(string serviceId = null)
{
var services = get_all_children().SelectMany(o => o.receives_these_services);
if (serviceId != null)
services = services.Where(s => s.serviceId == serviceId);
return services.Select(o => o.serviceRecipient)
.Distinct()
.ToList();
}
You can try this approach:
public ICollection<Organization> getNetworkServiceRecipients(string serviceId = null)
{
ICollection<Organization> children = get_all_children();
return children.SelectMany(o => o.receives_these_services)
.Where(s => serviceId == null || s.serviceId == serviceId)
.Select(o => o.serviceRecipient)
.Distinct()
.ToList();
}
In this case if your variable serviceId is null then only the first part of where condition would be executed, otherwise first part would be true and only second condition would matter.
Similar answer as dotnetom, but uses a ternary to determine which lambda to use
so that serviceId == null doesn't get executed on a per-item basis.
return children.SelectMany(o => o.receives_these_services)
.Where( serviceId == null ? (_ => true) : (s => s.serviceId == serviceId))
.Select(o => o.serviceRecipient)
.Distinct()
.ToList();

Linq result if null then zero

How do I write something like this:
int result = database
.Where(x => x.Name == "Criteria")
.Sum(x => x.Count)) ?? 0;
Where it will return the sum value unless linq does not find anything in which case it will return 0.
EDIT: The field is not null-able.
EDIT 2: I am using Entity Framework.
You were very close with your original query. You only needed to cast your Count variable:
int result = database
.Where(x => x.Name == "Criteria")
.Sum(x => (int?)x.Count) ?? 0;
Doing it this way would be a little more efficient and elegant than wrapping it in a Try/Catch.
I suspect you are using Entity Framework. If you were just using Linq-to-Objects, the solutions everybody else have provided would have worked.
This should work fine (no need for ?? 0):
var result = database
.Where(x => x.Name == "Criteria")
.Sum(x => x.Count))
Unless you want to check if x itself is null or not:
var result = database
.Where(x => x != null)
.Where(x => x.Name == "Criteria")
.Sum(x => x.Count))
You can just write:
int result = database
.Where(x => x.Name == "Criteria")
.Sum(x => x.Count));
The Enumerable.Sum method already returns zero on no results. From the documentation:
returns zero if source contains no elements.
This should work just fine:
var result = database.Where(x => x.Name == "Criteria").Sum(x => x.Count));
If no elements are returned by the Where function then the Sum function will return 0.
All of the Linq functions that return an IEnumerable<T> will return an empty collection instead of null.
Use the Aggregate extension method where 0 is a seed value
int sum = database.Where(x=>x.Name == "Criteria")
.Aggregate(0, (total, next) => total +=next);
I did it in a way that no one is going to like but garrantee to work 100% of the time, behold!
int result = 0;
try{
result = database
.Where(x => x.Name == "Criteria")
.Sum(x => x.Count));
} catch (Exception e){ }

C# Lambda returns some null values

opencall.Priority =
averages.Where(x => x.ProblemCode == opencall.ProblemCode)
.SingleOrDefault().Priority;
The above lambda statement returns some nulls because ProblemCode isn't always guaranteed to be in the averages list.
How can I rewrite this statement so that if that is the case opencall.Priority is set to "" instead of the application throwing an error?
You have to provide a new default value for your reference type, other than null.
opencall.Priority = averages.Where(x => x.ProblemCode == opencall.ProblemCode)
.Select(x => x.Priority)
.DefaultIfEmpty("")
.Single();
So Priority is a string? Note that you don't need SingleOrDefault anymore since the query can never throw an exception, because it will return your provided fallback value.
Split it up:
var result = averages.Where(x => x.ProblemCode == opencall.ProblemCode).SingleOrDefault()
opencall.Priority = result != null ? result.Priority : string.Empty;
Try getting the problem code first, then check if it's null.
var possiblyNullProblemCode=
averages.Where(
x => x.ProblemCode == opencall.ProblemCode)
.SingleOrDefault();
openCall.Priority =
possiblyNullProblemCode == null ?
string.Empty :
possiblyNullProblemCode.Priority;
Assuming Priority is string, you could try:
var average = averages.Where(x => x.ProblemCode == opencall.ProblemCode).SingleOrDefault()
opencall.Priority = average == null ? "" : average.Priority;
You can simply write:
opencall.Priority =
averages.Where(x => x.ProblemCode == opencall.ProblemCode)
.Select(x => x.Priority)
.SingleOrDefault() ?? string.Empty;

Categories

Resources