Removing Duplicates from bottom of Generic List - c#

I am trying to remove duplicates item from bottom of generic list. I have class defined as below
public class Identifier
{
public string Name { get; set; }
}
And I have defined another class which implements IEqualityComparer to remove the duplicates from List
public class DistinctIdentifierComparer : IEqualityComparer<Identifier>
{
public bool Equals(Identifier x, Identifier y)
{
return x.Name == y.Name;
}
public int GetHashCode(Identifier obj)
{
return obj.Name.GetHashCode();
}
}
However, I am trying to remove the old items and keep the latest. For example if I have list of identifier defined as below
Identifier idn1 = new Identifier { Name = "X" };
Identifier idn2 = new Identifier { Name = "Y" };
Identifier idn3 = new Identifier { Name = "Z" };
Identifier idn4 = new Identifier { Name = "X" };
Identifier idn5 = new Identifier { Name = "P" };
Identifier idn6 = new Identifier { Name = "X" };
List<Identifier> list = new List<Identifier>();
list.Add(idn1);
list.Add(idn2);
list.Add(idn3);
list.Add(idn4);
list.Add(idn5);
list.Add(idn6);
And I have implemented
var res = list.Distinct(new DistinctIdentifierComparer());
How do I make sure by using distinct that I am keeping idn6 and removing idn1 and idn4?

Most LINQ operators are order-preserving: the API of Distinct() says it will take the first instance of each item it comes across. If you want the last instance, just do:
var res = list.Reverse().Distinct(new DistinctIdentifierComparer());
Another option that would avoid you having to define an explicit comparer would be:
var res = list.GroupBy(i => i.Name).Select(g => g.Last());
From MSDN:
The IGrouping objects are yielded in an order based on
the order of the elements in source that produced the first key of
each IGrouping. Elements in a grouping are yielded in
the order they appear in source.

You could also implement a custom add method to maintain the latest records:
public class IdentifierList : List<Identifier>
{
public void Add(Identifier item)
{
this.RemoveAll(x => x.Name == item.Name);
base.Add(item);
}
}
Identifier idn1 = new Identifier { Name = "X" };
Identifier idn2 = new Identifier { Name = "Y" };
Identifier idn3 = new Identifier { Name = "Z" };
Identifier idn4 = new Identifier { Name = "X" };
Identifier idn5 = new Identifier { Name = "P" };
Identifier idn6 = new Identifier { Name = "X" };
IdentifierList list = new IdentifierList ();
list.Add(idn1);
list.Add(idn2);
list.Add(idn3);
list.Add(idn4);
list.Add(idn5);
list.Add(idn6);

You could Group and check if any Count is > 1
var distinctWorked = !(res
.GroupBy(a => a.Name)
.Select(g => new{g.Key, Count = g.Count()})
.Any(a => a.Count > 1));

Related

c# how to create an array of an anonymous type? [duplicate]

