Order a object's list - c#

I have a list of objects where the object has 2 attributes: id and name. I have to order the list by the name of the objects
How can I do this?

Have you tried using linq?
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
public class Test
{
public void SortTest()
{
var myList = new List<Item> { new Item { Name = "Test", Id = 1 }, new Item { Name = "Other", Id = 1 } };
var result = myList.OrderBy(x => x.Name);
}
}
public class Item
{
public string Name { get; set; }
public int Id { get; set; }
}
}

Linq is overkill for this when there are built-in methods to do this.
If you mean an array, just use Array.Sort:
Array.Sort(items, (x, y) => x.Name.CompareTo(y.Name));
If you mean a List<T>, use the Sort function there:
items.Sort((x, y) => x.Name.CompareTo(y.Name));

If you are dealing with a List of these objects, the easiest way is to supply a Comparison to the Sort method:
var list = new List<MyObject>();
...
list.Sort((item1, item2) => item1.Name.CompareTo(item2.Name));
(Above sample doesn't do null checking on the name)
This is nice and simple because string implements IComparable<string>. An alternative would be for you to implement IComparable<MyObject> on your class, and then you could just call list.Sort()

The Sort method of the List<T> class has an overload that takes a custom comparison:
list.Sort((x,y) => x.Name.CompareTo(y.Name));

Related

How to add items to a list from List<IEnumerable> using C# Linq

I have a List T , and I have a List of IEnumerable query.
I want to add the selection/ all the values of List of IEnumerable to this List T.
I tried below but it shows error : Cannot implicitly convert type 'List IEnumerable ' to a generic list Reason.
I am very new to LINQ, please guide. I tried the following :
public class Reason
{
public int ReasonId { get; set; }
public int OrderId { get; set; }
}
var newReason = new List<Reason>();
newReason = reasons?.Select(res => res.Select(re => new Reason()
{
ReasonId = re.ReasonId,
OrderId = re.OrderId,
})).ToList();
You are trying to create list of Reason instance by flattening nested reasons list, try SelectMany()
newReason = reasons?.SelectMany(re => new Reason()
{
ReasonId = re.ReasonId,
OrderId = re.OrderId,
})?.ToList()
?? new List<Reason>();
Select() inside Select() will return IEnumerable<IEnumerable<Reason>>, and you need flatten list of Reason class i.e. List<Reason> so use SelectMany()

How to find Distinc from the multiple lists using LINQ?

I need to find all the distinct items (religions) from the entire collection of countries, where everyone of them has its own list of the items (religions). Here is my object class:
public class Country
{
public string Name { get; }
public List<string> Religions { get; }
public Country(string name, List<string> religions)
{
Name = name;
Religions = religions;
}
public static List<Country> GetCountries()
{
return new List<Country>()
{
new Country( "Venezuela", new List<string> { "Roman Catholic", "Protestant" } ),
new Country( "Peru", new List<string> { "Roman Catholic", "Evangelical" } ),
new Country( "Paraguay", new List<string> { "Roman Catholic", "Protestant" } ),
new Country( "Bolivia", new List<string> { "Roman Catholic", "Evangelical", "Protestant" } )
};
}
public override string ToString() =>
$"\n{Name} \nReligions: {string.Join(", ", Religions)}";
}
Here is my Main class:
List<Country> countries = Country.GetCountries();
AllReligions(countries);
Console.ReadKey();
static void AllReligions(List<Country> countries)
{
var distinctReligions = countries
.Select(r => new { r.Religions })
.Distinct()
.ToList();
Console.WriteLine("Religions in South America:");
foreach (var rel in distinctReligions)
Console.WriteLine(rel);
}
I am on my 5th iteration of the code and one of the problems is that I don't know where the error is happening - inside my DISTINCT function or inside my printout function. Any help would be greatly appreciated. This is the printout:
Both are wrong. Both work on the anonymous objects, not the religion strings.
To get the distinct religions you need to use SelectMany to "flatten" the country/religion graph:
IEnumerable<string> religions=Country
.GetCountries()
.SelectMany(country=>country.Religions)
.Distinct();
Console.WriteLine("Religions in South America:");
foreach (var rel in religions)
{
Console.WriteLine(rel);
}
The equivalent in query form would be :
var religions = ( from country in Country.GetCountries()
from religion in country.Religions
select religion
).Distinct();
You have several problems here.
What you think you get with .Select(r => new { r.Religions }) - it gives you list of lists. What you want is SelectMany(r => r.Religions) - that way, you will get list of religions, no list of anonymous objects with list in its property.
Distinct uses basic comparison, you need to write custom comparer and provide it to overload:
Distinct<TSource>(IEnumerable<TSource>, IEqualityComparer<TSource>)
You need to implement IEqualityComparer<Religion>.
UPDATE: religion is just a string, so you do not need to implement one, after using SelectMany everything should be just fine.
Your "printout" function - you use Console.WriteLine which invokes just ToString on objects - as you have lists there, it looks weird :) After suggestion of using SelectMany it should go away.

