SQL Query -- Generate a 2 collections based on differences? - c#

I have 2 different lists:
public class Foo
{
public Int32 Id { get; set; }
}
ICollection<Foo> OriginalCollection
ICollection<Foo> NewCollection
I want to generate 2 lists to be able to foreach them...
All items that OriginalCollection contains that NewCollection does not (basically, a collection of stuff that was removed).
All items that OriginalCollection does not contain that NewCollection does (basically, a collection of stuff that was added).
I know I'll get back an IEnumerable with LINQ, that's fine since I need to foreach. I just have no idea what my queries should look like...
Update:
I forgot to mention that I did try the Except clause... it failed because the objects are not the same. They only contain the same Id.

So, you want the Complement of the Set.
This post seems to fit your needs:
Quickest way to find the complement of two collections in C#
Or perhaps the Enumerable.Except method:
http://msdn.microsoft.com/en-us/library/system.linq.enumerable.except.aspx

You are looking for the Except operator
http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#except1
NewCollection -except> OriginalCollection = deleted
OriginalCollection -except> NewCollection = added

List<Foo> original = ...
List<Foo> modified = ...
...
var deleted = original.Except(modified);
var added = modified.Except(original);
You could explicitly use LINQ but its not really worth it. One way could be:
var deleted = from i in original
where !modified.Contains(i)
select i;
var added = from i in modified
where !original.Contains(i)
select i;

using System.Linq;
originalC.Except(newC, comparer);
newC.Except(originalC, comparer);
Following edit:
You can use the overload that takes a "IEqualityComparer comparer".
You can define a small class that implements that interface with any kind of compare logic you wish.

Related

list property containing two types (string/int)

