Comparing custom objects c# - c#

I have the following situation:
I have a loop which could loop any amount of times. Within that loop I am calling a method which returns a List<CustomClass> After I have run through the loop I need to be able to compare all the List<CustomClass> items from each list and see which ones are common between all of them. In order to do this I have tried to put all the List<CustomClass> into another list: List<List<CustomClass> and then I need to use all of these to see which ones exist in all of them. I will be comparing on one property of my CustomClass (string Name)
This is what I have so far
public class CustomClass
{
public string name;
}
public List<CustomClass> SomeMethod()
{
List<List<CustomClass>> bigList = new List<List<CustomClass>>();
List<CustomClass> finalList = new List<CustomClass>();
for (int i=0;i<=5;i++)
{
List<List<CustomClass>> newList = GetNewList();
bigList.Add(newList);
}
//I now need to compare everything in bigList and create a new list with all common
//items in the list of bigList.
return finalList ;
}
public List<CustomClass> GetNewList()
{
List<CustomClass> newList = new List<CustomClass>();
for (int i=0;i<=5;i++)
{
CustomClass newClass = new CustomClass();
newClass.name = "some name";
newList.Add(newClass);
}
return newList;
{
I hope this makes sense. Any help on this is much appreciated.
Thanks
Edit
For example in List<List<CustomClass>> each List<CustomClass> contains a CustomClass with name set to "Pete", I then want to create a CustomClass with name set to "Pete" and add it to the final list.

see which ones are common between all of them
Use the Intersect extension method:
var common = list1.Intersect(list2);
Note that for this to work, you should either:
override Equals and GetHashCode in CustomClass
make CustomClass implement IEquatable<CustomClass>
provide Intersect with a custom comparer that implements IEqualityComparer<CustomClass>

Related

Remove all elements from a Key value pair which are equal to elements in a list?

I have two list
static List<dynamic> List1= new List<dynamic>();
list<string> List2
List1 is {Message='asdasd',Mobilenum=995955}
i want to remove all elements from List1 if the value in list2 is there in list1?
I have done this but it is not working
List1.RemoveAll(c => list2.ToList().Exists(n => n.Mobilenum== c.Values));
List1.RemoveAll(c => List2.Contains(c.Mobilenum));
But replace dynamic with the real type. Normally you don't need to use it. If it's an anonymous type and you have to pass this list around, consider to implement a new type with these properties.
First, create a type instead of using dynamic:
public class MyType
{
public string Message;
public string Mobilenum;
}
Then, you can do:
var List1 = new List<MyType>();
// ...build your list
var List2 = new List<string>();
// ...build your other list
var res = List1.Where(x => !List2.Contains(x.Mobilenum));
Avoid using Remove unless you're sure you want to throw away the information instead of just "filtering" it for a particular use.

Using Linq to filter an array with bool properties true in to another array

I have an array of type Brick with each brick having an isBroken bool property. How can I use Linq to filter all bricks with isBroken = true into a new array?
Use Where to filter the list of bricks and ToArray to materialize the result into a new array.
var result = MyBricksArray.Where(x => x.isBroken).ToArray();
I hope this example will explain things more clearly, Let the definition of your class will be like the following:
public class Brick
{
public string Name;
public bool isBroken ;
}
And the Array of it's objects is defined like this:
Brick[] BrickArray =new Brick[]{
new Brick(){Name="A",isBroken=true},
new Brick(){Name="B",isBroken=true},
new Brick(){Name="C",isBroken=false}};
Then you can use the .Where to filter the collection like the following:
var selectedBriks = BrickArray.Where(x=>x.isBroken).ToList();
Now the selectedBriks will contains the items with name A and B
You can use the select method for this :
var theNewList = Brick.Where(b => b.isBroken).Select(b => new Brick
{
//Populate new object
}).ToList() / .ToArray();
Note that Select is used and not where to project the List into a new one.
Also, the .ToList()/ToArray() is to add the array to memory, you may not need it, .Select() return an IEnumerable.
It is possible to use Brik.Where(..).ToList(); as this would create a new List but the reference to the object inside the list would be the same, so again it depands on your need.
And .Select() require using System.Linq;

Compare 2 Lists with a foreach Loop and create new List from items not found

I apologize for posting such a seemingly simple question. I know there are many similar questions already posted (and I have looked at many of these before posting my question), but I struggle to apply the answers to my situation. I am relatively new to C#, and would appreciate your input and help.
How can I compare my 2 Lists with a foreach loop, and create a new List with the records found that does not exist in my comparison?
Below is the code outline I already have, and comments of what needs to happen:
private void updateHolidays()
{
List<Holiday> localHolidays = getLocalHolidays();
List<Holiday> remoteHolidays = getRemoteHolidays();
List<Holiday> holidayDifference = new List<Holiday>();
foreach (Holiday holiday in remoteHolidays)
{
if (true) // holiday does not exist in localHolidays
{
// add holiday to holidayDifference
holidayDifference.Add(holiday);
}
}
createNewHolidays(holidayDifference);
}
Thank you in advance!
The easiest way would be using LinQ. The Except methods returns all items from the source which not exists in the second list.
holidayDifference = remoteHolidays.Except(localHolidays).ToList();
The Except method accepts a optional second parameter to customize the comparison. If you don't pass a IEqualityComparer<T> the standard comparison with the Holiday.Equals method will be used. Alternatively you can override this method instead of passing a comparer.
Like most of LinQ methods, Except returns a IEnumerable<T>, this can be easily converted to a List<T> with the ToList method.
The MSDN documentations are linked inline.
If you still want to implement this yourself, you can use the Contains method of List<T>:
foreach (Holiday holiday in remoteHolidays)
{
if (!localHolidays.Contains(holidy))
{
An alternative to Contains would be LinQs Any which allows you to compare your objects with a function/lamda expression.
Assuming you have a overload of the Equals method and Holiday objects are compareable
List<Holiday> holidayDifference = remoteHolidays.Except(localHolidays).ToList();
You can use Linq's Except extension method:
holidayDifference = remoteHolidays
.Except(localHolidays)
.ToList();
Note that this will also requires Holiday to implement a valid Equals method of IEquatable<Holiday> method override, also GetHashCode must return an identical hash for two Holidays for which Equals returns true.
Also, Except is an extension which returns (in this case) an IEnumerable<Holiday> therefore you will have to use the ToList extension method in order to retrieve a List<Holiday>
Alternatively, you can use the other overload of Except which allows you to provide an IEqualityComparer<Holiday> instead of modifying your original class.
Example with strings:
List<string> holidayDifference = new List<string>();
List<string> remoteHolidays = new List<string> { "1", "2", "3" };
List<string> localHolidays = new List<string> { "1", "3" };
holidayDifference = remoteHolidays
.Except(localHolidays)
.ToList();
holidayDifference.ForEach(Console.WriteLine);
Output:
2
Example With Holiday : IEquatable<Holiday>:
class Holiday : IEquatable<Holiday>
{
public string Name { get; set; }
public bool Equals(Holiday other)
{
return Name == other.Name;
}
// GetHashCode must return true whenever Equals returns true.
public override int GetHashCode()
{
//Get hash code for the Name field if it is not null.
return Name?.GetHashCode() ?? 0;
}
}
public class Program
{
public static void Main()
{
List<Holiday> holidayDifference = new List<Holiday>();
List<Holiday> remoteHolidays = new List<Holiday>
{
new Holiday { Name = "Xmas" },
new Holiday { Name = "Hanukkah" },
new Holiday { Name = "Ramadan" }
};
List<Holiday> localHolidays = new List<Holiday>
{
new Holiday { Name = "Xmas" },
new Holiday { Name = "Ramadan" }
};
holidayDifference = remoteHolidays
.Except(localHolidays)
.ToList();
holidayDifference.ForEach(x => Console.WriteLine(x.Name));
}
}
Output:
Hanukkah
If you insist on foreach loop you can use HashSet<Holiday> to store the Holidays to exclude:
HashSet<Holiday> hs = new HashSet<Holiday>(localHoliday);
foreach (Holiday holiday in remoteHolidays)
if (!hs.Contains(holiday))
holidayDifference.Add(holiday);
private void updateHolidays()
{
List<Holiday> localHolidays = getLocalHolidays();
List<Holiday> remoteHolidays = getRemoteHolidays();
List<Holiday> holidayDifference = new List<Holiday>();
foreach (Holiday holiday in remoteHolidays)``
{
if (localHolidays.Contains(holiday))
{
// add holiday to holidayDifference
holidayDifference.Add(holiday);
}
}
createNewHolidays(holidayDifference);
}
It depends if both the lists contain the same Holiday objects, if they are the same objects that are in both lists then you don't need to make a comparer that implements IEqualityComparer. I will assume they are not the same objects and hence you are doing comparisons by value rather than reference.
The quickest way of doing this is without a foreach loop, you can do this using LINQ as such.
var holidayDifference = remoteHolidays.Except(localHolidays, new HolidaysComparer()).ToList();
If you want to do it using a foreach loop you can do it several ways. You can do it using a HashSet (make sure to initialize the set with a HolidayComparer) by iterating on the values in localHolidays and adding them to the set, then pulling out the ones from remoteHolidays that aren't in that set. However the easy way would be to use the LINQ contains function (The except function above essentially wraps this into a loop).
var holidayDifference = new List<Holiday>();
var comparer = new HolidayComparer();
foreach(Holiday holiday in remoteHolidays)
{
if(!localHolidays.Contains(holiday, comparer))
holidayDifference.Add(holiday);
}
If you are comparing each holiday by reference then you won't need to implement the IEqualityComparer interface.
To learn how to implement this interface look here IEqualityComparer Interface

Retrieve Data From IEnumerable?

[CrossPost From MSDN]
I had a task that, I need to send a generic List to a method, where I need to iterate it and convert it to an Excel File. I already did this with Data Table, but with Generic list I am facing some problems (I don't want to convert my generic list to Data Table). I will paste the code which helps me out for an answer.
I Had Two Generic Lists
List<User> objList = new List<User>();
List<Student> objStudent = new List<Student>();
// I am adding some Item to List
User obj = new User(1, "aaa");
User obj1 = new User(2, "bbb");
User obj2 = new User(3, "ccc");
User obj3 = new User(4, "ddd");
Student sobj = new Student(1, "aaa");
Student sobj1 = new Student(2, "bbb");
Student sobj2 = new Student(3, "ccc");
Student sobj3 = new Student(4, "ddd");
objList.Add(obj);ExportToExcel(objList);
To Export it to Excel , I am passing the lists to the below methods as
public void ExportToExcel<T>(IEnumerable<T> list)
{
PropertyInfo[] piT = typeof(T).GetProperties();
var Users = list.ToList();
Type myType = (typeof(T));
}
When I am passing my list to Ienumerable... I am not able to retrieve the data present in the List IEnumerable list. If I retrieve the data , then I can handle further. Could any one suggest me the better Idea?
If your always going to work with List<T> you could change IEnumerable<T> to IList<T>. AFAIK the IEnumerable interface does not define methods for accessing the data inside the collection, only to iterate it.
You could even use ICollection<T> if it suits your needs.
If you need to access the values of all the properties on type T, you can use the PropertyInfo.GetValue method:
public void ExportToExcel<T>(IEnumerable<T> items)
{
var properties = typeof(T).GetProperties();
foreach(var item in items)
{
foreach(var property in properties)
{
var value = property.GetValue(item, null);
// Do something else with the property's value
}
}
}
Edit in response to comment
You indicated you might receive a single list or a list of lists. You can add another overload which takes the composed lists, then iterate through it and export each individual list:
public void ExportToExcel<T>(IEnumerable<IEnumerable<T>> itemSets)
{
foreach(var itemSet in itemSets)
{
ExportToExcel(itemSet);
}
}

C# List<List<T>> Sort

How can I sort a list of list?
persons.OrderBy(p => p.rate).ToList();
The list of list (persons) is declared like this:
public class Persons : List<Person> { }
When I'm trying to run the first statement I get an error:
Cannot convert from 'System.Collections.Generic.List' to
'Persons'
Is there a way to do this using LINQ?
Just because it inherits from a list doesn't mean you can use it like one.
Remember for everything else to see it as a list use interfaces (IList<T>). Then methods depending on IEnumerable, IList, ICollection, etc. can see that it's something it can deal with.
Otherwise, whose to say your Add() (As defined by IList) method isn't named AddPerson in your class?
You can achive it with that statement:
var persons = new Persons ();
persons.AddRange(persons.OrderBy(p => p.rate));
If you want to order all persons in all lists and huddle up them into one list:
var persons = new System.Collections.Generic.List<Persons>();
var trio = new Persons() { new Person(7), new Person(3), new Person(8) };
var pair = new Persons() { new Person(1), new Person(2) };
persons.Add(trio);
persons.Add(pair);
var ordered = persons.SelectMany(p => p).OrderBy(p => p.rate).ToList();
http://msdn.microsoft.com/en-us/library/system.linq.enumerable.selectmany.aspx
To achieve a SortBy behavior, you have to follow these three easy steps:
Store the old items (a. by storing the reference to the old list | b. by copying all entries of the old list into a new one)
Create an empty instance of your container class (a. by creating a new object of the needed type | b. by clearing the old list)
Fill your empty list with the entries while ordering them as you desire.
This little extension method should do the Trick:
public static void SortBy<TList, TItem, TOrder>(this TList source,
Func<TItem, TOrder> sortFunc)
where TList : List<TItem>
{
var l = source.ToList();
source.Clear();
source.AddRange(l.OrderBy(sortFunc));
}

Categories