Method for removing items from List - c#

Related to this: Adding new items dynamically to IQueryable hard-coded fake repository
How could I build the method for the following class, which would remove items from the list based on the value of one of its fields?
public class FakeProductsRepository
{
private readonly List<Product> fakeProducts = new List<Product>
{
new Product { ProductID = "xxx", Description = "xxx", Price = 1000 },
new Product { ProductID = "yyy", Description = "xxx", Price = 2000 },
new Product { ProductID = "zzz", Description = "xxx", Price = 3000 },
};
public void AddProduct(string productID, string description, int price)
{
fakeProducts.Add(new Product
{
ProductID = productID,
Description = description,
Price = price,
});
}
public void RemoveProduct(string productID)
{
????????
//How to remove the item from the fakeProducts List where ProductID == productID?
}
public IQueryable<Product> Products
{
get { return fakeProducts.AsQueryable(); }
}
}
The problem method is pointed out with "???????" and the comment string.

In general, for a collection I'd use this code:
var productsToRemove = fakeProducts.Where(p => p.ProductID == productID).ToList();
foreach(var product in productsToRemove)
{
fakeProducts.Remove(product);
}
Don't forget the ToList(), or you can get an InvalidOperationException saying "Collection was modified".
Update (thanks to linuxuser27):
But List<T> also has a special method, taking Predicate<T>:
fakeProducts.RemoveAll(product => product.ProductID == productID);

Try using LINQ with the where clause. Note you need .NET 3.5.
var reducedProducts = from r in fakeProducts
where r.ProductID != productID
select r;
This will give you a reduced collection.
You could also use the RemoveAll() method, which takes a predicate but modifies the current collection.
fakeProducts.RemoveAll(delegate (Product r) { return r.ProductID != productID; });