I need to have a property that will be an array that can hold both ints and strings.
if i set the property to an array of ints it should be ints so when I am searching through this array the search will be fast, and at odd times this property will also contain strings which the search will be slow.
Is there any other way other than the following to have a list that contain native types
two properties one for ints and one for strings
use List< object >
UPDATE:
The use-case is as follow. I have a database field [ReferenceNumber] that holds the values (integers and strings) and another field [SourceID] (used for other things) which can be used to determine if record holds an int or string.
I will be fetching collections of these records based on the source id, of course depending on what the source is, the list either will be integers or strings. Then I will go through this collection looking for certain reference numbers, if they exist not add them or they dont then add them. I will be pre-fetching a lot of records instead of hitting the database over and over.
so for example if i get a list for sourceid =1 that means they are ints and if searching i want the underline list to be int so the search will be fast. and if sourceid say is 2 which means they are strings and very rare its okay if the search is slow because those number of records are not that many and a performance hit on searching through strings is okay.
I will go through this collection looking for certain reference numbers, if they exist not add them or they dont then add them.
It sounds to me like you don't need a List<>, but rather a HashSet<>. Simply use a HashSet<object>, and Add() all the items, and the collection will ignore duplicate items. It will be super-fast, regardless of whether you're dealing with ints or strings.
On my computer, the following code shows that it takes about 50 milliseconds to populate an initial 400,000 unique strings in the hashset, and about 2 milliseconds to add an additional 10,000 random strings:
var sw = new Stopwatch();
var initial= Enumerable.Range(1, 400000).Select(i => i.ToString()).ToList();
sw.Start();
var set = new HashSet<object>(initial);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
var random = new Random();
var additional = Enumerable.Range(1, 10000).Select(i => random.Next(1000000).ToString()).ToList();
sw.Restart();
foreach (var item in additional)
{
set.Add(item);
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
Also, in case it's important, HashSet<>s do retain order of insertion.
The only other thing I would suggest is a custom object that implements IComparable
class Multitype: IComparable
{
public int? Number { get; set; }
public string Words {get; set; }
public int CompareTo(object obj)
{
Multitype other = obj as Multitype;
if (Number != null && other != null && other.Number != null)
{
//...
}
else
{
//...
}
}
}
There will be some extra comparison steps between numbers, but not as much as string parsing.
Are you storing a ton of data, is that performance difference really going to matter?
It's possible to use generics if you implement them on the class. Not sure if this solves your problem. Would be interested to hear the real-world example of a property that can have different types.
class Foo<T>
{
public List<T> GenericList { get; set; }
public Foo()
{
this.GenericList = new List<T>();
}
}
If by "use List" you mean the object primitive or provided System.Object, that is an option, but I think it would behoove you to make your own wrapper object -- IntString or similar -- that would handle everything for you. It should implement IComparable, as the other gal mentioned.
You can increase the efficiency of sorting your object in collections by writing a CompareTo method that does exactly what you need it to. Writing a good CompareTo method is a whole can of worms in itself, so you should probably start a new question for that, if that's what you want.
If you're looking for a property that is strongly typed as a List<Int> or List<String> at instantiation, but can change afterwards, then you want an interface. IList exists, but won't help you, since that must also be strongly typed upon declaration. You should probably make something like IIntStringList that can only be one of List<Int> or List<String>.
Sorry this answer doesn't have that many details (I need to leave the office now), but I hope I've set you on the right track.

Can I / How to Implement IEnumerable on third party object to Sort...just use LINQ?

My last two question have had down votes so my apologies in advance if this is a dumb question and a waste of your time.
I am wanting to sort a collection on a property called CreateDate so I thought I would do something like:
MyList.Sort(x => x.CreateDate)
However MyList.Sort() is not available since MyList is a Microsoft DataCollection object that does NOT implement IEnumerable.
The Microsoft DataCollection from the metadata is as such:
public class DataCollection<T> : Collection<T>
{
public void AddRange(IEnumerable<T> items);
public void AddRange(params T[] items);
public T[] ToArray();
}
So the only two options I can think of are:
Use Linq something along these lines:
var query = from xyz in self.Entities
orderby xyz.Attributes["CreateDate"]
select xyz;
or cast to a list but then I have to create a comparer and I'm not sure how to get it back without iterating over the whole thing and rebuilding the DataCollection.
List<Entity> x = self.Entities.ToList();
x.Sort(y => y.Attributes["CreateDate"], comparer)
foreach(var x in x)
{
//iterate and rebuild the DataCollection
}
Any input / critical evaluation would be appreciated
Thank You
I'm not quite sure there's enough information here to accurately diagnose the problem, but I'll give it a shot. I see two potential problems:
You're missing the reference to either System.Linq or System.Data.DataSetExtensions.
You need to cast the DataCollection to the correct type, using MyList.Cast<Entity>().OrderBy(x => x.CreateDate)
Also, note the use of .OrderBy() instead of .Sort()

How can I compare two C# collections and issue Add, Delete commands to make them equal?

I have two ICollection collections:
public partial class ObjectiveDetail
{
public int ObjectiveDetailId { get; set; }
public int Number { get; set; }
public string Text { get; set; }
}
var _objDetail1: // contains a list of ObjectiveDetails from my database.
var _objDetail2: // contains a list of ObjectiveDetails from web front end.
How can I iterate through these and issue and Add, Delete or Update to synchronize the database with the latest from the web front end?
If there is a record present in the first list but not the second then I would like to:
_uow.ObjectiveDetails.Delete(_objectiveDetail);
If there is a record present in the second list but not the first then I would like to:
_uow.ObjectiveDetails.Add(_objectiveDetail);
If there is a record (same ObjectiveDetailId) in the first and second then I need to see if they are the same and if not issue an:
_uow.ObjectiveDetails.Update(_objectiveDetail);
I was thinking to do this with some kind of:
foreach (var _objectiveDetail in _objectiveDetails) {}
but then I think I might need to have two of these and I am also wondering if there is a better way. Does anyone have any suggestions as to how I could do this?
The following code is one of some possible solutions
var toBeUpdated =
objectiveDetail1.Where(
a => objectiveDetail2.Any(
b => (b.ObjectiveDetailId == a.ObjectiveDetailId) &&
(b.Number != a.Number || !b.Text.Equals(a.Text))));
var toBeAdded =
objectiveDetail1.Where(a => objectiveDetail2.All(
b => b.ObjectiveDetailId != a.ObjectiveDetailId));
var toBeDeleted =
objectiveDetail2.Where(a => objectiveDetail1.All(
b => b.ObjectiveDetailId != a.ObjectiveDetailId));
The rest is a simple code to Add, Delete, Update the three collections to the database.
It's look like you just want the two lists to be a copy of one another, you can just implement a Copy method and replace the outdated collection, if you implement ICollection you will need to implement CopyTo, also you can add a version field to the container so you can know if you need to update it.
If you don't want to do it this way and you want to go through the elements and update them check if you can save in each object the state (modified, deleted, updated) this will help in the comparison.
foreach (var _objectiveDetail in _objectiveDetails) {} but then I
think I might need to have two of these and I am also wondering if
there is a better way. Does anyone have any suggestions as to how I
could do this?
instead of looping through whole collection use LINQ query:
var query = from _objectiveDetail in _objectiveDetails
where (condition)
select ... ;
update:
It's pointless to iterate through whole collection if you want to update/delete/add something from web end. Humans are a bit slower than computers, isn't it? Do it one by one. In fact I don't understand the idea of 2 collections. What is it for? If you still want it: use event to run query, select updated/deleted/added record, do appropriate operation on it.

How would I remove items from a List<T>?

I have a list of items.
The problem is the returned items (which I have no control over) return the same items THREE time.
So while the actual things that should be in the list are:
A
B
C
I get
A
B
C
A
B
C
A
B
C
How can I cleanly and easily remove the duplicates? Maybe count the items, divide by three and delete anything from X to list.Count?
The quickest, simplest thing to do is to not remove the items but run a distinct query
var distinctItems = list.Distinct();
If it's a must that you have a list, you can always append .ToList() to the call. If it's a must that you continue to work with the same list, then you'd just have to iterate over it and keep track of what you already have and remove any duplicates.
Edit: "But I'm working with a class"
If you have a list of a given class, to use Distinct you need to either (a) override Equals and GetHashCode inside your class so that appropriate equality comparisons can be made. If you do not have access to the source code (or simply don't want to override these methods for whatever reason), then you can (b) provide an IEqualityComparer<YourClass> implementation as an argument to the Distinct method. This will also allow you to specify the Equals and GetHashCode implementations without having to modify the source of the actual class.
public class MyObjectComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject a, MyObject b)
{
// code to determine equality, usually based on one or more properties
}
public int GetHashCode(MyObject a)
{
// code to generate hash code, usually based on a property
}
}
// ...
var distinctItems = myList.Distinct(new MyObjectComparer());
if you are 100% sure that you receive everything you need 3 times, then just
var newList = oldList.Take(oldList.Count / 3).ToList()
Linq has a Distinct() method which does exactly this. Or put the items in a HashSet if you want to avoid duplicated completely.
If you're using C# 3 or up:
var newList = dupList.Distinct().ToList();
If not then sort the list and do the following:
var lastItem = null;
foreach( var item in dupList )
{
if( item != lastItem )
{
newItems.Add(item);
}
lastItem = item;
}
you could simply create a new list and add items to it that are not already there.