In C# 3.0 you can create anonymous class with the following syntax
var o = new { Id = 1, Name = "Foo" };
Is there a way to add these anonymous class to a generic list?
Example:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
List<var> list = new List<var>();
list.Add(o);
list.Add(o1);
Another Example:
List<var> list = new List<var>();
while (....)
{
....
list.Add(new {Id = x, Name = y});
....
}
You could do:
var list = new[] { o, o1 }.ToList();
There are lots of ways of skinning this cat, but basically they'll all use type inference somewhere - which means you've got to be calling a generic method (possibly as an extension method). Another example might be:
public static List<T> CreateList<T>(params T[] elements)
{
return new List<T>(elements);
}
var list = CreateList(o, o1);
You get the idea :)
Here is the answer.
string result = String.Empty;
var list = new[]
{
new { Number = 10, Name = "Smith" },
new { Number = 10, Name = "John" }
}.ToList();
foreach (var item in list)
{
result += String.Format("Name={0}, Number={1}\n", item.Name, item.Number);
}
MessageBox.Show(result);
There are many ways to do this, but some of the responses here are creating a list that contains garbage elements, which requires you to clear the list.
If you are looking for an empty list of the generic type, use a Select against a List of Tuples to make the empty list. No elements will be instantiated.
Here's the one-liner to create an empty list:
var emptyList = new List<Tuple<int, string>>()
.Select(t => new { Id = t.Item1, Name = t.Item2 }).ToList();
Then you can add to it using your generic type:
emptyList.Add(new { Id = 1, Name = "foo" });
emptyList.Add(new { Id = 2, Name = "bar" });
As an alternative, you can do something like below to create the empty list (But, I prefer the first example because you can use it for a populated collection of Tuples as well) :
var emptyList = new List<object>()
.Select(t => new { Id = default(int), Name = default(string) }).ToList();
Not exactly, but you can say List<object> and things will work. However, list[0].Id won't work.
This will work at runtime in C# 4.0 by having a List<dynamic>, that is you won't get IntelliSense.
If you are using C# 7 or above, you can use tuple types instead of anonymous types.
var myList = new List<(int IntProp, string StrProp)>();
myList.Add((IntProp: 123, StrProp: "XYZ"));
I guess
List<T> CreateEmptyGenericList<T>(T example) {
return new List<T>();
}
void something() {
var o = new { Id = 1, Name = "foo" };
var emptyListOfAnonymousType = CreateEmptyGenericList(o);
}
will work.
You might also consider writing it like this:
void something() {
var String = string.Emtpy;
var Integer = int.MinValue;
var emptyListOfAnonymousType = CreateEmptyGenericList(new { Id = Integer, Name = String });
}
I usually use the following; mainly because you then "start" with a list that's empty.
var list = Enumerable.Range(0, 0).Select(e => new { ID = 1, Name = ""}).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );
//etc.
Lately, I've been writing it like this instead:
var list = Enumerable.Repeat(new { ID = 1, Name = "" }, 0).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );
Using the repeat method would also allow you to do:
var myObj = new { ID = 1, Name = "John" };
var list = Enumerable.Repeat(myObj, 1).ToList();
list.Add(new { ID = 2, Name = "Liana" });
..which gives you the initial list with the first item already added.
You can do this in your code.
var list = new[] { new { Id = 1, Name = "Foo" } }.ToList();
list.Add(new { Id = 2, Name = "Bar" });
I checked the IL on several answers. This code efficiently provides an empty List:
using System.Linq;
…
var list = new[]{new{Id = default(int), Name = default(string)}}.Skip(1).ToList();
In latest version 4.0, can use dynamic like below
var list = new List<dynamic>();
list.Add(new {
Name = "Damith"
});
foreach(var item in list){
Console.WriteLine(item.Name);
}
}
You can create a list of dynamic.
List<dynamic> anons=new List<dynamic>();
foreach (Model model in models)
{
var anon= new
{
Id = model.Id,
Name=model.Name
};
anons.Add(anon);
}
"dynamic" gets initialized by the first value added.
Here is a another method of creating a List of anonymous types that allows you to start with an empty list, but still have access to IntelliSense.
var items = "".Select( t => new {Id = 1, Name = "foo"} ).ToList();
If you wanted to keep the first item, just put one letter in the string.
var items = "1".Select( t => new {Id = 1, Name = "foo"} ).ToList();
Here is my attempt.
List<object> list = new List<object> { new { Id = 10, Name = "Testing1" }, new {Id =2, Name ="Testing2" }};
I came up with this when I wrote something similar for making a Anonymous List for a custom type.
I'm very surprised nobody has suggested collection initializers. This way can only add objects when the list is created hence the name however it seems like the nicest way of doing it. No need to create an array then convert it to a list.
var list = new List<dynamic>()
{
new { Id = 1, Name = "Foo" },
new { Id = 2, Name = "Bar" }
};
You can always use object instead of dynamic but trying to keep it in a true generic way then dynamic makes more sense.
Instead of this:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
List <var> list = new List<var>();
list.Add(o);
list.Add(o1);
You could do this:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
List<object> list = new List<object>();
list.Add(o);
list.Add(o1);
However, you will get a compiletime error if you try to do something like this in another scope, although it works at runtime:
private List<object> GetList()
{
List<object> list = new List<object>();
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
list.Add(o);
list.Add(o1);
return list;
}
private void WriteList()
{
foreach (var item in GetList())
{
Console.WriteLine("Name={0}{1}", item.Name, Environment.NewLine);
}
}
The problem is that only the members of Object are available at runtime, although intellisense will show the properties id and name.
In .net 4.0 a solution is to use the keyword dynamic istead of object in the code above.
Another solution is to use reflection to get the properties
using System;
using System.Collections.Generic;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var anonymous = p.GetList(new[]{
new { Id = 1, Name = "Foo" },
new { Id = 2, Name = "Bar" }
});
p.WriteList(anonymous);
}
private List<T> GetList<T>(params T[] elements)
{
var a = TypeGenerator(elements);
return a;
}
public static List<T> TypeGenerator<T>(T[] at)
{
return new List<T>(at);
}
private void WriteList<T>(List<T> elements)
{
PropertyInfo[] pi = typeof(T).GetProperties();
foreach (var el in elements)
{
foreach (var p in pi)
{
Console.WriteLine("{0}", p.GetValue(el, null));
}
}
Console.ReadLine();
}
}
}
You can do it this way:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
var array = new[] { o, o1 };
var list = array.ToList();
list.Add(new { Id = 3, Name = "Yeah" });
It seems a little "hacky" to me, but it works - if you really need to have a list and can't just use the anonymous array.
This is an old question, but I thought I'd put in my C# 6 answer. I often have to set up test data that is easily entered in-code as a list of tuples. With a couple of extension functions, it is possible to have this nice, compact format, without repeating the names on each entry.
var people= new List<Tuple<int, int, string>>() {
{1, 11, "Adam"},
{2, 22, "Bill"},
{3, 33, "Carol"}
}.Select(t => new { Id = t.Item1, Age = t.Item2, Name = t.Item3 });
This gives an IEnumerable - if you want a list that you can add to then just add ToList().
The magic comes from custom extension Add methods for tuples, as described at https://stackoverflow.com/a/27455822/4536527.
public static class TupleListExtensions {
public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
T1 item1, T2 item2) {
list.Add(Tuple.Create(item1, item2));
}
public static void Add<T1, T2, T3>(this IList<Tuple<T1, T2, T3>> list,
T1 item1, T2 item2, T3 item3) {
list.Add(Tuple.Create(item1, item2, item3));
}
// and so on...
}
The only thing I don't like is that the types are separated from the names, but if you really don't want to make a new class then this approach will still let you have readable data.
var list = new[]{
new{
FirstField = default(string),
SecondField = default(int),
ThirdField = default(double)
}
}.ToList();
list.RemoveAt(0);
For your second example, where you have to initialize a new List<T>, one idea is to create an anonymous list, and then clear it.
var list = new[] { o, o1 }.ToList();
list.Clear();
//and you can keep adding.
while (....)
{
....
list.Add(new { Id = x, Name = y });
....
}
Or as an extension method, should be easier:
public static List<T> GetEmptyListOfThisType<T>(this T item)
{
return new List<T>();
}
//so you can call:
var list = new { Id = 0, Name = "" }.GetEmptyListOfThisType();
Or probably even shorter,
var list = new int[0].Select(x => new { Id = 0, Name = "" }).Tolist();
Deriving from this answer, I came up with two methods that could do the task:
/// <summary>
/// Create a list of the given anonymous class. <paramref name="definition"/> isn't called, it is only used
/// for the needed type inference. This overload is for when you don't have an instance of the anon class
/// and don't want to make one to make the list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="definition"></param>
/// <returns></returns>
#pragma warning disable RECS0154 // Parameter is never used
public static List<T> CreateListOfAnonType<T>(Func<T> definition)
#pragma warning restore RECS0154 // Parameter is never used
{
return new List<T>();
}
/// <summary>
/// Create a list of the given anonymous class. <paramref name="definition"/> isn't added to the list, it is
/// only used for the needed type inference. This overload is for when you do have an instance of the anon
/// class and don't want the compiler to waste time making a temp class to define the type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="definition"></param>
/// <returns></returns>
#pragma warning disable RECS0154 // Parameter is never used
public static List<T> CreateListOfAnonType<T>(T definition)
#pragma warning restore RECS0154 // Parameter is never used
{
return new List<T>();
}
You can use the methods like
var emptyList = CreateListOfAnonType(()=>new { Id = default(int), Name = default(string) });
//or
var existingAnonInstance = new { Id = 59, Name = "Joe" };
var otherEmptyList = CreateListOfAnonType(existingAnonInstance);
This answer has a similar idea, but I didn't see it until after I made those methods.
Using Reflection
Microsoft documentation about this topic.
using System;
using System.Collections;
using System.Collections.Generic;
var anonObj = new { Id = 1, Name = "Foo" };
var anonType = anonObj.GetType();
var listType = typeof(List<>);
// We know that List<> have only one generic argument, so we do this:
var contructed = listType.MakeGenericType(anonType);
// Create instance
var instance = Activator.CreateInstance(contructed);
// Using it
var list = (IList)instance;
list.Add(anonObj);
For Dictionary<,> you need to pass 2 arguments
Ex.: dicType.MakeGenericType( type1, type2 )
And for generic types with constraints (where T : struct),
we need to do more verifications. Check microsoft docs to learn how.
Try with this:
var result = new List<object>();
foreach (var test in model.ToList()) {
result.Add(new {Id = test.IdSoc,Nom = test.Nom});
}

