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.
Related
I have a list of the following class
class testdata {
public string code { get; set; }
public string[] values { get; set; }
}
how to perform join such that I get list of code and values, i.e values will repeat with every code, e.g if there are two items in list like
1."code1",{"value1","value2"}
2."code2",{"value1","value2","value3"}
I want the final to be in format like
Code1,Value1
code1,Value2
Code2,value1
code3,value2
code3,value3
I want to achieve it via LINQ and anonymous class, I am able to do it with loop and by creating a class as per final structure.
myList.SelectMany(td => td.values.Select(v => new { td.code, v } ))
SelectMany says "take many IEnumerables and concatenate their elements into a single IEnumerable"
td is a single element in your list. It is a single instance of testdata
So now you can do a Select over td.values to create the individual anonymous objects. Select will return an IEnumerable, but wrapped inside a SelectMany, many instances ofIEnumerable<anonymous object> will be flattened into a single IEnumerable<anonymous object>
Try this
List<TestData> testData = new List<TestData>() {
new TestData() { code = "Code1", values = new string[] {"Value1","Value2"}},
new TestData() { code = "Code2", values = new string[] {"value1"}},
new TestData() { code = "code3", values = new string[] {"value2","value3"}}
};
var results = testData.Select(x => x.values.Select(y => new { code = x.code, value = y })).SelectMany(y => y).ToList();
I have a situation where I need to iterate through a collection and add another collection to one of its member using Linq.
For example I have this class
public class Product
{
public string Car { get; set; }
public IEnumerable<Part> Part { get; set; }
}
This class would be within a collection like
IEnumerable<Product> ProductList
How can I populate the Part-property for each Product using GetPartData() with Linq
private IEnumerable<IEnumerable<Part>> GetPartData()
{
return new List<List<Part>>() {
new List<Part>{
new Part(){PartType="11",PartValue=1},
new Part(){PartType="12",PartValue=2}
},
new List<Part>{
new Part(){PartType="21",PartValue=1},
new Part(){PartType="22",PartValue=2}
}
};
}
So ultimately, my ProductList[0].Part should be equal to GetPartData()[0]
If both sequences should be linked via index you can use Enumerable.Zip:
ProductList = ProductList.Zip(GetPartData()
, (product, part) => new Product
{
Car = product.Car,
Part = part
})
.ToList();
Basically, you need to enumerate two IEnumerable at a time to match items from both. The ProductList and the result of GetPartData.
// The two IEnumerable
var products = ProductList;
var parts = GetPartData();
foreach((product, part) in (products, parts)) // will not work :(
{
product.Part = part;
}
Solutions has been debated before.
The Zip method will do it.
// The two IEnumerable
var products = ProductList;
var parts = GetPartData();
products.Zip(parts, (product, part) => product.Part = part).ToList();
The ToList() is really important, to force the execution.
If you are not comfortable with the lambda, you can do it like this:
// The two IEnumerable
var products = ProductList;
var parts = GetPartData();
products.Zip(parts, ProductPartAssociation).ToList();
...
Product ProductPartAssociation(Product product, IEnumerable<Part> part)
{
product.Part = part;
return product; // Actually not used.
}
The result of the Zip is an IEnumerable of whatever the ProductPartAssociation function return. You don't care about it, because what you need is just to be sure that the ProductPartAssociation is executed.
Consider this,
class Item
{
public string ID { get; set;}
public string Description { get; set; }
}
class SaleItem
{
public string ID { get; set;}
public string Discount { get; set; }
}
var itemsToRemoved = (List<Item>)ViewState["ItemsToRemove"];
// get only rows of ID
var query = from i in itemsToRemoved select i.ID;
var saleItems= (List<SaleItem>)ViewState["SaleItems"];
foreach (string s in query.ToArray())
{
saleItems.RemoveItem(s);
}
How can I write this LINQ phrase using IEnumerable/List Extension methods
// get only rows of ID
var query = from i in items select i.ID;
thanks in advance.
That one's easy:
var query = items.Select(i => i.ID);
A select clause always corresponds to a call to Select. Some of the other operators end up with a rather more complex expansion :) If you work hard, you can get the compiler to do some very odd stuff...
You can find all the details of this and other query expression translations in section 7.16 of the C# specification (v3 or v4).
<plug>
You could also buy C# in Depth, 2nd edition and read chapter 11 if you really wanted to :)</plug>
You can use this:
var query = items.Select(i => i.ID);
A couple of other points:
Here you don't need the call to ToArray:
foreach (string s in query.ToArray())
Also if your list is large and you are removing a lot of items you may want to use List.RemoveAll instead of iterating. Every time you remove an item from a list all the other items after it have to be moved to fill the gap. If you use RemoveAll this only has to be done once at the end, instead of once for every removed item.
List<Item> itemsToRemove = (List<Item>)ViewState["ItemsToRemove"];
HashSet<string> itemIds = new HashSet<string>(itemsToRemove.Select(s => s.ID));
saleItems.RemoveAll(c => itemIds.Contains(c.ID));
public static class ItemCollectionExtensions
{
public static IEnumerable<int> GetItemIds(this List<Item> list)
{
return list.Select(i => i.ID);
}
}
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));
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;