Remove items from list 1 not in list 2

I am learning to write lambda expressions, and I need help on how to remove all elements from a list which are not in another list.
var list = new List<int> {1, 2, 2, 4, 5};
var list2 = new List<int> { 4, 5 };
// Remove all list items not in List2
// new List Should contain {4,5}
// The lambda expression is the Predicate.
list.RemoveAll(item => item. /*solution expression here*/ );
// Display results.
foreach (int i in list)
{
Console.WriteLine(i);
}
You can do this via RemoveAll using Contains:
list.RemoveAll( item => !list2.Contains(item));
Alternatively, if you just want the intersection, using Enumerable.Intersect would be more efficient:
list = list.Intersect(list2).ToList();
The difference is, in the latter case, you will not get duplicate entries. For example, if list2 contained 2, in the first case, you'd get {2,2,4,5}, in the second, you'd get {2,4,5}.
Solution for objects (maybe easier than horaces solution):
If your list contains objects, rather than scalars, it is that simple, by removing by one selected property of the objects:
var a = allActivePatientContracts.RemoveAll(x => !allPatients.Select(y => y.Id).Contains(x.PatientId));
list = list.Except(list2).ToList();
This question has been marked as answered, but there is a catch. If your list contains an object, rather than a scalar, you need to do a bit more work.
I tried this over and over with Remove() and RemoveAt() and all sorts of things and none of them worked correctly. I couldn't even get a Contains() to work correctly. Never matched anything. I was stumped until I got the suspicion that maybe it could not match up the item correctly.
When I realized this, I refactored the item class to implement IEquatable, and then it started working.
Here is my solution:
class GenericLookupE : IEquatable<GenericLookupE>
{
public string ID { get; set; }
public bool Equals( GenericLookupE other )
{
if ( this.ID == other.ID ) return true;
return false;
}
}
After I did this, the above RemoveAll() answer by Reed Copsey worked perfectly for me.
See: http://msdn.microsoft.com/en-us/library/bhkz42b3.aspx

Categories