Remove list items where property = myValue - c#

I've got a problem. I have a list of objects (vehicle).
List<Vehicle> vehicleList = Vehicle.GetVehiclesFromDatabase();
And now I want something like this:
vehicleList.Remove(where vehicleList.Brand == "Volkswagen");
I hope I could explain what my problem is.
Many thanks in advance!

You can use List<T>.RemoveAll:
int recordsRemoved = vehicleList.RemoveAll(v => v.Brand == "Volkswagen");
The method takes a Predicate<T> (= Func<T,bool>), which will remove all items for which the predicate returns true.
For you this is equal to the following method:
bool Filter(Vehicle vehicle)
{
return vehicle.Brand == "Volkswagen";
}

You can use linq to do this, like so
vehicleList = vehicleList.Where(v => v.Brand != "Volkswagen").ToList();
You can also do this with a RemoveAll
vehicleList.RemoveAll(v => v.Brand == "Volkswagen");

You can do like this
var itemToRemove = vehicleList.Single(r => r.Brand == "Volkswagen");
resultList.Remove(itemToRemove);
When you are not sure the item really exists you can use SingleOrDefault. SingleOrDefault will return null if there is no item (Single will throw an exception when it can't find the item). Both will throw when there is a duplicate value (two items with the same id).
var itemToRemove = vehicleList.SingleOrDefault(r => r.Brand == "Volkswagen");
if (itemToRemove != null)
resultList.Remove(itemToRemove);

Related

Linq value, use Where to Remove items

I am trying to remove items from an IQueryable list but the result is only pulling in those items:
public IQueryable<Biz.Data.AllItems> items_GetData()
{
var submissions = Biz.Data.AllItems.LoadNotDeleted().Where(x =>
// these items need to match to remove the item
x.itemOne != null &&
x.itemTwo != null &&
x.itemThree != null));
var filter = new Biz.Data.AllItemsFilter();
return submissions = Biz.Data.Registration.Load(filter).OrderBy(x => x.LastName).ThenBy(x => x.FirstName);
}
Currently, it's only pulling in items that match those instead of removing. I can't use RemoveAll because it's not a List and I don't want to reformat this because it passes through a filter process after this code. Is there another way to remove items that match these results first before it passes through a filter?
As discussed in comments, simply negate the condition in your predicate.
So if this is your original statement:
var itemsThatMatch = list.Where(x => /* some condition */);
This will give you the opposite:
var itemsThatDoNotMatch = list.Where(x => !(/* some condition */));

Return a value or null using LINQ in one line?

