sometimes, i just feel dumb...
i have a simple class:
public class myClass
{
public long Id { get; set; }
public long ParentChannelId { get; set; }
}
and i have a list that contains the class:
List<myClass> myItems = new List<myClass>
further down the code, i feed the list with classes.
now, i want to delete an item from the list.
but, since an item can have children and grandchilds etc...
i want to delete everything related..
was thinking of something like:
(pseudo code )
var List<myClass> itemsToDelete = myItems.Where(i => i.Ancestors.Contains(myItemId));
but i dont really have the brains atm to know how to write it exactly... :\
i do have the .Ancestors function...
just need help with the lambda linq
public List<Channel> Ancestors
{
get
{
List<MyCms.Content.Channels.Channel> result = new List<MyCms.Content.Channels.Channel>();
Channel channel = this;
while (channel != null)
{
result.Add(channel);
channel = myChannels.Where(c => c.ParentChannelId == this.Id).First();
}
result.Reverse();
return result;
}
}
EDIT: guess i did not explain myself as i should...
i have all the properties like ancestors, children parent etc...
i want to select all the classes that might contain the specific class...
I've re-read your question, especially the last part where you said you already have .Ancestors, and now it makes more sense.
Do this to get your list of items to delete:
List<MyClass> itemsToDelete = myItems
.Where(i => i.Id == myItemId)
.SelectMany(i => i.Ancestors)
.Concat(myItems) // Want to delete these too, not just the ancestors
.ToList()
;
Then you can foreach through the result, and remove them from the original list.
I'd suggest keeping these in a Dictionary<int, MyClass> or a HashSet<MyClass> instead of a list, since removal will be way faster.
For a HashSet, you'll have to implement Equals and GetHashCode, or create an IEqualityComparer<MyClass> implementation to provide those methods.
Before Edit:
I wouldn't write my code this way. I'd simply create a Dictionary<int, MyClass> instead of a list. It will do a lookup way faster than anything involving ancestors/tree traversal.
But here is how to accomplish what you're trying to accomplish:
If you're using Linq to Objects (as opposed to Linq to SQL or Linq to Entities), make a property called Parent on MyClass, of the correct type, instead of trying to link them by Id.
Then you can make an Ancestors property fairly easily:
public IEnumerable<MyClass> Ancestors
{
get
{
MyClass current = this;
while(current != null)
{
current = current.Parent;
yield return current;
}
}
}
If you can't edit the class, make an extension method called GetAncestors.
Then you can use something very similar to the code you wrote in your question:
List<MyClass> itemsToDelete = myItems
.Where(i => i.Ancestors.Any(a => a.Id == myItemId))
.ToList();
Linq to Entities
If you are using Linq to Entities, create a navigation property of the type MyClass to navigate to the parent, and do the same thing. Note that this might cause re-queries. Not sure the Linq can or would get translated into a hierarchical query.
This is how I would do it using a hashset and RemoveAll method.
var itemsToDelete = new HashSet<myClass>(otherItems);
myItems.RemoveAll(i => itemsToDelete.Contains(i));
RemoveAll Method
http://msdn.microsoft.com/en-us/library/wdka673a.aspx
Related
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
c#: how do I remove an Item inside IEnumerable
I have an Inumerable of Objects foo.
public IEnumerable<foo> listOfFoo{ get; set; }
Foo has Id and name lets say.
I want to pass an ID to a method and the method should remove the object with that ID from the IEnumerable and return it.
Whats the best way of doing it?
IEnumerables are read-only. You can't remove an object from one.
That said, you could do something like
public Foo QuoteRemoveUnquoteById(int id)
{
var rtnFoo = listOfFoo.SingleOrDefault(f => f.Id == id);
if (rtnFoo != default(Foo))
{
listOfFoo = listOfFoo.Where(f => f.Id != id);
}
return rtnFoo;
}
which just masks out the matching Foo? However, this will get less and less performant the more items you "remove". Also, anything else that holds a reference to listOfFoo won't see any change.
That's not possible for any collection that implements IEnumerable<foo>. If it is for example a List<foo>, then it's possible to remove items for it, but if it is for example a foo[] it's not possible to remove items.
If you use a List<foo> instead:
public foo Extract(int id) {
int index = listOfFoo.FindIndex(x => x.Id == id);
foo result = listOfFoo[index];
listOfFoo.removeAt(index);
return result;
}
You can't simply remove an item from an IEnumerable.
But you can either do the change in the underlying collection (e.g. if it is a List or something), or filter the IEnumerable using a Where clause.
If you need to remove items, you should use a collection that supports removing items, like List<>.
For example, you could have your backing field be of type List<foo>:
private List<foo> _listOfFoo;
public IEnumerable<foo> listOfFoo
{
get { return _listOfFoo.AsReadOnly(); }
set { _listOfFoo = value.ToList(); }
}
And then remove items from _listOfFoo.
_listOfFoo.Remove(_listOfFoo.Single(foo => foo.ID == id_to_remove));
IEnumarable is an interface for iterating thru a collection and there is no way to remove items without casting to some kind of collection.
read more here:
http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.aspx
In a C# class I have a list and two different getters for the list:
private List<A> a;
public List<A> EveryA
{
get
{
if (a == null) a = new List<A>();
return a;
}
}
public List<A> FilteredA
{
get
{
return EveryA.FindAll(a => a.IsInFilter);
}
}
Now my question is: how about the syntax FilteredA.Add(this);?
It compiles and runs but it cannot add any item to any list.
Should a better compiler have to notify the (small) problem?
They are not the same list. This is not something the compiler can check for you, since the compiler can't really read your mind. Check the documentation for List<T>.FindAll
The result is a list, but it isn't the same list (how could it be? your original list isn't filtered!).
You should be able to add items to the list returned by FilteredA, except they won't show up in a.
I suggest you use LINQs Where instead, returning an IEnumerable<T>. That way, it is obvious that the result of FilteredA shouldn't be changed, only iterated over:
public IEnumerable<A> FilteredA
{
get { return EveryA.Where(a => a.IsInFilter); }
}
No. Why should it notify you about this? It is completely ok.
FilteredA doesn't return a but a new instance of a List<A>.
FilteredA.Add(this); adds this to this new instance.
See this code:
var filteredA = FilteredA;
int count1 = filteredA.Count;
filteredA.Add(this);
int count2 = filteredA.Count;
Assert.AreEqual(count1 + 1, count2);
This shows, that the new item IS added to the list. But to that new instance that is independent of the list inside your class.
FindAll returns a new list. You're adding the new item to the new list but not retaining a reference to the new list, I suppose. The semantics would be clearer if the filtered list came from a method rather than a property.
public List<A> FilteredA returns some output of the FindAll method, as a List<A>. This will not be the same object as EveryA so when it goes out of scope your addition will be lost.
It's not really a compiler issue - since the code is valid it will compile just fine. The problem is more on a code quality level. To catch something like this, you could use a tool like FxCop to analyze your code.
Both methods can be seen as query methods. You should not expose the result as a List, but rather an IEnumerable or A[]. If you want to add an item to the list, do so with an Add method.
private List<A> items = new List<A>();
public IEnumerable<A> EveryA
{
get { return items; }
}
public IEnumerable<A> FilteredA
{
get { return items.Where(item => item.IsInFilter); }
}
public void AddItem(A item)
{
items.Add(item);
}
I have a list of objects and I'd like to update a particular member variable within one of the objects. I understand LINQ is designed for query and not meant to update lists of immutable data. What would be the best way to accomplish this? I do not need to use LINQ for the solution if it is not most efficient.
Would creating an Update extension method work? If so how would I go about doing that?
EXAMPLE:
(from trade in CrudeBalancedList
where trade.Date.Month == monthIndex
select trade).Update(
trade => trade.Buy += optionQty);
Although linq is not meant to update lists of immutable data, it is very handy for getting the items that you want to update. I think for you this would be:
(from trade in CrudeBalancedList
where trade.Date.Month == monthIndex
select trade).ToList().ForEach( trade => trade.Buy += optionQty);
I'm not sure if this is the best way, but will allow you to update an element from the list.
The test object:
public class SomeClass {
public int Value { get; set; }
public DateTime Date { get; set; }
}
The extension method:
public static class Extension {
public static void Update<T>(this T item, Action<T> updateAction) {
updateAction(item);
}
}
The test:
public void Test()
{
// test data
List<SomeClass> list = new List<SomeClass>()
{
new SomeClass {Value = 1, Date = DateTime.Now.AddDays(-1)},
new SomeClass {Value = 2, Date = DateTime.Now },
new SomeClass {Value = 3, Date = DateTime.Now.AddDays(1)}
};
// query and update
(from i in list where i.Date.Day.Equals(DateTime.Now.Day) select i).First().Update(v => v.Value += 5);
foreach (SomeClass s in list) {
Console.WriteLine(s.Value);
}
}
So you're expecting to get a single result here. In that case you might consider utilizing the SingleOrDefault method:
var record =
(from trade in CrudeBalancedList
where trade.Date.Month == monthIndex
select trade).SingleOrDefault();
if (record != null)
record.Buy += optionQty;
Note that the SingleOrDefault method expects there to be exactly one or zero value returned (much like a row in a table for some unique primary key). If more than one record is returned, the method will throw an exception.
To create such a method, you would start with its prototype:
public static class UpdateEx {
public void Update(this IEnumerable<T> items,
Expression<Action> updateAction) {
}
}
That's the easy part.
The hard part will be to compile the Expression<Action> into an SQL update statement. Depending on how much syntax you want to support, such a compiler's complexity can range from trivial to impossible.
For an example of compiling Linq Expressions, see the TableQuery class of the sqlite-net project.
I have asked this question about using the a Linq method that returns one object (First, Min, Max, etc) from of a generic collection.
I now want to be able to use linq's Except() method and I am not sure how to do it. Perhaps the answer is just in front on me but think I need help.
I have a generic method that fills in missing dates for a corresponding descriptive field. This method is declared as below:
public IEnumerable<T> FillInMissingDates<T>(IEnumerable<T> collection, string datePropertyName, string descriptionPropertyName)
{
Type type = typeof(T);
PropertyInfo dateProperty = type.GetProperty(datePropertyName);
PropertyInfo descriptionProperty = type.GetProperty(descriptionPropertyName);
...
}
What I want to accomplish is this. datePropertyName is the name of the date property I will use to fill in my date gaps (adding default object instances for the dates not already present in the collection). If I were dealing with a non-generic class, I would do something like this:
foreach (string description in descriptions)
{
var missingDates = allDates.Except(originalData.Where(d => d.Description == desc).Select(d => d.TransactionDate).ToList());
...
}
But how can I do the same using the generic method FillInMissingDates with the dateProperty and descriptionProperty properties resolved in runtime?
I think the best way would be to define an interface with all of the properties that you want to use in your method. Have the classes that the method may be used in implement this interface. Then, use a generic method and constrain the generic type to derive from the interface.
This example may not do exactly what you want -- it fills in missing dates for items in the list matching a description, but hopefully it will give you the basic idea.
public interface ITransactable
{
string Description { get; }
DateTime? TransactionDate { get; }
}
public class CompletedTransaction : ITransactable
{
...
}
// note conversion to extension method
public static void FillInMissingDates<T>( this IEnumerable<T> collection,
string match,
DateTime defaultDate )
where T : ITransactable
{
foreach (var trans in collection.Where( t => t.Description = match ))
{
if (!trans.TransactionDate.HasValue)
{
trans.TransactionDate = defaultDate;
}
}
}
You'll need to Cast your enumeration to ITransactable before invoking (at least until C# 4.0 comes out).
var list = new List<CompletedTransaction>();
list.Cast<ITransactable>()
.FillInMissingDates("description",DateTime.MinValue);
Alternatively, you could investigate using Dynamic LINQ from the VS2008 Samples collection. This would allow you to specify the name of a property if it's not consistent between classes. You'd probably still need to use reflection to set the property, however.
You could try this approach:
public IEnumerable<T> FillInMissingDates<T>(IEnumerable<T> collection,
Func<T, DateTime> dateProperty, Func<T, string> descriptionProperty, string desc)
{
return collection.Except(collection
.Where(d => descriptionProperty(d) == desc))
.Select(d => dateProperty(d));
}
This allows you to do things like:
someCollection.FillInMissingDates(o => o.CreatedDate, o => o.Description, "matching");
Note that you don't necessarily need the Except() call, and just have:
.. Where(d => descriptionProperty(d) != desc)
foreach (string description in descriptions)
{
var missingDates = allDates.Except<YourClass>(originalData.Where(d => d.Description == desc).Select(d => d.TransactionDate).ToList());
}
In fact, almost all LINQ extension in C# have a generic possible value. (Except and Except)
If you're going to identify the property to be accessed by a string name, then you don't need to use generics. Their only purpose is static type safety. Just use reflection to access the property, and make the method work on a non-generic IEnumerable.
Getting Except result with multiple properties working with custom data class is not allowed.
You have to use it like this: (given in msdn 101 LINQ Samples)
public void Linq53()
{
List<Product> products = GetProductList();
List<Customer> customers = GetCustomerList();
var productFirstChars =
from p in products
select p.ProductName[0];
var customerFirstChars =
from c in customers
select c.CompanyName[0];
var productOnlyFirstChars = productFirstChars.Except(customerFirstChars);
Console.WriteLine("First letters from Product names, but not from Customer names:");
foreach (var ch in productOnlyFirstChars)
{
Console.WriteLine(ch);
}
}
Having the key, you can handle your data accordingly :)
I have a List of a "complex" type - an object with a few string properties. The List itself is a property of another object and contains objects of a variety of types, as shown in this abbreviated class structure:
Customer {
public List<Characteristic> Characteristics;
.
.
.
}
Characteristic {
public string CharacteristicType;
public string CharacteristicValue;
}
I'd like to be able to collect a List of the values of a given type of Characteristics for the current Customer, which I can do in a 2-step process as follows:
List<Characteristic> interestCharacteristics = customer.Characteristics.FindAll(
delegate (Characteristic interest) {
return interest.CharacteristicType == "Interest";
}
);
List<string> interests = interestCharacteristics.ConvertAll<string>(
delegate (Characteristic interest) {
return interest.CharacteristicValue;
}
);
That works fine, but it seems like a long way around. I'm sure I must be missing a simpler way of getting to this list, either by chaining together the FindAll() and Convert() methods, or something else I'm overlooking entirely.
For background, I'm working in .Net 2.0, so I'm limited to the .Net 2 generics, and the Characteristic class is an external dependency - I can't change it's structure to simplify it, and there are other aspects of the class that are important, just not in relations to this problem.
Any pointers or additional reading welcomed.
Here's a generator implementation
public static IEnumerable<string> GetInterests(Customer customer)
{
foreach (Characteristic c in customer.Characteristics)
{
if (c.CharacteristicType == "Interest")
yield return c.CharacteristicValue;
}
}
sadly 3.5 extension methods and lambda are out based on your requirements but for reference here's how to do it:
customer.Characteristics
.Where(c => c.CharacteristicType == "Interest")
.Select(c => c. CharacteristicValue);
I would do some of the work manualy. By doing a FindAll first, and then a Convert, you're looping through your collection twice. It doesn't seem neccessary. If all you want at the end of the day, is a List of CharacteristicValue then just loop through your original collection, and add the CharacteristicValue to a List of each one that matches your criteria. Something like this:
Predicate<Characteristic> criteria = delegate (Characteristic interest)
{
return interest.CharacteristicType == "Interest";
};
List<string> myList = new List<string>();
foreach(Characteristic c in customer.Characteristics)
{
if(criteria(c))
{
myList.Add(c.CharacteristicValue);
}
}
Why not create a Dictionary<string, List<string>>, that way you can add "Interest" as the key, and a list of values as the value. For example:
Customer {
public Dictionary<string, List<string>> Characteristics;
.
.
.
}
...
Characteristics.Add("Interest", new List<string>());
Characteristics["Interest"].Add("Post questions on StackOverflow");
Characteristics["Interest"].Add("Answer questions on StackOverflow");
..
List<Characteristic> interestCharacteristics = Characteristics["Interest"];
Furthermore, if you wanted, you could limit your characteristics to a list of possible values by making it an enum, then use that as the data type of your dictionary's key:
public enum CharacteristicType
{
Interest,
Job,
ThingsYouHate
//...etc
}
then declare your dictionary as:
public Dictionary<CharacteristicType, List<string>> Characteristics;
..
Characteristics.Add(CharacteristicType.Interest, new List<string>());
Characteristics[CharacteristicType.Interest].Add("Post questions on StackOverflow");
Characteristics[CharacteristicType.Interest].Add("Answer questions on StackOverflow");