I am tring to get the value "thisValueIwant". Is there any possibility to get this value so easy? Or Maybe there is another solution for these 2 ObservableCollection
public class Foo
{
public int number { get; set; }
public ObservableCollection<FooInFoo> Details { get; set; }
}
public class FooInFoo
{
public string thisValueIwant { get; set; }
}
public class TestClass
{
public void Test()
{
ObservableCollection<Foo> FooCollection = new ObservableCollection<Foo>();
FooCollection.Add(new Foo{
number =1,
Details = new ObservableCollection<FooInFoo>{ new FooInFoo {thisValueIwant = "very important string"}}
});
string x = (from f in FooCollection
where f.number == 1
select ???)
}
}
Since ObservableCollection<FooInFoo> Details is a collection, you have to decide which details you want: the first, last, any or all.
Assuming you want the first:
var d = FooCollection.Where(f => f.Number == 1).FirstOrDefault()?.Details.FirstOrDefault()?.thisValueIwant;
Or the last:
var d = FooCollection.Where(f => f.Number == 1).FirstOrDefault()?.Details.LastOrDefault()?.thisValueIwant;
Or all (materialized as an array):
var ds = FooCollection.Where(f => f.Number == 1).FirstOrDefault()?.Details.Select(d => d.thisValueIwant).ToArray();
An ObservableCollection<T> is a Collection<T> which implements IEnumerable<T>. Therefore the fact that your FooCollection is an observable collection is not important, you can regard it as a sequence of Foo, an IEnumerable<Foo> and equally an IEnumerable<FooInFoo>
Your code will be like (sorry, I only know how to write in Method format)
In baby steps:
IEnumerable<Foo> AllFooWithNumber1 = FooCollection
.Where(foo => foo.Number == 1);
If you are certain there is exactly one continue with:
Foo fooWithNumber1 = AllFooWithNumber1.Single();
Consider using SingleOrDefault if you are not certain that there is one.
Once you have the Foo that you want, you can select the Details:
IEnumerable<FooInFoo> detailsOfFooWithNumber1 = fooWithNumber1.Details;
FooInFoo detailIWant = detailsOfFooWithNumber1
.Where(detail => some expression that uses detail...)
.SingleOrDefault();
string thisValueIWant = defailtIWant.thisValueIWant;
Or in one statement:
string thisValueIWant = FooCollection
.Where(foo => foo.Number == 1)
.Single()
.Details
.Where(detail => ...)
.Single()
.thisValueIWant;
Problems might arise if you are not certain there is one Single element.
If you want to check foo.Number for a given value AND all details for some predicate, consider using Enumerable.SelectMany. This is used whenever you have sequences of sequences (arrays within arrays). With SelectMany you enumerate over all these inner arrays as if it was one array:
IEnumerable<string> valuesIWant = FooCollection
.Where(foo => foo.Number == 1)
.SelectMany(foo => foo.Details)
// now you have one sequence of all FooInFoo that are Details within
// Foo objects with Number 1
.Where(detail => expression that selects the FooInFoo you want)
.Select(detail => detail.thisValueIWant);
You need my ObservableComputations library maybe. Using this library you can code like this:
Expression<Func<string>> expr = () =>
FooCollection
.Filtering(а => f.number == 1)
.FirstComputing().Value
.Using(f => f != null
? f.Details.FirstComputing().Using(fif =>
fif.Value != null ? fif.Value.thisValueIwant : null).Value
: null).Value;
Computing<string> x = expr.Computing();
// x.Value is what you want
x is INotifyPropertyChanged and notifies you about changes of computing result of expr. Do not forget make all properties mentioned in the code above notify of changes through the INotifyPropertyChanged interface.
Related
I need to return one row of List from my function Selectus.
So I pass to the function Selectus object that reflects database table fields and I need to return one row which match the parameter looking_for:
public static List<T> Selectus<T>(string looking_for)
{
//all select data
var db = OrmLiteBaza().Open();//opening database
var select_all_list = db.Select<T>();//getting all data for <T> object works fine
db.Dispose();
//try to select one row - here I have trouble:
var prop = typeof(T).GetProperties();//properties of passed <T> object
var list_selected_record = from records in select_all_list where prop[1].Name == looking_for select records;//tryin to select one record from <T> object as in looking_for variable
return list_selected_record.ToList();//here one record should be returned
}
I do not know how to select one row from the list assuming that T parameter is vary. In SelectusT> method I want to pass as T different objects which reflect fields in database table rather than creatinig separate methods for each select. e.g. call Selectus, where object passed is public class ProductCodes { public int ID { get; set; } public string SapIndex { get; set; } public string SapName { get; set; } }. Then I want to call another Selectus<ProductTypes> for another table etc... So I want to write generic/overall method and use it universally for all types of my objects which reflects the fields of few database tables. The SapIndex property is always in the same place of all objects...
Using prop[1] is incredibly fragile. Who says that the property you're currently interested in is always going to be in second place? What if someone adds another property tomorrow? What if not every T that you use have the same property in the second place on its list of properties? It is quite unclear what your actual goal is here and why you've taken the reflection route.
You would be better off using inheritance or interface implementation here. I'm going to use an interface in this answer, but either would work.
For the sake of clarity, let's assume there is a Code field in all your possible lists, and this is the property you're trying to match with.
Define a reusable interface:
public interface ICodeEntity
{
string Code { get; }
}
Apply your interface to all of the classes that you intend to use for your Selectus method.
public class Person : ICodeEntity
{
public string Code { get; set; }
// And other properties
}
public class Document : ICodeEntity
{
public string Code { get; set; }
// And other properties
}
Add a generic type constraint that limits the use of T only to types that implement your interface.
public static List<T> Selectus<T>(string code)
where T : ICodeEntity
You can now write your code in a way that it relies on the type in question having a Code property, and the compiler will help enforce it.
var db = OrmLiteBaza().Open();
var list = db.Select<T>().ToList();
db.Dispose();
return list.Where(item => item.Code == code).ToList();
Usage examples:
List<Person> peopleWithCodeABC = Selectus<Person>("ABC");
List<Person> documentsWithCodeXYZ = Selectus<Document>("XYZ");
// This will fail if Animal does not implement ICodeEntity
var compilerError = Selectus<Animal>("ABC");
I might not understand fully what you want, but instead of string looking_for you could pass in a Func<,> delegate which acts as a selector.
Something like:
public static List<TField> Selectus<T, TField>(Func<T, TField> selector)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
var list_selected_record = select_all_list.Select(selector); // 'using System.Linq;'
return list_selected_record.ToList();
}
Then I believe it could be called like this:
var list_one = Selectus((ProductCodes x) => x.SapIndex);
var list_two = Selectus((ProductTypes x) => x.SapIndex);
var list_three = Selectus((ProductCodes x) => x.SapName);
With this syntax I leave out the <ProductCodes, string> generic arguments to the method since they can be inferred.
Hmm, maybe you want it in the opposite dimension. You could do:
public static List<T> Selectus<T>(Func<T, bool> predicate)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
var list_selected_record = select_all_list.Where(predicate); // 'using System.Linq;'
return list_selected_record.ToList();
}
with:
var list_one = Selectus((ProductCodes x) => x.SapIndex == "ABC");
var list_two = Selectus((ProductTypes x) => x.SapIndex == "ABC");
var list_three = Selectus((ProductCodes x) => x.SapName == "DaName");
or:
var list_one = Selectus<ProductCodes>(x => x.SapIndex == "ABC");
var list_two = Selectus<ProductTypes>(x => x.SapIndex == "ABC");
var list_three = Selectus<ProductCodes>(x => x.SapName == "DaName");
But if it is going to always be the "same" property, like always x.SapIndex (but for different types of x), then Flater's answer looks good.
Otherwise, if you insist, your reflection approach should be possible. Use propety's name, not its index! Let me try:
public static List<T> Selectus<T>(string looking_for)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
const string prop_name = "SapIndex";
var prop = typeof(T).GetProperty(prop_name); // can blow up for bad T
var list_selected_record = select_all_list
.Where(x => (string)(prop.GetValue(x)) == looking_for); // 'using System.Linq;'
return list_selected_record.ToList();
}
with:
var list_one = Selectus<ProductCodes>("ABC");
var list_two = Selectus<ProductTypes>("ABC");
you can change code to return just one element
public static T Selectus<T>(string looking_for)
{
//all select data
var db = OrmLiteBaza().Open();//opening database
var select_all_list = db.Select<T>();//getting all data for <T> object works fine
db.Dispose();
//try to select one row - here I have trouble:
var prop = typeof(T).GetProperties();//properties of passed <T> object
var list_selected_record = from records in select_all_list where prop[1].Name == looking_for select records;//tryin to select one record from <T> object as in looking_for variable
return list_selected_record.FirstOrDefault();//here one record should be returned
}
I'm having a hard time with this approach since I am new to Entity Framework. I actually don't know if there is something special related to EF or if the limitation is on me.
I would like to group some records from my database and after that, I'd like to iterate over the groups, then iterate over the elements on each group comparing it with all the other elements in the same group.
I have created two simple classes to illustrate the scenario:
public class MyContext : DbContext
{
public DbSet<MyClass> MyClass { get; set; }
}
And:
public class MyClass
{
public int Id { get; set; }
public int Value { get; set; }
}
What I have so far with a context injected is:
this.MyContext.MyClass
.GroupBy(x => x.Value)
.ToList() // need to materialize here
.ForEach(grp =>
{
// compare each item with all the other
// items in the current group
});
But I don't know how to iterate over the items and then compare with the others in the same group.
With the following code, the quesiton becomes what type is grp?
this.MyContext.MyClass
.GroupBy(x => x.Value)
.ToList() // need to materialize here
.ForEach(grp =>
{
// compare each item with all the other
// items in the current group
});
Well the grp variable is of type IGrouping<TKey, TElement>. That type derives from IEnumerable<TElement> so each grp is a list of TElement so you can foreach or do whatever you want to all the items in the grp.
DotNetFiddle Example.
Your variable grp is an IGrouping<int, MyClass>. You can treat it as an IEnumerable<MyClass>. For instance, you can get the item with the biggest Id like this:
this.MyContext.MyClass
.GroupBy(x => x.Value)
.ToList() // need to materialize here
.ForEach(grp =>
{
MyClass itemWithMaxId = grp.FirstOrDefault();
foreach (MyClass item in grp)
{
if (item.Id > itemWithMaxId.Id)
{
itemWithMaxId = item;
}
}
});
Note, however, that the ForEach method does not return anything, it only performs the specified action on each element of the list. If you want to get something, for instance the item with the biggest Id of each group, I suggest you to use the Select method provided by Linq, like in this example:
var itemsWithMaxIdByGroup = this.MyContext.MyClass
.GroupBy(x => x.Value)
.ToList() // need to materialize here
.Select(grp =>
{
MyClass itemWithMaxId = grp.First();
foreach (MyClass item in grp.Skip(1))
{
if (item.Id > itemWithMaxId.Id)
{
itemWithMaxId = item;
}
}
return itemWithMaxId;
});
I have three classes:
public partial class Objective{
public Objective() {
this.ObjectiveDetails = new List<ObjectiveDetail>();
}
public int ObjectiveId { get; set; }
public int Number { get; set; }
public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
}
public partial class ObjectiveDetail {
public ObjectiveDetail() {
this.SubTopics = new List<SubTopic>();
}
public int ObjectiveDetailId { get; set; }
public int Number { get; set; }
public string Text { get; set; }
public virtual ICollection<SubTopic> SubTopics { get; set; }
}
public partial class SubTopic {
public int SubTopicId { get; set; }
public string Name { get; set; }
}
I have two lists:
IList<ObjectiveDetail> oldObj;
IList<ObjectiveDetail> newObj;
The following LINQ gives me a new list of ObjectiveDetail objects where: the Number or the Text fields for any ObjectiveDetail object in the list differ between oldObj and newObj.
IList<ObjectiveDetail> upd = newObj
.Where(wb => oldObj
.Any(db => (db.ObjectiveDetailId == wb.ObjectiveDetailId) &&
(db.Number != wb.Number || !db.Text.Equals(wb.Text))))
.ToList();
How can I modify this so the LINQ gives me a new list of ObjectiveDetail objects where: the Number or the Text fields or the SubTopic collections for any ObjectiveDetail object in the list differ between oldObj and newObj.
In other words I want an ObjectiveDetail to be added to the upd list if:
It has Text in oldObj that's different from Text in newObj
It has a Number in oldObj that's different from the Number in newObj
It has a SubTopics collection with three elements in oldObj and 4 elements in newObj
It has a SubTopics collection with no elements in oldObj and 2 elements in newObj
It has a SubTopics collection with 2 elements in oldObj and no elements in newObj
It has a SubTopics collection with elements with SubTopicId of 1 and 2 in oldObj and 1 and 3 in newObj
I hope someone can come up with just some additional line in the LINQ statement that I already have.
Instead of creating a huge and hard maintanable LINQ query that will try to find differences, I would create a list of the same objects within both list (intersection) and as a result, take sum of both collection except this intersection. To compare objects you can use IEqualityComparer<> implementation. Here is a draft:
public class ObjectiveDetailEqualityComparer : IEqualityComparer<ObjectiveDetail>
{
public bool Equals(ObjectiveDetail x, ObjectiveDetail y)
{
// implemenation
}
public int GetHashCode(ObjectiveDetail obj)
{
// implementation
}
}
and then simply:
var comparer = new ObjectiveDetailEqualityComparer();
var common = oldObj.Intersect(newObj, comparer);
var differs = oldObj.Concat(newObj).Except(common, comparer);
This will be much easier to maintain when classes change (new properties etc.).
This should be what you need:
IList<ObjectiveDetail> upd = newObj.Where(wb =>
oldObj.Any(db =>
(db.ObjectiveDetailId == wb.ObjectiveDetailId) &&
(db.Number != wb.Number || !db.Text.Equals(wb.Text)
|| db.SubTopics.Count != wb.SubTopics.Count
|| !db.SubTopics.All(ds => wb.SubTopics.Any(ws =>
ws.SubTopicId == ds.SubTopicId))
))).ToList();
How It Works
db.SubTopics.Count != wb.SubTopics.Count confirms that the new object being compared (wb) and the old object being compared (db) have the same number of SubTopics. That part is pretty straightforward.
!db.SubTopics.All(ds => wb.SubTopics.Any(ws => ws.SubTopicId == ds.SubTopicId)) is a bit more complicated. The All() method returns true if the given expression is true for all members of the set. The Any() method returns true if the given expression is true for any member of the set. Therefore the entire expression checks that for every SubTopic ds in the old object db there is a Subtopic ws with the same ID in the new object wb.
Basically, the second line ensures that every SubTopic present in the old object is also present in the new object. The first line ensures that the old & new objects have the same number of SubTopics; otherwise the second line would consider an old object with SubTopics 1 & 2 the same as a new object with SubTopics 1, 2, & 3.
Caveats
This addition will not check whether the SubTopics have the same Name; if you need to check that as well, change the ws.SubTopicId == ds.SubTopicId in the second line to ws.SubTopicId == ds.SubTopicId && ws.Name.Equals(ds.Name).
This addition will not work properly if an ObjectiveDetail can contain more than one SubTopic with the same SubTopicId (that is, if SubTopicIds are not unique). If that's the case, you need to replace the second line with !db.SubTopics.All(ds => db.SubTopics.Count(ds2 => ds2.SubTopicId == ds.SubTopicId) == wb.SubTopics.Count(ws => ws.SubTopicId == ds.SubTopicId)). That will check that each SubTopicId appears exactly as many times in the new object as it does in the old object.
This addition will not check whether the SubTopics in the new object & the old object are in the same order. For that you would need to replace the 2nd line with db.SubTopics.Where((ds, i) => ds.SubTopicId == wb.SubTopics[i].SubTopicId).Count != db.SubTopics.Count. Note that this version also handles non-unique SubTopicId values. It confirms that the number of SubTopics in the old object such that the SubTopic in the same position in the new object is the same equals the total number of SubTopics in the old object (that is, that for every SubTopic in the old object, the SubTopic in the same position in the new object is the same).
High Level Thoughts
Konrad Kokosa's answer is better from a maintainability perspective (I've already upvoted it). I would only use a big ugly LINQ statement like this if you don't expect to need to revisit the statement very often. If you think the way you decide whether two ObjectiveDetail objects are equal might change, or the method that uses this statement might need to be reworked, or the method is critical enough that someone new to the code looking at it for the first time needs to be able to understand it quickly, then don't use a big long blob of LINQ.
Normally I would go with #Konrad Kokosa way. But it looks like you need a fast solution.
I tried it with some data. It gives the expected result. I am sure that you can modify the code for desired results.
var updatedObjects = oldObj.Join(newObj,
x => x.ObjectiveDetailId,
y => y.ObjectiveDetailId,
(x, y) => new
{
UpdatedObject = y,
IsUpdated = !x.Text.Equals(y.Text) || x.Number != y.Number //put here some more conditions
})
.Where(x => x.IsUpdated)
.Select(x => x.UpdatedObject);
Problems
Your LINQ query was not that bad but some proplems needed to be solved:
Using .Any() in a .Where() means that the query is much slower than needed. This is because for every item in objNew, you iterate the items of objOld.
!db.Text.Equals(wb.Text) throws an exception when db.Text is null.
Your code doesn't detect new items added to objNew that doesn't exists in objOld. I don't know if that is a problem because you didn't told us if that is possible.
Solution
If you compare collections, it would be a good idea to override the Equals() and GetHashcode() methods:
public partial class ObjectiveDetail
{
public ObjectiveDetail()
{
this.SubTopics = new List<SubTopic>();
}
public int ObjectiveDetailId { get; set; }
public int Number { get; set; }
public string Text { get; set; }
public virtual ICollection<SubTopic> SubTopics { get; set; }
public override bool Equals(Object obj)
{
var typedObj = obj as ObjectiveDetail;
return Equals(typedObj);
}
public bool Equals(ObjectiveDetail obj)
{
if ((object)obj == null) return false;
return ObjectiveDetailId == obj.ObjectiveDetailId &&
Number == obj.Number &&
Text == obj.Text &&
SubTopics != null && obj.SubTopics != null && // Just in the unlikely case the list is set to null
SubTopics.Count == obj.SubTopics.Count;
}
public override int GetHashCode()
{
return new { A = ObjectiveDetailId, B = Number, C = Text }.GetHashCode();
}
}
Then it is easy:
var dictionary = oldObj.ToDictionary(o => o.ObjectiveDetailId);
IList<ObjectiveDetail> upd = newObj
.Where(n => !EqualsOld(n, dictionary))
.ToList();
using this method:
private bool EqualsOld(ObjectiveDetail newItem, Dictionary<int, ObjectiveDetail> dictionary)
{
ObjectiveDetail oldItem;
var found = dictionary.TryGetValue(newItem.ObjectiveDetailId, out oldItem);
if (!found) return false; // This item was added to the new list
return oldItem.Equals(newItem);
}
If I get it right, you want to make a deep comparison between two .NET objects, regardless of LINQ. Why don't you use something like comparenetobjects?
Trying to implement a deep comparison through LINQ would probably be slower and more complex than making the comparison in the memory. Even if you chose to do it in LINQ realm, you would finally retrieve the whole object and perhaps you would do it with more than one queries, adding performance overhead. Therefore, I would suggest to eagerly load your data object from database and make the deep comparison without a specific linq query.
Hope I helped!
Find the entities that are not updated then exclude:
IEnumerable<ObjectiveDetail> newOds = ...;
IEnumerable<ObjectiveDetail> oldOds = ...;
// build collection of exclusions
// start with ObjectiveDetail entities that have the same properties
var propertiesMatched = oldOds.Join( newOds,
o => new { o.ObjectiveDetailId, o.Number, o.Text },
n => new { n.ObjectiveDetailId, n.Number, n.Text },
( o, n ) => new { Old = o, New = n } );
// take entities that matched properties and test for same collection
// of SubTopic entities
var subTopicsMatched = propertiesMatched.Where( g =>
// first check SubTopic count
g.Old.SubTopics.Count == g.New.SubTopics.Count &&
// match
g.New.SubTopics.Select( nst => nst.SubTopicId )
.Intersect( g.Old.SubTopics.Select( ost => ost.SubTopicId ) )
.Count() == g.Old.SubTopics.Count )
// select new ObjectiveDetail entities
.Select( g => g.New );
// updated ObjectiveDetail entities are those not found
// in subTopicsMatched
var upd = newOds.Except( subTopicsMatched );
This would work w/ EF and run completely server-side if newOds and oldOds are IQueryable<ObjectiveDetail>s from a DbContext
I have tried what you wanted but it is not too "neat" and it was not possible for me to make "one-liner-linq-expression" type code. Check it out and see if it is acceptable to you.
Also you need to check the performance but as you said there are not many objects so performance might not be of concern.
Also I have not tested it properly so if you wish to accept it then please do testing.
var oldObj = _objectiveDetailService.GetObjectiveDetails(id);
var newObj = objective.ObjectiveDetails.ToList();
var upd = newObj
.Where(wb => oldObj
.Any(db => (db.ObjectiveDetailId == wb.ObjectiveDetailId) &&
(db.Number != wb.Number || !db.Text.Equals(wb.Text))))
.ToList();
newObj.ForEach(wb =>
{
var comOld = oldObj.Where(db => wb.ObjectiveDetailId == db.ObjectiveDetailId &&
db.Number == wb.Number && db.Text.Equals(wb.Text)).FirstOrDefault();
if (comOld != null && wb.SubTopics.Any(wb2 => comOld.SubTopics.Where(oldST => wb2.SubTopicId == oldST.SubTopicId).Any(a => !a.Name.Equals(wb2.Name))))
{
upd.Add(wb);
}
});
You can write similar code to add and delete as well.
Hope this helps.
IList<ObjectiveDetail> upd = newObj
.Where(wb => oldObj
.Any(db => (db.ObjectiveDetailId == wb.ObjectiveDetailId) &&
(db.Number != wb.Number || !db.Text.Equals(wb.Text)))
||!oldObj.Any(o=>o.DetailId == wb.DetailId) //check if it's there or a new one
//check count
|| ((wb.SubTopics.Count!= oldObj.FirstOrDefault(o=>o.DetailId == wb.DetailId).SubTopics.Count
|| //check Ids match, or you can add more properties with OR
wb.SubTopics.Any(wbs=>oldObj.FirstOrDefault(o=>o.DetailId == wb.DetailId)
.SubTopics.Any(obs=>obs.SubTopicId !=wbs.SubTopicId))))
).ToList();
Have a look at below code. I created this function to compare two object then returns matched properties fields as an object.It may help full to you.
/// <summary>
/// Compare two objects, returns destination object with matched properties, values. simply Reflection to automatically copy and compare properties of two object
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
/// <returns>destination</returns>
public static object CompareNameAndSync(object source, object destination)
{
Type stype = source.GetType();
Type dtype = destination.GetType();
PropertyInfo[] spinfo = stype.GetProperties();
PropertyInfo[] dpinfo = dtype.GetProperties();
foreach (PropertyInfo des in dpinfo)
{
foreach (PropertyInfo sou in spinfo)
{
if (des.Name == sou.Name)
{
des.SetValue(destination, sou.GetValue(source));
}
}
}
return destination;
}
How would one implement LINQ to extract the Guid's from one collection of objects of type A such that they can exclude these Guids from another collection of objects of type B. Object A and Object B both have a Guid field called 'ID."
I have the following:
ObservableCollection<Component> component Component has a
field called ID of type Guid
ObservableCollection<ComponentInformation> ComponentInformationCollection ComponentInformation
has a field called ID of type Guid
My implementation:
component =>
{
if (component != null)
{
var cancelledComponents = new List<ComponentInformation>();
foreach (Component comp in component)
{
cancelledComponents.Add(new ComponentInformation() { ID = comp.ID });
}
this.ComponentInformationCollection.Remove(cancelledComponents);
}
});
I believe there is a more elegant solution which I've been working at to solve but the issue I keep running into is creating a 'new ComponentInformation' such that the types do not give me an error.
====== FINAL SOLUTION =======
var cancelledComponentIDs = new HashSet<Guid>(component.Select(x => x.ID));
this.ComponentInformationCollection.Remove(
this.ComponentInformationCollection.Where(x => cancelledComponentIDs.Contains(x.ID)).ToList());
Thank you to:
Jason - I used this as a template for my final solution (listed below).
Servy - While I could have used a comparer, I think for this particular scenario a comparer was not neccessary because of its one-time-use type of situation.
ComponentInformationCollection is a Silverlight DependencyProperty that will trigger a INotifyChangedEvent (MVVM pattern) when altered, so the solution above worked best for my situation.
I would do this:
var ids = new HashSet<Guid>(
component.Select(x => x.ID)
);
var keepers = ComponentInformationCollection.Where(x => !ids.Contains(x.ID));
If Component doesn't already define an Equals and GetHashCode that uses the ID to do the compare you can define a comparer such as this:
class ComponentComparer : IEqualityComparer<Component>
{
public int Compare(Component a, Component b)
{
return a.ID.CompareTo(b.ID);
}
public int GetHashCode(Component a)
{
return a.ID.GetHashCode();
}
}
Then you can just use:
var result = componentCollectionA.Except(componentCollectionB, new ComponentComparer());
(written off of the top of my head; may require minor modifications to get it to compile.)
LINQ will allow you to find the GUIDs you need, but LINQ sequences are generally immutable; you'll still need to use some kind of loop to actually change the collection. The trick is getting the correct instances of your original collection that you want to remove.
Implementing one of the equality/comparison interfaces is one way to go, and if you need to compare your objects for equality in multiple places, is definitely the way to go. If you don't want to do that, this should get you what you want:
var removeme = (from x in this.ComponentInformationCollection
join y in component on x.ID equals y.ID
select x).ToList();
removeme.ForEach(x => this.ComponentInformationCollection.Remove(x));
Thinking out loud (meaning I didn't create a project and types and compile this), but how about:
var cancelledComponents = component.Select(c=> new ComponentInformation() {ID = c.ID}).ToList();
cancelledComponents.ForEach(c => ComponentInformationCollection.Remove(c));
There are a number of ways to solve this... this is a pretty simple Linq statement to query the ones you are looking for from the collection.
var keep = typeAList.Where(a => typeBList.FirstOrDefault(b => a.ID == b.ID) == null);
Here is the little test app I put together to demo it.
class Program
{
static void Main(string[] args)
{
List<TypeA> typeAList = new List<TypeA>();
typeAList.Add(new TypeA() { ID = Guid.NewGuid() });
typeAList.Add(new TypeA() { ID = Guid.NewGuid() });
typeAList.Add(new TypeA() { ID = Guid.NewGuid() });
List<TypeB> typeBList = new List<TypeB>();
typeBList.Add(new TypeB() { ID = typeAList[0].ID });
typeBList.Add(new TypeB() { ID = typeAList[1].ID });
//this is the statement
var keep = typeAList.Where(a => typeBList.FirstOrDefault(b => a.ID == b.ID) == null);
}
}
class TypeA
{
public Guid ID { get; set; }
}
class TypeB
{
public Guid ID { get; set; }
}
I have an example class containing two data points:
public enum Sort { First, Second, Third, Fourth }
public class MyClass
{
public MyClass(Sort sort, string name) {
this.Sort = sort;
this.Name = name;
}
public Sort Sort { get; set; }
public string Name { get; set; }
}
I'm looking to sort them into groups by their Sort property, and then alphabetize those groups.
List<MyClass> list = new List<MyClass>() {
new MyClass(MyClass.Sort.Third, "B"),
new MyClass(MyClass.Sort.First, "D"),
new MyClass(MyClass.Sort.First, "A"),
new MyClass(MyClass.Sort.Fourth, "C"),
new MyClass(MyClass.Sort.First, "AB"),
new MyClass(MyClass.Sort.Second, "Z"),
};
The output would then be:
(showing Name)
A
AB
D
Z
B
C
I've been able to do this by using a foreach to separate the items into many smaller arrays (grouped by the enum value) but this seems very tedious - and I think there must be some LINQ solution that I don't know about.
Using extension methods, first OrderBy the enum, ThenBy name.
var sorted = list.OrderBy( m => m.Sort ).ThenBy( m => m.Name );
Aside from the nice LINQ solutions, you can also do this with a compare method like you mentioned. Make MyClass implement the IComparable interface, with a CompareTo method like:
public int CompareTo(object obj)
{
MyClass other = (MyClass)obj;
int sort = this.srt.CompareTo(other.srt);
return (sort == 0) ? this.Name.CompareTo(other.Name) : sort;
}
The above method will order your objects first by the enum, and if the enum values are equal, it compares the name. Then, just call list.Sort() and it will output the correct order.
This should do it, I think
var result = from m in list
orderby m.Sort, m.Name
select m;