I have statements like this all over the place. Is there some way to do it all in one line ? Maybe something in C# 6.0 would help ? I just want to get the value if there is something there and if not then return a empty string or maybe NULL.
var item = ll.Fields.Where(x => x.FieldTitle == "Loan Amount");
if (item.Count() != 0) {
LatestItem.VelocifyLoanAmount = double.Parse(item.FirstOrDefault().Value);
}
EDIT 1: It is a double though other times I need to get a string. I love how quickly I get help for my LINQ questions. My Excel VSTO addin questions get crickets :)
Why not simply:
var item = ll.Fields
.Where(x => x.FieldTitle == "Loan Amount")
.Select(x => (double?)double.Parse(x.Value))
.FirstOrDefault();
One thing you can do is the following:
var item = ll.Fields.FirstOrDefault(x => x.FieldTitle == "Loan Amount")?.Value;
if (!double.TryParse(item, out LatestItem.VelocifyLoanAmount)
{
// do your error case here
}
UPDATE: added C#6.0 syntax to shorten the syntax, and using the preferred TryParse instead for error checking.
UPDATE 2: if for some reason double.Parse is preferred (for instance if a wrapper is handling exceptions for a large piece of code), here is a shorter example.
LatestItem.VelocifyLoanAmount =
double.Parse(ll.Fields.FirstOrDefault(x => x.FieldTitle == "Loan Amount")?.Value);
You can use Select to project the objects in the collection to double? and return FirstOrDefault
double? parsedFirstValueOrNull = ll.Fields
.Where(x => x.FieldTitle == "Loan Amount")
.Select(i => (double?)double.Parse(i.Value))
.FirstOrDefault();
The cast double? is necessary, otherwise FirstOrDefault will return 0.0 for empty collection, which might be ambiguous.
I have statements like this all over the place.
Since you are doing this multiple times, you may want to improve efficiency, rather than reducing the line count. One way to approach this would be extracting all fields into a Dictionary at once, and then quickly taking the items as needed:
var fieldNames = new Set<string> {
"Loan Amount", "Loan Balance", "Amount Due"
};
var dict = ll
.Fields
.Where(f => fieldNames.Contains(f.FieldTitle))
.ToDictionary(f => f.FieldTitle, f => double.Parse(f.Value));
dict.TryGetValue("Loan Amount", out LatestItem.VelocifyLoanAmount);
dict.TryGetValue("Loan Balance", out LatestItem.VelocifyLoanBalance);
dict.TryGetValue("Amount Due", out LatestItem.VelocifyAmountDue);
You can do something like:
var item = ll.Fields.Where(x = > x.FieldTitle == "Loan Amount").FirstOrDefault();
return (item != null) ? item.Value : DefaultReturnValue;
The return uses a tertiary operator. The first value is a boolean check. If the boolean is true then the value after the "?" is returned. Else, the value after the ":" is returned;

How do I Take everything in a collection using Linq?

I've got a method which can accept an optional int? value as a number of items to Take from a collection. I want to return all items if a null value is passed. Right now I have to duplicate my query to accomplish this
if(take == null)
{
x = db.WalkingDeadEps.Where(x => x.BicyclesCouldHaveSavedLives == true).ToList()
}
else
{
x = db.WalkingDeadEps.Where(x => x.BicyclesCouldHaveSavedLives == true).Take(take).ToList()
}
Is there a simpler way? Something like this?
.Take(take != null ? take : "all")
with Linq you have the option to store your query in variables. it will not be executed until you call ToList or equivalent methods on it.
var query = db.WalkingDeadEps.Where(x => x.BicyclesCouldHaveSavedLives == true);
x = take.HasValue ? query.Take(take.Value).ToList() : query.ToList();

FirstOrDefault returns NullReferenceException if no match is found

Here is my code:
string displayName = Dictionary.FirstOrDefault(x => x.Value.ID == long.Parse(options.ID)).Value.DisplayName;
The code works fine if x.Value.ID matches options.ID. However, I get a NullReferenceException if it doesn't.
FirstOrDefault returns the default value of a type if no item matches the predicate. For reference types that is null. Thats the reason for the exception.
So you just have to check for null first:
string displayName = null;
var keyValue = Dictionary
.FirstOrDefault(x => x.Value.ID == long.Parse(options.ID));
if(keyValue != null)
{
displayName = keyValue.Value.DisplayName;
}
But what is the key of the dictionary if you are searching in the values? A Dictionary<tKey,TValue> is used to find a value by the key. Maybe you should refactor it.
Another option is to provide a default value with DefaultIfEmpty:
string displayName = Dictionary
.Where(kv => kv.Value.ID == long.Parse(options.ID))
.Select(kv => kv.Value.DisplayName) // not a problem even if no item matches
.DefaultIfEmpty("--Option unknown--") // or no argument -> null
.First(); // cannot cause an exception
You can use a combination of other LINQ methods to handle not matching condition:
var res = dictionary.Where(x => x.Value.ID == someID)
.Select(x => x.Value.DisplayName)
.DefaultIfEmpty("Unknown")
.First();
Simply use the question mark trick for null checks:
string displayName = Dictionary.FirstOrDefault(x => x.Value.ID == long.Parse(options.ID))?.Value.DisplayName ?? "DEFINE A DEFAULT DISPLAY NAME HERE";
That is because FirstOrDefaultcan return null causing your following .Value to cause the exception. You need to change it to something like:
var myThing = things.FirstOrDefault(t => t.Id == idToFind);
if(myThing == null)
return; // we failed to find what we wanted
var displayName = myThing.DisplayName;
To add to the solutions, here is a LINQ statement that might help
Utilities.DIMENSION_MemTbl.Where(a => a.DIMENSION_ID == format.ContentBrief.DimensionID).Select(a=>a.DIMENSION1).DefaultIfEmpty("").FirstOrDefault();
The result will be an empty string if the result of the query is a null..
This answer is for those of us who need a visual write up (like me :)
In the code screenshot below, a NullReferenceException will be thrown, the root cause is the ReferenceIdentification_02 property.
When debugging, we see that the orderLine.REF array, I am querying does not include a matching object whose ReferenceIdentificationQualifier_01 value == "RU", so at that point FirstOrDefault() return value is NULL
to prevent the NullReferenceException, I do a FirstOrDefault() on the orderLine.REF array first. If the returned value is not null then I retrieve the value.
i assume you are working with nullable datatypes, you can do something like this:
var t = things.Where(x => x!=null && x.Value.ID == long.Parse(options.ID)).FirstOrDefault();
var res = t == null ? "" : t.Value;
you can use with 'Where' statement with FirstOrDefault().
like this.
var modelItem = _dbcontext.ModelName.Where(n => n.NewsTagId == newsTag.Id).FirstOrDefault();
It returns first item if does not match query.
It is better practice to check the NULL after query.
if(modelItem == null)
{
return "Not Found."
}
else
{
// continue process
}

How can I get multiple data?

I can show just one customer, I know the problem is because I use FirstOrDefault in my LINQ.
How can get another customer? I still don't understand the concept of IQueryable or IEnumerable.
public int getNota(DateTime dt, int lap)
{
DataClassesPelleDataContext myDb = new DataClassesPelleDataContext();
var nota = (from u in myDb.TBL_TRANSAKSI_SEWA_LAPANGAN_REGULERs
where u.TGL_PEMAKAIAN.Value.Date == dt.Date && u.ID_LAPANGAN == lap
select u.ID_SEWA).FirstOrDefault();
return nota;
}
I dont know for sure what you are trying to achive
But you can use
var notasIds = yDb.TBL_TRANSAKSI_SEWA_LAPANGAN_REGULERs
.Where(u => u.TGL_PEMAKAIAN.Value.Date == dt.Date && u.ID_LAPANGAN == lap)
.Select(n => n.ID_SEWA)
.ToList();
and then loop over the notas with
foreach (var sewaId in notasIds)
{
// to logic here
}
You can also comment .Select() call and get whole objects.
Regars

Categories