I have a list stored in resultlist as follows:
var resultlist = results.ToList();
It looks something like this:
ID FirstName LastName
-- --------- --------
1 Bill Smith
2 John Wilson
3 Doug Berg
How do I remove ID 2 from the list?
List<T> has two methods you can use.
RemoveAt(int index) can be used if you know the index of the item. For example:
resultlist.RemoveAt(1);
Or you can use Remove(T item):
var itemToRemove = resultlist.Single(r => r.Id == 2);
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 = resultlist.SingleOrDefault(r => r.Id == 2);
if (itemToRemove != null)
resultList.Remove(itemToRemove);
Short answer:
Remove (from list results)
results.RemoveAll(r => r.ID == 2); will remove the item with ID 2 in results (in place).
Filter (without removing from original list results):
var filtered = result.Where(f => f.ID != 2); returns all items except the one with ID 2
Detailed answer:
I think .RemoveAll() is very flexible, because you can have a list of item IDs which you want to remove - please regard the following example.
If you have:
class myClass {
public int ID; public string FirstName; public string LastName;
}
and assigned some values to results as follows (used for all examples below):
var results = new List<myClass> {
new myClass { ID=1, FirstName="Bill", LastName="Smith" }, // results[0]
new myClass { ID=2, FirstName="John", LastName="Wilson" }, // results[1]
new myClass { ID=3, FirstName="Doug", LastName="Berg" }, // results[2]
new myClass { ID=4, FirstName="Bill", LastName="Wilson" } // results[3]
};
Then you can define a list of IDs to remove:
var removeList = new List<int>() { 2, 3 };
And simply use this to remove them:
results.RemoveAll(r => removeList.Any(a => a==r.ID));
It will remove the items 2 and 3 and keep the items 1 and 4 - as specified by the removeList. Note that this happens in place, so there is no additional assigment required.
Of course, you can also use it on single items like:
results.RemoveAll(r => r.ID==4);
where it will remove Bill with ID 4 in our example.
A last thing to mention is that lists have an indexer, that is, they can also be accessed like a dynamic array, i.e. results[3] will give you the 4th element in the results list (because the first element has the index 0, the 2nd has index 1 etc).
So if you want to remove all entries where the first name is the same as in the 4th element of the results list, you can simply do it this way:
results.RemoveAll(r => results[3].FirstName == r.FirstName);
Note that afterwards, only John and Doug will remain in the list, Bill is removed (the first and last element in the example). Important is that the list will shrink automatically, so it has only 2 elements left - and hence the largest allowed index after executing RemoveAll in this example is 1 (which is results.Count() - 1).
Some Trivia:You can use this knowledge and create a local function
void myRemove() { var last = results.Count() - 1;
results.RemoveAll(r => results[last].FirstName == r.FirstName); }
What do you think will happen, if you call this function twice?
Like
myRemove(); myRemove();
Answer (click to show):
The first call will remove Bill at the first and last position, the second call will remove Doug and only John Wilson remains in the list.
Note: Since C# Version 8, you can as well write results[^1] instead of var last = results.Count() - 1; and results[last]:
void myRemove() => results.RemoveAll(r => results[^1].FirstName == r.FirstName);
So you would not need the local variable last anymore (see indices and ranges). Furthermore, since it is a one-liner, you don't require the curly braces and can use => instead.
For a list of all the new features in C#, look here.
DotNetFiddle: Run the demo
resultList = results.Where(x=>x.Id != 2).ToList();
There's a little Linq helper I like that's easy to implement and can make queries with "where not" conditions a little easier to read:
public static IEnumerable<T> ExceptWhere<T>(this IEnumerable<T> source, Predicate<T> predicate)
{
return source.Where(x=>!predicate(x));
}
//usage in above situation
resultList = results.ExceptWhere(x=>x.Id == 2).ToList();
You don't specify what kind of list, but the generic List can use either the RemoveAt(index) method, or the Remove(obj) method:
// Remove(obj)
var item = resultList.Single(x => x.Id == 2);
resultList.Remove(item);
// RemoveAt(index)
resultList.RemoveAt(1);
More simplified:
resultList.Remove(resultList.Single(x => x.Id == 2));
there is no needing to create a new var object.
There is another approach. It uses List.FindIndex and List.RemoveAt.
While I would probably use the solution presented by KeithS (just the simple Where/ToList) this approach differs in that it mutates the original list object. This can be a good (or a bad) "feature" depending upon expectations.
In any case, the FindIndex (coupled with a guard) ensures the RemoveAt will be correct if there are gaps in the IDs or the ordering is wrong, etc, and using RemoveAt (vs Remove) avoids a second O(n) search through the list.
Here is a LINQPad snippet:
var list = new List<int> { 1, 3, 2 };
var index = list.FindIndex(i => i == 2); // like Where/Single
if (index >= 0) { // ensure item found
list.RemoveAt(index);
}
list.Dump(); // results -> 1, 3
Happy coding.
Try this code:
resultlist.Remove(resultlist.Find(x => x.ID == 2));
... or just resultlist.RemoveAt(1) if you know exactly the index.
{
class Program
{
public static List<Product> list;
static void Main(string[] args)
{
list = new List<Product>() { new Product() { ProductId=1, Name="Nike 12N0",Brand="Nike",Price=12000,Quantity=50},
new Product() { ProductId =2, Name = "Puma 560K", Brand = "Puma", Price = 120000, Quantity = 55 },
new Product() { ProductId=3, Name="WoodLand V2",Brand="WoodLand",Price=21020,Quantity=25},
new Product() { ProductId=4, Name="Adidas S52",Brand="Adidas",Price=20000,Quantity=35},
new Product() { ProductId=5, Name="Rebook SPEED2O",Brand="Rebook",Price=1200,Quantity=15}};
Console.WriteLine("Enter ProductID to remove");
int uno = Convert.ToInt32(Console.ReadLine());
var itemToRemove = list.Find(r => r.ProductId == uno);
if (itemToRemove != null)
list.Remove(itemToRemove);
Console.WriteLine($"{itemToRemove.ProductId}{itemToRemove.Name}{itemToRemove.Brand}{itemToRemove.Price}{ itemToRemove.Quantity}");
Console.WriteLine("------------sucessfully Removed---------------");
var query2 = from x in list select x;
foreach (var item in query2)
{
/*Console.WriteLine(item.ProductId+" "+item.Name+" "+item.Brand+" "+item.Price+" "+item.Quantity );*/
Console.WriteLine($"{item.ProductId}{item.Name}{item.Brand}{item.Price}{ item.Quantity}");
}
}
}
}
Related
I have two lists, both containing models that share a common field, ID(String value). I am comparing the ID's for duplication.
I currently have a LINQ statement in place to determine the duplicated ID values, which stores them into a list of strings:
List<string> duplicateRecords = testData.TestRecords.GroupBy(aa => aa.ID).Where(x => x.Count() > 1).Select(y => y.Key).ToList();
And a second LINQ statement that maps a List of respected models based off the duplicated ID LINQ result:
List<Model> modelRecords = testData.Models.Where(x => duplicateRecords.Any(y => x.ID == y)).ToList();
These two LINQ statements do exactly what I expected them to do which is great. But now there is a recent request to determine duplicate ID's based off of their minimum N characters during a comparison. This minimum N comparison must happen for the last N characters in a string.
EX)
ID1: 123 == ID2: 123
ID1: 0123 == ID2: 123
ID1: 123 == ID2: 0123
ID1: 1230 != ID2: 123
ID1: 123 != ID2: 1230
ID1: 122110123 == ID2: 123
Hopefully those examples give some insight into the problem I am trying to solve. This could be done using foreach loops but I have come to experience the code becomes very messy and unmanageable on complex list query's.
So my question is this: How can I use the last N characters of the smaller of the two comparing strings to determine duplicates using LINQ?
Note: I am also very open to more elegant ways of solving this problem, would really appreciate excluding any for or foreach solutions.
I assume that when the input contains 123 and 0123 you want the result to have both of them
var input = new List<Model>()
{
new Model {ID = "123"},
new Model {ID = "0123"},
new Model {ID = "1230"},
new Model {ID = "12"},
new Model {ID = "122110123"}
};
var result = input.Where(x => input.Any(y => y != x && (y.ID.EndsWith(x.ID) || x.ID.EndsWith(y.ID)))).ToList();
\\this will return 123, 0123 and 122110123
If you want to check agains existing duplicateRecords list then this should work:
List<Model> modelRecords = testData.Models.Where(x => duplicateRecords.Any(y => x.ID.EndsWith(y) || y.EndsWith(x.ID))).ToList();
In order to efficiently find the duplicates you need to sort the IDs by length so you can minimize the comparisons necessary. (The sort adds some overhead, but greatly decreases the comparisons that must be done - in my test where 9 IDs have and 3 are duplicates of 8 values, it is 15 comparisons sorted versus 42 unsorted.) Once you have them sorted by length, just compare each one to the ones that are equal to or longer (in case of complete duplicates) to find which short IDs need to be kept, marking any matches so you can skip them and then find all the Models that end with the found matches.
Create the List of IDs ordered by their length:
var orderedIDs = testData.TestRecords.Select(tr => tr.ID).OrderBy(id => id.Length).ToList();
I don't think there is any way to do this efficiently with LINQ, but a nested for loop skipping previous matches optimizes the search for duplicates.
First, variables to keep track of IDsand whichID`s have already matched:
var dupRecordSubIDs = new List<string>();
var alreadyMatched = new bool[testData.TestRecords.Count];
Now loop through the IDs and save the shorter matching IDs:
// foreach ID in length order
for (int n1 = 0; n1 < testData.TestRecords.Count-1; ++n1) {
// skip the ones that already matched a shorter ID
if (!alreadyMatched[n1]) {
// remember if the shorter ID was alrady added
var added_n1 = false;
// compare the ID to all greater than or equal length IDs
for (int n2 = n1 + 1; n2 < testData.TestRecords.Count; ++n2) {
// if not previously matched, see if we have a new match
if (!alreadyMatched[n2] && orderedIDs[n2].EndsWith(orderedIDs[n1])) {
// only add the shorter ID once for new matches
if (!added_n1) {
dupRecordSubIDs.Add(orderedIDs[n1]);
added_n1 = true;
}
// remember which longer IDs are already matched
alreadyMatched[n2] = true;
}
}
}
}
Now find all the Models that match one of the IDs with a duplicate:
var modelRecords = testData.Models.Where(m => dupRecordSubIDs.Any(d => m.ID.EndsWith(d))).ToList();
I assume ID is string. If so, you can do this :
string match = "123";
var duplicate = list.Where(x=> x.Substring(x.Length - match.Length) == match).ToList();
If I understand your question correctly, it looks to be just a matter of chopping off the last N characters in each ID property while grouping.
Something like this:
using System;
using System.Linq;
public class TestRecord
{
public string ID { get; set; }
}
public class TestModel
{
public string ID { get; set; }
}
public class Program
{
public static void Main()
{
var N = 3; // This is where you define the desired N length
var rand = new Random();
var testRecords = new TestRecord[]
{
new TestRecord {ID = "123"},
new TestRecord {ID = "0123"},
new TestRecord {ID = "1230"},
new TestRecord {ID = "122110123"},
};
var testModels = new TestModel[]
{
new TestModel {ID = "123"},
new TestModel {ID = "0123"},
new TestModel {ID = "1230"},
new TestModel {ID = "122110123"},
};
bool SortEm(string a, string b) => a.Length < b.Length ? b.EndsWith(a) : a.EndsWith(b);
var models = testRecords
.Where(record => testRecords.Any(target => record.ID != target.ID && SortEm(target.ID, record.ID)))
.ToDictionary(
key => key,
key => testModels.Where(testModel => SortEm(key.ID, testModel.ID)).ToArray());
foreach (var kvp in models)
{
System.Console.WriteLine($"For duplicate key ({kvp.Key.ID}) found models: \r\n\t{string.Join("\r\n\t", kvp.Value.Select(x => x.ID))}");
}
}
}
In my c# MVC project I have a list of items in that I want to sort in order of another list
var FruitTypes = new List<Fruit> {
new Fruit { Id = 1, Name = "Banana"},
new Fruit { Id = 2, Name = "Apple" },
new Fruit { Id = 3, Name = "Orange" },
new Fruit { Id = 4, Name = "Plum"},
new Fruit { Id = 5, Name = "Pear" },
};
SortValues = new List<int> {5,4,3,1,2};
Currently my list is showing as default of fruit type.
How can I sort the Fruit list by SortValues?
It's unclear if you are sorting by the indexes in SortValues or whether SortValues contains corresponding Id values that should be joined.
In the first case:
First you have to Zip your two lists together, then you can sort the composite type that Zip generates, then select the FruitType back out.
IEnumerable<FruitType> sortedFruitTypes = FruitTypes
.Zip(SortValues, (ft, idx) => new {ft, idx})
.OrderBy(x => x.idx)
.Select(x => x.ft);
However, this is simply sorting the first list by the ordering indicated in SortValues, not joining the ids.
In the second case, a simple join will suffice:
IEnumerable<FruitType> sortedFruitTypes = SortValues
.Join(FruitTypes, sv => sv, ft => ft.Id, (_, ft) => ft);
This works because Enumerable.Join maintains the order of the "left" hand side of the join.
While there is almost certainly a more LINQ-y way, if you tend towards verbosity, you could accomplish this with an iterator function. For example:
public IEnumerable<Fruit> SortFruits(IEnumerable<Fruit> unordered, IEnumerable<int> sortValues)
{
foreach (var value in sortValues)
yield return unordered.Single(f => f.Id == value);
}
I like that it's explicit about what it's doing. You may consider throwing an exception when the number of items in each list is different, or maybe you just don't return an item if there is no sort value for it. You'll have to decide what the behaviour should be for "missing" values in either collection is. I think that having to handle these scenarios is a good reason to put it all in a single method this way, instead of a longer LINQ query.
Time complexity:O(n) + TM of Linq.
Declare list of fruits to store result.
Iterate through each fruit type.
Use Linq FirstOrDefault to get element by sorted value.
List<int> SortValues = new List<int> { 5, 4, 3, 1, 2 };
List<Fruit> result = new List<Fruit>();
foreach (var element in SortValues)
{
Fruit f = FruitTypes.FirstOrDefault(fruitElement => fruitElement.Id == element);
result.Add(f);
}
Implementation: DotNetFiddler
I am working on a collection. I need to remove one item from a collection and use the filtered/removed collection.
Here is my code
public class Emp{
public int Id{get;set;}
public string Name{get;set;}
}
List<Emp> empList=new List<Emp>();
Emp emp1=new Emp{Id=1, Name="Murali";}
Emp emp2=new Emp{Id=2, Name="Jon";}
empList.Add(emp1);
empList.Add(emp2);
//Now i want to remove emp2 from collection and bind it to grid.
var item=empList.Find(l => l.Id== 2);
empList.Remove(item);
The issue is even after removing the item my collection still shows count 2.
What could be the issue?
EDIT:
Original Code
var Subset = otherEmpList.FindAll(r => r.Name=="Murali");
if (Subset != null && Subset.Count > 0)
{
foreach (Empl remidateItem in Subset )
{
Emp removeItem = orginalEmpList.Find(l => l.Id==
remidateItem.Id);
if (removeItem != null)
{
orginalEmpList.Remove(remidateItem); // issue here
}
}
}
It is working fine. In actual code i was removing remediateItem. remediateItem was
same type but it belongs to different collection.
You are passing the objects to Remove which are not in your list you are trying to remove but copy of your object in other the list, that is why they are not being deleted, Use List.RemoveAll method to pass the predicate.
lst.RemoveAll(l => l.Id== 2);
If you want to remove many ids in some other collections like array of ids
int []ids = new int[3] {1,3,7};
lst.RemoveAll(l => ids.Contains(l.Id))
int removeIndex = list.FindIndex(l => e.Id== 2);
if( removeIndex != -1 )
{
list.RemoveAt(removeIndex);
}
Try this may be work for you
This original code you have pasted, works perfectly. its removing the items accordingly.
List<Emp> empList = new List<Emp>();
Emp emp1 = new Emp { Id = 1, Name = "Murali" };
Emp emp2 = new Emp { Id = 2, Name = "Jon" };
empList.Add(emp1);
empList.Add(emp2);
//Now i want to remove emp2 from collection and bind it to grid.
var item = empList.Find(l => l.Id == 2);
empList.Remove(item);
You wrote your lambda wrong. It should be this way
var item=empList.Find(l => l.Id== 2);
You need to add this blow the menthod Remove():
orginalEmpList.SaveChanges();
I am trying to create 3 different lists (1,2,3) from 2 existing lists (A,B).
The 3 lists need to identify the following relationships.
List 1 - the items that are in list A and not in list B
List 2 - the items that are in list B and not in list A
List 3 - the items that are in both lists.
I then want to join all the lists together into one list.
My problem is that I want to identify the differences by adding an enum identifying the relationship to the items of each list. But by adding the Enum the Except Linq function does not identify the fact (obviously) that the lists are the same. Because the Linq queries are differed I can not resolve this by changing the order of my statements ie. identify the the lists and then add the Enums.
This is the code that I have got to (Doesn't work properly)
There might be a better approach.
List<ManufactorListItem> manufactorItemList =
manufactorRepository.GetManufactorList();
// Get the Manufactors from the Families repository
List<ManufactorListItem> familyManufactorList =
this.familyRepository.GetManufactorList(familyGuid);
// Identify Manufactors that are only found in the Manufactor Repository
List<ManufactorListItem> inManufactorsOnly =
manufactorItemList.Except(familyManufactorList).ToList();
// Mark them as (Parent Only)
foreach (ManufactorListItem manOnly in inManufactorsOnly) {
manOnly.InheritanceState = EnumInheritanceState.InParent;
}
// Identify Manufactors that are only found in the Family Repository
List<ManufactorListItem> inFamiliesOnly =
familyManufactorList.Except(manufactorItemList).ToList();
// Mark them as (Child Only)
foreach (ManufactorListItem famOnly in inFamiliesOnly) {
famOnly.InheritanceState = EnumInheritanceState.InChild;
}
// Identify Manufactors that are found in both Repositories
List<ManufactorListItem> sameList =
manufactorItemList.Intersect(familyManufactorList).ToList();
// Mark them Accordingly
foreach (ManufactorListItem same in sameList) {
same.InheritanceState = EnumInheritanceState.InBoth;
}
// Create an output List
List<ManufactorListItem> manufactors = new List<ManufactorListItem>();
// Join all of the lists together.
manufactors = sameList.Union(inManufactorsOnly).
Union(inFamiliesOnly).ToList();
Any ideas hot to get around this?
Thanks in advance
You can make it much simplier:
List<ManufactorListItem> manufactorItemList = ...;
List<ManufactorListItem> familyManufactorList = ...;
var allItems = manufactorItemList.ToDictionary(i => i, i => InheritanceState.InParent);
foreach (var familyManufactor in familyManufactorList)
{
allItems[familyManufactor] = allItems.ContainsKey(familyManufactor) ?
InheritanceState.InBoth :
InheritanceState.InChild;
}
//that's all, now we can get any subset items:
var inFamiliesOnly = allItems.Where(p => p.Value == InheritanceState.InChild).Select(p => p.Key);
var inManufactorsOnly = allItems.Where(p => p.Value == InheritanceState.InParent).Select(p => p.Key);
var allManufactors = allItems.Keys;
This seems like the simplest way to me:
(I'm using the following Enum for simplicity:
public enum ContainedIn
{
AOnly,
BOnly,
Both
}
)
var la = new List<int> {1, 2, 3};
var lb = new List<int> {2, 3, 4};
var l1 = la.Except(lb)
.Select(i => new Tuple<int, ContainedIn>(i, ContainedIn.AOnly));
var l2 = lb.Except(la)
.Select(i => new Tuple<int, ContainedIn>(i, ContainedIn.BOnly));
var l3 = la.Intersect(lb)
.Select(i => new Tuple<int, ContainedIn>(i, ContainedIn.Both));
var combined = l1.Union(l2).Union(l3);
So long as you have access to the Tuple<T1, T2> class (I think it's a .NET 4 addition).
If the problem is with the Except() statement, then I suggest you use the 3 parameter override of Except in order to provide a custom IEqualityComparer<ManufactorListItem> compare which tests the appropriate ManufactorListItem fields, but not the InheritanceState.
e.g. your equality comparer might look like:
public class ManufactorComparer : IEqualityComparer<ManufactorListItem> {
public bool Equals(ManufactorListItem x, ManufactorListItem y) {
// you need to write a method here that tests all the fields except InheritanceState
}
public int GetHashCode(ManufactorListItem obj) {
// you need to write a simple hash code generator here using any/all the fields except InheritanceState
}
}
and then you would call this using code a bit like
// Identify Manufactors that are only found in the Manufactor Repository
List<ManufactorListItem> inManufactorsOnly =
manufactorItemList.Except(familyManufactorList, new ManufactorComparer()).ToList();
Using LINQ what is the best way to select a single item from a list if the item may not exists in the list?
I have come up with two solutions, neither of which I like. I use a where clause to select the list of items (which I know will only be one), I can then check the count and make a Single call on this list if count is one, the other choice is to use a foreach and just break after getting the item.
Neither of these seem like a good approach, is there a better way?
You can use IEnumerable.First() or IEnumerable.FirstOrDefault().
The difference is that First() will throw if no element is found (or if no element matches the conditions, if you use the conditions). FirstOrDefault() will return default(T) (null if it's a reference type).
Use the FirstOrDefault selector.
var list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var firstEven = list.FirstOrDefault(n => n % 2 == 0);
if (firstEven == 0)
Console.WriteLine("no even number");
else
Console.WriteLine("first even number is {0}", firstEven);
Just pass in a predicate to the First or FirstOrDefault method and it'll happily go round' the list and picks the first match for you.
If there isn't a match, FirstOrDefault will returns the default value of whatever datatype the list items is.
Hope this helps :-)
List<string> items = new List<string>();
items.Find(p => p == "blah");
or
items.Find(p => p.Contains("b"));
but this allows you to define what you are looking for via a match predicate...
I guess if you are talking linqToSql then:
example looking for Account...
DataContext dc = new DataContext();
Account item = dc.Accounts.FirstOrDefault(p => p.id == 5);
If you need to make sure that there is only 1 item (throws exception when more than 1)
DataContext dc = new DataContext();
Account item = dc.Accounts.SingleOrDefault(p => p.id == 5);
Just to complete the answer, If you are using the LINQ syntax, you can just wrap it since it returns an IEnumerable:
(from int x in intList
where x > 5
select x * 2).FirstOrDefault()
Maybe I'm missing something here, but usually calling .SingleOrDefault() is the way to go to return either the single element in the list, or a default value (null for reference or nullable types) if the list is empty.
It generates an exception if the list contains more than one element.
Use FirstOrDefault() to cover the case where you could have more than one.
There are two easy ways, depending on if you want to deal with exceptions or get a default value.
You can use the First<T>() or the FirstOrDefault<T>() extension method to get the first result or default(T).
var list = new List<int> { 1, 2, 4 };
var result = list.Where(i => i == 3).First(); // throws InvalidOperationException
var result = list.Where(i => i == 3).FirstOrDefault(); // = 0
SingleOrDefault() is what you need
cheers
just saw this now, if you are working with a list of object you can try this
public class user
{
public string username { get; set; }
public string password { get; set; }
}
List<user> userlist = new List<user>();
userlist.Add(new user { username = "macbruno", password = "1234" });
userlist.Add(new user { username = "james", password = "5678" });
string myusername = "james";
string mypassword = "23432";
user theUser = userlist.Find(
delegate (user thisuser)
{
return thisuser.username== myusername && thisuser.password == mypassword;
}
);
if (theUser != null)
{
Dosomething();
}
else
{
DoSomethingElse();
}