If looking up items by product ID is a common operation, consider replacing the list with a dictionary keyed by the product ID.
If, however, you want to keep the List as the data structure, then just loop over it (this assumes that there's only one product with a given ProductID):
foreach(Product p in fakeProducts)
{
if(p.ProductId == productID)
{
fakeProducts.remove(p);
break;
}
}

Related

how can i delete the object kept in the list with a button

public interface IPerson
{
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
string Address { get; set; }
}
List<Customer> Customers = new List<Customer>();
private void button3_Click(object sender, EventArgs e)
{
int b = Convert.ToInt32(comboBox1.Text);
foreach (IPerson person in Customers)
if (person.Id == b)
Customers.Remove((Customer)person);
}
I want to delete the customer data kept in this way according to the id information selected from the combobox that appears in the visual. what should I do ?
List<T> has a RemoveAll method that accepts a predicate:
Customers.RemoveAll( c => b == c.Id );
First of all, you don't need to foreach an IPerson - you already know it's a customer - since you have a list of Customers, so why bother with the interface and why even specify it?
Second of all - you could remove all the items matching a pattern with RemoveAll ; code below is a bit verbose, but I will explain after.
var listOfCustomers = new List<Customer>();
listOfCustomers.Add(new Customer() { ID = 1 });
listOfCustomers.Add(new Customer() { ID = 2 });
listOfCustomers.RemoveAll(matchingCustomer => matchingCustomer.ID == 1);
Console.WriteLine(listOfCustomers.Count());
For this example I add 2 customers, Customer 1 and Customer 2
Normally I wouldn't use matchingCustomer as a term, but this is for the explanation; you tell the Lambda function: "I will use 'matchingCustomer' as the name you should be using while performing this action".
You could even chain this kind of functions, like so:
matchingCustomer.ID == id &&
matchingCustomer.Status = CustomerStatus.Active &&
matchingCustomer.SignOnDate < DateTime.Now.AddYears(-1)
So you say - the ID should be the id I got in the function, but it should also be active and at least signed on 1 year ago. (Where CustomerStatus is an enum)
You can do it using the following this way.
List<string> MyList = new List<string>();
MyList.Add("A");
MyList.Add("B");
MyList.Add("C");
MyList.Add("D");
MyList.Add("E");
MyList.ForEach(i => Console.Write("{0}\t", i));
//You should write the index of the element you want to delete here.
MyList.RemoveAt(1);
MyList.ForEach(i => Console.Write("{0}\t", i));

LINQ Where query with object list

I have a list of objects ListA with property Id and I have to make a query in a table that has a column Id and find the rows that the ids are the same. How exactly can I achieve that with a single query and not a foreach loop of listA?
Thank you for your time
foreach(var object in listA)
{
context.Table.Where(x => x.id == object.Id)....
}
Looks like you want to return all rows from the table that have an ID contained in the list of objects with the same ID. The following will achieve this. I can modify my answer to suit your need. Just let me know if you are looking for something slightly different.
void Main()
{
var listA = new List<A> { new A { Id = 1 }, new A { Id = 4 } };
var results = context.Table
.Where(t => listA.Select(l => l.Id).Contains(t.Id))
}
public class A
{
public int Id { get; set; }
}

How to get value from IEnumerable collection using its Key?

I have IEnumerable collection like following
IEnumerable<Customer> items = new Customer[]
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 989 }
};
I want to get value using key Id
I tried like following
public int GetValue(IEnumerable<T> items,string propertyName)
{
for (int i = 0; i < items.Count(); i++)
{
(typeof(T).GetType().GetProperty(propertyName).GetValue(typeof(T), null));
// I will pass propertyName as Id and want all Id propperty values
// from items collection one by one.
}
}
If you want to retrieve a Customer name from a collection by its Id:
public string GetCustomerName(IEnumerable<Customer> customers, int id)
{
return customers.First(c => c.Id == id).Name;
}
Using LINQ you can get all customers names (values) having specific value in this way:
var valuesList = items.Where(x => x.Something == myVar).Select(v => v.Name).ToList();
For single customer name you can do this:
var singleName = items.FirstOrDefault(x => x.Id == 1)?.Name;
Obviously, the Id can be 1, 2 or any other.
Edit:
I recommend you List<Customer> instead of Customer[]
So,
var items = new List<Customer>
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 989 }
};
// I will pass propertyName as Id and want all Id propperty values
// from items collection one by one.
If I understand you correctly
public static IEnumerable<object> GetValues<T>(IEnumerable<T> items, string propertyName)
{
Type type = typeof(T);
var prop = type.GetProperty(propertyName);
foreach (var item in items)
yield return prop.GetValue(item, null);
}
Just use LINQ to achieve what you want to do. if you want to retrieve a specific value you can use where like this:
public Customer GetCustomerById(IEnumerable<Customer> items,int key)
{
return items.Where(x=>x.id==key)
.Select(x =>x.Name)
.First();
}
this will retrieve the customer who match a specific Id.
Do you want to look things up repeatedly after creating the list? If so, you might want to consider creating a dictionary to do the lookups, like so:
IEnumerable<Customer> items = new Customer[]
{
new Customer {Name = "test1", Id = 999},
new Customer {Name = "test2", Id = 989}
};
var lookup = items.ToDictionary(itemKeySelector => itemKeySelector.Id);
var result = lookup[989];
Console.WriteLine(result.Name); // Prints "test2".
I'm assuming that you don't create the collection in the first place - if you had control over creating the original collection you could use a dictionary in the first place.
private TextBox [] Collectionstextboxonpanel(Panel panel)
{
var textBoxspanel1 = panel.Controls.OfType<TextBox>(); // select controls on panle1 by type
IEnumerable<TextBox> textBoxes = textBoxspanel1; // create collection if need
TextBox[] textBoxes1 = textBoxes.ToArray(); // Array collection
return textBoxes1; // get back TextBox Collection
}

Compare two lists of object for new, changed, updated on a specific property