Converting a distinct list to dictionary not working

I'm trying to convert a list of objects to a dictionary using the following code:
var MyDictionary = MyList.Distinct().ToDictionary(i => i.ObjectId, i => i);
I know that a dictionary should not contain duplicate elements, hence the .Distinct(). Yet I still get the following Exception whenever there's a duplicate element:
An item with the same key has already been added.
MyList is a list of MyObject that looks like this:
public class MyObject{
public string ObjectId { get; set; }
public string FName { get; set; }
public string LName { get; set; }
}
Is there a better way to create a dictionary from a list of objects ? or am I doing something wrong?
If you want to compare on the ObjectId, you'll need to pass in a custom comparer to .Distinct(). You can do so like this:
class MyObjectComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
return x.ObjectId == y.ObjectId;
}
public int GetHashCode(MyObject obj)
{
return obj.ObjectId.GetHashCode();
}
}
var MyDictionary = MyList
.Distinct(new MyObjectComparer())
.ToDictionary(i => i.ObjectId, i => i);
You could use Group by and then select first from the List as below:
var MyDictionary = MyList.GroupBy(i => i.ObjectId, i => i).ToDictionary(i => i.Key, i => i.First());
Distinct works using the objects built in Equals and GetHashCode methods by default but your dictionary works only over the id. You need to pass in a IEqualityComparer in to distinct that does the comparison on Id to test if items are equal or make MyObject implment Equals and GetHashCode and have that compare on the Id.

cannot convert error when trying to assign List<T> to List<U> where U is T's interface

here is code illustration
interface IObjectA
{
int Id { get; }
string Name { get; }
}
class ObjectA : IObjectA
{
public int Id { get; set; }
public string Name { get; set; }
public ObjectA(int id, string name)
{
Id = id;
Name = name;
}
}
There are two ways for me to generate List<IObjectA> from some other objects
First one is using forloop:
IList<IObjectA> list = new List<IObjectA>();
foreach(var item in someList)
{
list.Add(new ObjectA(item.Id, item.Name));
}
This works perfectly fine.
Then I tried with linq
IList<IObjectA> list = someList.Select(c => new ObjectA(c.Id, c.Name)).ToList();
The compiler will throw me a error basically saying cannot convert ObjectA to IObjectA
To make it work, i have to add
IList<IObjectA> list = someList.Select(c => new ObjectA(c.Id, c.Name)).Cast<IObjectA>().ToList();
Can some one explain why the compile would complain?
Thanks in advance!
The problem is that the linq expressions result in a List<ObjectA>. If you can treat this result as a List<IObjectA>, the compiler might let you add hypothetical OtherObjectA objects to the list, which would blow up on you if you ever tried to cast back to the original List<ObjectA> type, which should be allowed.
To get around this, you can .Cast() the elements before calling .ToList() to get a list of the correct type:
IList<IObjectA> list = someList.Select(c => new ObjectA(c.Id, c.Name)).Cast<IObjectA>().ToList();
You could also use the var keyword:
var list = someList.Select(c => new ObjectA(c.Id, c.Name)).ToList();
But this will still result in a List<ObjectA> and I suspect you need the List<IObjectA> for code further on.

Exclude a collection from another by lambda

This is my type:
public class myType
{
public int Id { get; set; }
public string name { get; set; }
}
And there is 2 collection of this type:
List<myType> FristList= //fill ;
List<myType> Excludelist= //fill;
And I need to exclude Excludelist from FristList something like the following:
List<myType> targetList =
FirstList.Where(m=>m.Id not in (Excludelist.Select(t=>t.Id));
What is your suggestion about the exact lambda expression of the above query?
Three options. One without any changes:
var excludeIds = new HashSet<int>(excludeList.Select(x => x.Id));
var targetList = firstList.Where(x => !excludeIds.Contains(x.Id)).ToList();
Alternatively, either override Equals and GetHashCode and use:
var targetList = firstList.Except(excludeList).ToList();
Or write an IEqualityComparer<MyType> which compares by IDs, and use:
var targetList = firstList.Except(excludeList, comparer).ToList();
The second and third options are definitely nicer IMO, particularly if you need to do this sort of work in various places.

Categories