Linq Union ordering - how do i ensure items from the first IEnumerable remain first in result?

i'd like to figure out if it's possible (or, if it's already being done) to ensure the items from the first IEnumerable are kept - while duplicates from a union of another IEnumerable are discarded.
For example:
using System.Collections.Generic;
using System.Linq;
namespace MyApp.ExampleStuff
{
public class SomeDto
{
string name {get; set;}
int classId {get; set;}
int notComparedObject {get; set;}
}
public class test {
public void DoSomething()
{
IEnumerable<SomeDto> firstDto = new List<SomeDto>() { new SomeDto() {name = "Dave", classId = 1, notComparedObject = 12}};
IEnumerable<SomeDto> secondDto = new List<SomeDto>() { new SomeDto() {name = "Dave", classId = 1, notComparedObject = 16}, new SomeDto() {name = "Brad", classId = 1, notComparedObject = 77}};
var result = GetUnionedLists(firstDto, secondDto);
}
public ILookup<SomeDto> GetUnionedLists (IEnumerable<SomeDto> dtoA, IEnumerable<SomeDto> dtoB)
{
return dtoA.Union(dtoB, new SomeDtoComparer()).ToLookUp(x => x.classId);
}
}
public class SomeDtoComparer : IEqualityComparer<SomeDto>
{
public bool Equals(SomeDto SomeDtoA, SomeDto SomeDtoB)
{
if (SomeDtoA == null && SomeDtoB == null)
{
return true;
} else if (SomeDtoA == null || SomeDtoB == null)
{
return false;
}
return (SomeDtoA.Name == SomeDtoB.Name && SomeDtoA.classId == SomeDtoB.classId);
}
public int GetHashCode(SomeDto SomeDtoX)
{
int hashName = SomeDtoX.Name == null ? 0 : SomeDtoX.Name.GetHashCode();
int hashClassId = SomeDtoX.classId == null ? 0 : SomeDtoX.classId.GetHashCode();
return hashName ^ hashClassId;
}
}
}
If this is run - i would hope that the value of result in DoSomething() is a Lookup containing only the following someDto's under classId "1":
SomeDto() {name = "Dave", classId = 1, notComparedObject = 12}
SomeDto() {name = "Brad", classId = 1, notComparedObject = 77}
As you can see, if "Name" and "classId" are the same - the results are considered Equal, and i'd then like to keep the item from the original IEnumerable, and discard the "duplicate" - in this case that was:
SomeDto() {name = "Dave", id = 1, notComparedObject = 16}
If the result were to come out like this - it would be considered wrong (as the items from the second Enumerable were placed first in the result):
SomeDto() {name = "Brad", classId = 1, notComparedObject = 77}
SomeDto() {name = "Dave", classId = 1, notComparedObject = 12}
Enumerable.Union method already yields items in order that you've described. It's written in the docs that
When the object returned by this method is enumerated, Union enumerates first and second in that order and yields each element that has not already been yielded.
On the other side, Lookup type as well as IGrouping interface does not give any guarantees on elements order (looks like current implementation of ToLookup keeps the original order, but this could change). So if it really matters, you should add some additional logic — like using custom type instead of Lookup, adding custom property for index and ordering by it or, probably, using GroupBy, which does guarantees the order as it's stated in the docs.
The IGrouping objects are yielded in an order based on the order of the elements in source that produced the first key of each IGrouping. Elements in a grouping are yielded in the order that the elements that produced them appear in source.
I think you could do this using the FullJoin function available within the MoreLinq library (available on NuGet).
https://morelinq.github.io/3.0/ref/api/html/M_MoreLinq_MoreEnumerable_FullJoin__3_1.htm
Example:
public ILookup<SomeDto> GetUnionedLists (IEnumerable<SomeDto> dtoA, IEnumerable<SomeDto> dtoB)
{
return dtoA
.FullJoin(dtoB,
e => e,
first => first,
second => second,
(first, second) => first,
new SomeDtoComparer())
.ToLookUp(x => x.classId);
}

Using Linq to remove from set where key exists in other set?

What is the proper way to do set subtraction using Linq? I have a List of 8000+ banks where I want to remove a portion of those based on the routing number. The portion is in another List and routing number is the key property to both. Here is a simplification:
public class Bank
{
public string RoutingNumber { get; set; }
public string Name { get; set; }
}
var removeThese = new List<string>() { "111", "444", "777" };
var banks = new List<Bank>()
{
new Bank() { RoutingNumber = "111", Name = "First Federal" },
new Bank() { RoutingNumber = "222", Name = "Second Federal" },
new Bank() { RoutingNumber = "333", Name = "Third Federal" },
new Bank() { RoutingNumber = "444", Name = "Fourth Federal" },
new Bank() { RoutingNumber = "555", Name = "Fifth Federal" },
new Bank() { RoutingNumber = "666", Name = "Sixth Federal" },
new Bank() { RoutingNumber = "777", Name = "Seventh Federal" },
new Bank() { RoutingNumber = "888", Name = "Eight Federal" },
new Bank() { RoutingNumber = "999", Name = "Ninth Federal" },
};
var query = banks.Remove(banks.Where(x => removeThese.Contains(x.RoutingNumber)));
This should do the trick:
var toRemove = banks.Where(x => removeThese.Contains(x.RoutingNumber)).ToList();
var query = banks.RemoveAll(x => toRemove.Contains(x));
The first step is to make sure that you don't have to re-run that first query over and over again, whenever banks changes.
This should work too:
var query = banks.Except(toRemove);
as your second line.
EDIT
Tim Schmelter pointed out that for Except to work, you need to override Equals and GetHashCode.
So you could implement it like so:
public override string ToString()
{
... any serialization will do, for instance JSON or CSV or XML ...
... OR any serialization that identifies the object quickly, such as:
return "Bank: " + this.RoutingNumber;
}
public override bool Equals(System.Object obj)
{
return ((obj is Bank) && (this.ToString().Equals(obj.ToString()));
}
public override int GetHashCode()
{
return this.ToString().GetHashCode();
}
Generally it's less work to just pull out the ones you need rather than deleting the ones you don't i.e.
var query = myList.Where(x => !removeThese.Contains(x.RoutingNumber));
Filtering of this type is generally done with generic LINQ constructs:
banks = banks.Where(bank => !removeThese.Contains(bank.RoutingNumber)).ToList();
In this specific case you can also use List<T>.RemoveAll to do the filtering in-place, which will be faster:
banks.RemoveAll(bank => removeThese.Contains(bank.RoutingNumber));
Also, for performance reasons, if the amount of routing numbers to remove is large you should consider putting them into a HashSet<string> instead.
Either use the Linq extension methods Where and ToList to create a new list or use List.RemoveAll which is more efficient since it modifies the original list:
banks = banks.Where(x => !removeThese.Contains(x.RoutingNumber)).ToList();
banks.RemoveAll(x => removeThese.Contains(x.RoutingNumber));
Of course you have to reverse the condition since the former keeps what Where leaves and the latter removes what the predicate in RemoveAll returns.
Have you tried using RemoveAll()?
var query = banks.RemoveAll(p => removeThese.Contains(p.RoutingNumber));
This will remove the any values from banks where a matching record is present in removeThese.
query will contain the number of records removed from the list.
Note: The orginal variable banks will be updated directly by this query; a reassignment is not required.
You can use RemoveAll()
var removedIndexes = banks.RemoveAll(x => removeThese.Contains(x.RoutingNumber));
or
banks = banks.Where(bank => !removeThese.Contains(bank.RoutingNumber)).ToList();

Group by in linq + select

say I have this data
1 757f27a2-e997-44f8-b2c2-6c0fd6ee2c2f 2 3
2 757f27a2-e997-44f8-b2c2-6c0fd6ee2c2f 3 1
3 757f27a2-e997-44f8-b2c2-6c0fd6ee2c2f 2 2
column 1 // pk
column 2 // userId
column 3 // courseId
column 4 // permissionId
I have this class
class CoursePermissions
{
public string Prefix { get; set; }
public bool OwnerPermission { get; set; } // permissionId 1
public bool AddPermission { get; set; } // permissionId 2
public bool EditPermission { get; set; } // permissionId 3
}
I want to group all the 3 rows by courseId(or Prefix) and then take that information and make a class out Of it
So the end result would be
List<CoursePermissions> permissions = new List<CoursePermissions>();
CoursePermissions a = new CoursePermissions
{
Prefix = "comp101";
OwnerPermission = false,
AddPermission = true,
EditPermission = true
};
CoursePermissions b = new CoursePermissions
{
Prefix = "comp102";
OwnerPermission = true,
AddPermission = false,
EditPermission = false
};
permissions.Add(a);
permissions.Add(b);
So the above is how the object would look if I took all the row data from the db and manually made it the way I wanted it too look. Of course I need to do it somehow as a query.
In my example I have 2 students. They both belong to the same course. Student 1has edit and Add permission for Comp101 but only owner permissions for comp102.
I want to get all the rows back for Comp101 and put it into CoursePermissions. Then I want to get all the rows back for Comp102 and put it into CoursePermissions. Then store all these in a collection and use them.
The only thing I can do is something like this
var list = session.Query<PermissionLevel>().Where(u => u.Student.StudentId == studentId).ToList();
IEnumerable<IGrouping<string, PermissionLevel>> test = list.GroupBy(x => x.Course.Prefix);
foreach (var t in test)
{
CoursePermissions c = new CoursePermissions();
foreach (var permissionLevel in t)
{
if (permissionLevel.PermissionLevelId == 1)
{
c.OwnerPermission = true;
}
}
}
It would nice if I could get rid of the nest for each loop and do it all when the data comes from the query.
Here's an approach that I think is quite functional.
First set up a dictionary of actions that will set the appropriate course permission given a permission level id.
var setPermission = new Dictionary<int, Action<CoursePermissions>>()
{
{ 1, cps => cps.OwnerPermission = true },
{ 2, cps => cps.AddPermission = true },
{ 3, cps => cps.EditPermission = true },
};
Now create a function that will turn the course prefix and a list of permission level ids into a new CoursePermissions object.
Func<string, IEnumerable<int>, CoursePermissions>
buildCoursePermission = (prefix, permissionLevelIds) =>
{
var cps = new CoursePermissions() { Prefix = prefix };
foreach (var permissionLevelId in permissionLevelIds)
{
setPermission[permissionLevelId](cps);
}
return cps;
};
Now all you have left is a simple query that turns your list of permission levels into a list of course permissions.
var coursePermissionsList =
(from pl in list
group pl.PermissionLevelId by pl.Course.Prefix into gcpls
select buildCoursePermission(gcpls.Key, gcpls)).ToList();
How does that work for you?

How to get items that are and are not in a list

I have an IEnumerable, listOfOnes, and an IEnumerable, listOfTwos.
Assuming that I can compare objects of V against objects of T, I'd like to find which items are in listOfOnes but, not in listOfTwos. And vice versa.
ex:
var listOfOnes = new List<One>
{
new One
{
name = "chris",
type = "user"
},
new One
{
name = "foo",
type = "group"
},
new One
{
name = "john",
type = "user"
},
};
var listOfTwos = new[]
{
new Two
{
name = "chris",
type = "user"
},
new Two
{
name = "john",
type = "user"
},
new Two
{
name = "the Steves",
type = "group"
}
};
var notInTwos; //= listOfOnes.FindDifferences(listOfTwos);
//find all objects not in listOfTwos. Should find 'foo'.
var notInOnes; //= listOfTwos.FindDifferences(listOfOnes)
//find all objects not in listOfOnes. Should find 'the Steves'.
If you can convert one of the types to the other, you can use Except and Intersect, for example:
listOfOnes.Except(listOfTwos.Cast<One>())
Otherwise you can test for each element in the first list if it is equal to any of the elements in the second list:
var notInTwos = listOfOnes.Where(one =>
!listOfTwos.Any(two => two.Equals(one)));
This won't be as fast though.
Perhaps something like
public static IEnumerable<T> FindDifference<U> (
this IEnumerable<T> a,
IEnumerable<U> b,
Func<T, U> convert)
{
IEnumerable<T> bConvertedToT = b.Select (item => convert (item));
IEnumerable<T> aNotInB = a.Except (bConvertedToT);
return aNotInB;
}

Categories