I've been trying and failing for a while to find a solution to compare to lists of objects based on a property of the objects. I've read other similar solutions but they were either not suitable (or I didn't understand the answer!).
Code is C#
I have a model that represents an image
public class AccommodationImageModel
{
public int Id { get; set; }
public string Path { get; set; }
public string Caption { get; set; }
public string Description { get; set; }
public bool CoverImage { get; set; }
public bool Visible { get; set; }
}
I have two lists of this model. One is the existing list, another is an updated list. I need to compare the two lists to see which have been removed, updated or are new.
I don't need to compare the whole object, just compare them on their property Id.
List<AccommodationImageModel> masterList;
List<AccommodationImageModel> compareList;
New
If compareList contains any AccommodationImageModel with Id=0 then they are new because new entries do not have an Id assigned yet.
To be deleted
If masterList contains any AccommodationImageModel with Ids that are Not in compareList then they are to be deleted, because they have been removed from the compareList and should be removed from the masterList. Therefore I need a list of the ones that need to be deleted.
To be updated
If newList and masterList have Id's that are the same then they are to be updated. Therefore I need a list of the ones that share the same ID, so I can them update them. I'm not too concerned if these models are identical and don't need updating as there will only be a small number per list so it doesn't matter much if they get updated even if they haven't changed.
Each of the three results needs to be returned as a List of AccommodationImageModel so that I can then carry out the appropriate update, remove, add.
Edit
I've added 3 test methods below with my chosen solution from ATM, showing its working implementation.
Test methods
[TestMethod]
public void Test_Deleted_Image()
{
// set up the masterList
List<AccommodationImageModel> masterList = new List<AccommodationImageModel>();
masterList.Add(new AccommodationImageModel { Id = 1 });
masterList.Add(new AccommodationImageModel { Id = 2 });
// set up the compare list
List<AccommodationImageModel> compareList = new List<AccommodationImageModel>();
compareList.Add(new AccommodationImageModel { Id = 1 });
compareList.Add(new AccommodationImageModel { Id = 3 });
compareList.Add(new AccommodationImageModel { Id = 0 });
// get the deleted models
List<AccommodationImageModel> result = masterList.Where(c => !compareList.Any(d => d.Id == c.Id)).ToList();
// result should hold first model with id 2
Assert.AreEqual(2, result.FirstOrDefault().Id);
}
[TestMethod]
public void Test_Added_Image()
{
// set up the masterList
List<AccommodationImageModel> masterList = new List<AccommodationImageModel>();
masterList.Add(new AccommodationImageModel { Id = 1 });
masterList.Add(new AccommodationImageModel { Id = 2 });
// set up the compare list
List<AccommodationImageModel> compareList = new List<AccommodationImageModel>();
compareList.Add(new AccommodationImageModel { Id = 1 });
compareList.Add(new AccommodationImageModel { Id = 3 });
compareList.Add(new AccommodationImageModel { Id = 0 });
// get the added models
List<AccommodationImageModel> result = compareList.Where(c => c.Id == 0).ToList();
// result should hold first model with id 0
Assert.AreEqual(0, result.FirstOrDefault().Id);
}
[TestMethod]
public void Test_Updated_Image()
{
// set up the masterList
List<AccommodationImageModel> masterList = new List<AccommodationImageModel>();
masterList.Add(new AccommodationImageModel { Id = 1 });
masterList.Add(new AccommodationImageModel { Id = 2 });
// set up the compare list
List<AccommodationImageModel> compareList = new List<AccommodationImageModel>();
compareList.Add(new AccommodationImageModel { Id = 1 });
compareList.Add(new AccommodationImageModel { Id = 3 });
compareList.Add(new AccommodationImageModel { Id = 0 });
// get the updated models
List<AccommodationImageModel> result = masterList.Where(c => compareList.Any(d => c.Id == d.Id)).ToList();
// result should hold first model with id 1
Assert.AreEqual(1, result.FirstOrDefault().Id);
}
Simple Linq
New
List<AccommodationImageModel> toBeAdded = compareList.Where(c=>c.Id==0).ToList();
To be deleted
List<AccomodationImageModel> toBeDeleted = masterList.Where(c => !compareList.Any(d => c.Id == d.Id)).ToList();
To be updated
List<AccomodationImageModel> toBeUpdated = masterList.Where(c => compareList.Any(d => c.Id == d.Id)).ToList();
Assuming that two models with the same Id are considered the same model, you can write a IEqualityComparer like this:
public class AccommodationImageModelComparer : IEqualityComparer<AccommodationImageModel>
{
public bool Equals(AccommodationImageModel x, AccommodationImageModel y)
{
if(x == null && y == null)
return true;
return x.Id == y.Id;
}
public int GetHashCode(AccommodationImageModel model)
{
return model.Id.GetHashCode();
}
}
You can then use Linq to get the lists that you want:
var comparer = new AccommodationImageModelComparer();
var newItems = compareList.Where (l => l.Id == 0).ToList();
var toBeDeleted = masterList.Except(compareList, comparer).ToList();
var toBeUpdated = masterList.Intersect(compareList, comparer).ToList();
The first one just filters the items with an Id of 0, which are conisdered new. The second query returns the items in the masterList which are not in the compareList. The last query returns the items which are in both lists. This code compiles but is untested.
One simple approach would be to override the == operator in yourAccomodationImageModel as such:
public static override bool operator ==(AccommodationImageModel a, AccommodationImageModel b)
{
return a.Id == b.Id;
}
Then when comparing just check the master list against the comparer list and remove those in the master list that don't have an identical object in the comparer list:
List<AccomodationImageModel> rem = new List<AccomodationImageModel>;
List<AccomodationImageModel> newobj = new List<AccomodationImageModel>;
foreach(AccomodationImageModel a in compareList) {
if(a.Id == 0) { // id == 0 => new item
newobj.Add(a); // add new item later
} else {
// check those existing items as to whether they need to be updated or removed
bool match = false;
foreach(AccomodationImageModel b in masterList) {
if(a == b) match = true; // match found
}
if(!match) rem.Add(a); else Update(a); // will be removed or updated
}
}
// now remove unmatched items
foreach(AccomodationImageModel a in rem) { masterList.Remove(a); }
foreach(AccomodationImageModel a in newobj) { AddNew(a); }
Note Update(AccomodationImageModel a) is your method to update a certain item and AddNew(AccomodationImageModel a) is your method of insterting a new item in the master list.
Also as you may have noteiced removing from and inserting to the master list should be done after you have looped the master list!
///If image is not in list then add the image to the list
public void AddNew (List<AccomodationImageModel> masterList, AccomodationImageModel theImage)
{
masterList.Add(theImage);
}
/// If Image is in List then change listitem with new one
public void Update (List<AccomodationImageModel> masterList, int OldOnesID, AccomodationImageModel theNew)
{
masterList[OldOnesID] = theNew;
}
/// If Image should delete then removes the image from list
public void Delete (List<AccomodationImageModel> imgList, AccomodationImageModel theImageToDelete)
{
masterList.Remove(theImageToDelete);
}
/// this method checks the image state and do the work
public void CheckState (List<AccommodationImageModel> masterList, AccomodationImageModel theImage, bool deleteIt)
{
for(int i = 0; i < masterList.Count; i++)
{
if (deleteIt)
{
Delete(masterList, theImage);
}
else
{
if(theImage.ID == 0)
{
AddNew(masterList, theImage);
}
if(masterList[i].ID == theImage.ID)
{
Update(masterList, i, theImage);
}
}
}
If you prefer use 2 lists as a master and Temporary list, then you can simply iterate your temporary list and each of templist item use CheckState method..
Hope this helps..

Take the highest bid by this user

I'm building an auction site and the user can bid on the same item more than once (obviously). In the user's dashboard, a user can view his bids. When the user bids on the same item more than once, I want only one entry with the highest bid value to show up. My current code shows an entry for each bid. I tried a few things but I couldn't figure it out. Here's what I've got:
public class Bid
{
public int Id { get; set; }
public double Amount { get; set; }
public DateTime Date { get; set; }
public virtual Item Item { get; set; }
public virtual User User { get; set; }
}
protected override List<ItemForUserBids> ResolveCore(User source)
{
var items = new List<ItemForUserBids>();
var userBids = source.Bids;
foreach (var bid in userBids)
{
var item = bid.Item;
var c = new ItemForUserBids
{
BidValue = bid.Amount,
BidId = bid.Id,
Description = item.Description,
Id = item.Id,
ItemThumb = item.MainImageLink(),
Status = _itemsService.GetBiddingStatus(item, source),
TimeLeft = item.EndDate.TimeLeft(),
Title = item.Title
};
items.Add(c);
}
return items;
}
I tried to get Distinct bids based on the Item.Id but that did not work. Now I'm thinking maybe I could use the Date property of the Bid entity somehow to get to the result I want but my head stopped thinking.
Any suggestions?
UPDATE:
I got it to work using a dictionary and using OrderBy() and Max() like many suggested. But I think the latter could be further improved.
Implementation using a dictionary (works):
var userBids = new Dictionary<string, Bid>();
foreach (var bid in allUserBids)
{
var key = bid.Item.Id.ToString();
if(userBids.ContainsKey(key))
{
if (userBids[key].Amount < bid.Amount)
userBids[key] = bid;
}
userBids[key] = bid;
}
Attempt using the other method (works):
var highestBids =
source.Bids.Where(x => x.Date > DateTime.Now.AddYears(-1))
.GroupBy(x => x.Item.Id,
(itemId, bids) =>
new
{
ItemId = itemId,
MaxBid = bids.Max(x => x.Amount)
}).ToList();
var userBids = new List<Bid>();
foreach (var bid in source.Bids)
{
for(var i = 0; i < highestBids.Count; i++)
{
var curr = highestBids[i];
if (bid.Item.Id.Equals(curr.ItemId) && bid.Amount.Equals(curr.MaxBid)) {
userBids.Add(bid);
highestBids.Remove(curr);
}
}
}
How do I get rid of those loops? And maybe have it all in one chained statement?
The comments posted so far should be a good indication that you should look into re-architecting this a little, but the immediate code solution involves using System.Linq to chain together a GroupBy, Max, and a Select.
you could simply create a dictionary of user bids, where the key is the item id. Then for each user bid if an item id is not already used then add the current bid to the dictionary, if it is used then see if the bid amount of the item that exists in the dictionary already is lower than the current item then replace the existing item in the dictionary with the current one.
However this is very inefficient as really you only want to load the top 1 bid sorted descending by bid amount for each bid id, not load all the bids then work out the highest. What happens if your user has 10,000 old bids? Do they all get loaded?

Categories

Resources