I'm having a List<Department>, in that it has Department Name, List of Employees Name and OrderBy Dicrection for List. Now I need to construct a Dictionary<string, ObservableCollection<Employee>>, the KEY is a Department Name and the Value is an ObservableCollection by using List<Department>.
Expectation: I need to Sort the EmpName within the ObservableCollection<Employee> which is present in Dictionary<string, ObservableCollection<Employee>> EmpList based on sortEmpName Property which is in List<Department> DepList using
LINQ
I written the LINQ for this in the below Main() Function.
void Main()
{
List<Department> DepList = new List<Department>()
{
new Department() {
DepName = "HR",
sortEmpName = FilterListSortDirection.SortDirection.Ascending,
EmpName = new List<string>() {"Raj", "Baba"}
},
new Department() {
DepName = "iLab",
sortEmpName = FilterListSortDirection.SortDirection.Descending,
EmpName = new List<string>() {"Emma", "Kaliya"}
},
new Department() {
DepName = "Testing",
sortEmpName = FilterListSortDirection.SortDirection.None,
EmpName = new List<string>() {"Supriya", "Billa"}
}
};
Dictionary<string, ObservableCollection<Employee>> EmpList = DepList.Select(m =>
new {
Dep = m.DepName,
EmpCollection = new ObservableCollection<Employee>(
m.EmpName.Select(k =>
new Employee() { EmpName = k, IsChecked = true }).ToList())
}
).ToDictionary(x => x.Dep, x => x.EmpCollection);
}
The Model Classes are
public class Employee
{
public string EmpName { get; set; }
public bool IsChecked { get; set; }
}
public class Department
{
public string DepName { get; set; }
public FilterListSortDirection.SortDirection sortEmpName { get; set; }
public List<string> EmpName { get; set; }
}
public class FilterListSortDirection
{
public enum SortDirection
{
None,
Ascending,
Descending
}
}
The Output Screen Shot of List<Department> DepList
The Output Screen Shot of Dictionary<string, ObservableCollection<Employee>> EmpList
Expectation: I need to Sort the EmpName within the ObservableCollection<Employee> which is present in Dictionary<string, ObservableCollection<Employee>> EmpList based on sortEmpName Property which is in List<Department> DepList using
LINQ
This is a small part of a complex LINQ query in my project, so, I need to achieve this in LINQ. Kindly assist me.
HR => List of Employee Names should be in Ascending
iLab => List of Employee Names should be in Descending
Testing => List of Employee Names should be in original order - None (i.e., No Change - Don't Sort)
I guess you need something like this:
var EmpList = DepList.ToDictionary(p => p.DepName, p =>
{
var empList = p.EmpName.Select(k => new Employee() { EmpName = k, IsChecked = true });
if (p.sortEmpName == FilterListSortDirection.SortDirection.Ascending)
{
empList = empList.OrderBy(q => q.EmpName);
}
else if (p.sortEmpName == FilterListSortDirection.SortDirection.Descending)
{
empList = empList.OrderByDescending(q => q.EmpName);
}
return new ObservableCollection<Employee>(empList.ToList());
});
use OrderBy
Dictionary<string, ObservableCollection<Employee>> EmpList = DepList.Select(m => new
{
Dep = m.DepName,
EmpCollection =
m.sortEmpName == SortDirection.Ascending ?
new ObservableCollection<Employee>(m.EmpName.Select(k => new Employee {EmpName = k, IsChecked = true}).ToList().OrderBy(emp => emp.EmpName)) :
m.sortEmpName == SortDirection.Descending ?
new ObservableCollection<Employee>(m.EmpName.Select(k => new Employee { EmpName = k, IsChecked = true }).ToList().OrderByDescending(emp => emp.EmpName)) :
new ObservableCollection<Employee>(m.EmpName.Select(k => new Employee { EmpName = k, IsChecked = true }).ToList())
}).ToDictionary(x => x.Dep, x => x.EmpCollection);
Related
Supposed that I have these classes
public class Subject
{
public int Id { get; set; }
public string Category { get; set; }
public string Type { get; set; }
}
public class Student
{
public int Id { get; set; }
public List<MySubject> MySubjects { get; set; }
}
public class MySubject
{
public int Id { get; set; }
public string Category { get; set; }
public string Type { get; set; }
public string Schedule { get; set; }
public string RoomNumber { get; set; }
}
sample data
var subjects = new List<Subject>()
{
new Subject(){ Id = 1, Category = "Mathematics", Type = "Algebra" },
new Subject(){ Id = 2, Category = "Computer Science", Type = "Pascal" }
};
var student = new Student()
{ Id = 1, MySubjects = new List<MySubject>() {
new MySubject() {Id = 1, Category = "Mathematics", Type = "Algebra" },
new MySubject() {Id = 3, Category = "Mathematics", Type = "Trigonometry"},
}
};
//TODO: Update list here
student.MySubjects.ForEach(i => Console.WriteLine("{0}-{1}-{2}\t", i.Id, i.Category, i.Type));
the above line of code returns
1-Mathematics-Algebra
3-Mathematics-Trigonometry
which is incorrect. I need to return this
1-Mathematics-Algebra
2-Computer Science-Pascal
Basically I would like to modify and iterate the student.MySubjects and check its contents against subjects.
I would like to remove the subjects (3-Mathematics-Trigonometry) that are not present in the subjects and also ADD subjects that are missing (2-Computer Science-Pascal).
Can you suggest an efficient way to do this by searching/comparing using Category + Type?
Try like below.
// Remove those subjects which are not present in subjects list
student.MySubjects.RemoveAll(x => !subjects.Any(y => y.Category == x.Category && y.Type == x.Type));
// Retrieve list of subjects which are not added in students.MySubjects
var mySubjectsToAdd = subjects.Where(x => !student.MySubjects.Any(y => y.Category == x.Category && y.Type == x.Type))
.Select(x => new MySubject() {
Id = x.Id,
Category = x.Category,
Type = x.Type
}).ToList();
// If mySubjectsToAdd has any value then add it into student.MySubjects
if (mySubjectsToAdd.Any())
{
student.MySubjects.AddRange(mySubjectsToAdd);
}
student.MySubjects.ForEach(i => Console.WriteLine("{0}-{1}-{2}\t", i.Id, i.Category, i.Type));
// make an inner join based on mutual values to filter out wrong subjects.
var filteredList =
from mySubject in student.MySubjects
join subject in subjects
on new { mySubject.Category, mySubject.Type }
equals new { subject.Category, subject.Type }
select new MySubject { Id = mySubject.Id, Category = mySubject.Category, Type = mySubject.Type };
// make a left outer join to find absent subjects.
var absentList =
from subject in subjects
join mySubject in filteredList
on new { subject.Category, subject.Type }
equals new { mySubject.Category, mySubject.Type } into sm
from s in sm.DefaultIfEmpty()
where s == null
select new MySubject { Id = subject.Id, Category = subject.Category, Type = subject.Type };
student.MySubjects = filteredList.ToList();
student.MySubjects.AddRange(absentList.ToList());
I have a class like
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Now I have a list of this class: List<Person> persons;
var persons = new List<Person> {
new Person { Id = 1, LastName = "Reza", FirstName="Jenabi" },
new Person { Id = 1, LastName = "Amin", FirstName="Golmahalle"},
new Person { Id = 2, LastName = "Hamed", FirstName="Naeemaei"}
};
Is there a way I can group by Id and get the list of all the full Name (Combine first and last names)?
So after grouping:
var Id = results[0].Id; // Output : 1
List<string> fullNames = results[0].FullNames; // Output : "Reza Jenabi","Amin Golmahalle"
I believe this is what you need:
var results = persons.GroupBy(x => x.Id)
.Select(x => new { Id = x.Key, FullNames = x.Select(p => $"{p.FirstName} {p.LastName}").ToList() })
.ToList();
I think bellow code can help you:
var fff = from p in persons
group $"{p.FirstName} {p.LastName}" by p.Id into g
select new { PersonId = g.Key, FullNames = g.ToList() };
yeah, you can use GroupBy and Join those items:
var grouped = persons.GroupBy(p => p.Id)
.Select(s => string.Join(", ", s.Select(a=> $"{a.FirstName} {a.LastName}")));
I have a model (sample below), and sample data with desired output..
I have also given two ways to populate model which will give desired output but for somereason,
looks like I am missing something....
Not able to figure out what is the issue here...
need inputs in correcting approach 1 or approach 2 or you can also suggest any other approach which will help populate model in below response..
models
class Emp
{
public int id {get;set;}
public int Name {get;set;}
public List<cardType> cardTypes {get;set;}
}
class cardType
{
public int name {get;set;}
public DateTime Expiry {get;set;}
}
sample data (Data is returned in 1 table only)
id Name cardTypeName exp
1 a Amex 1010
1 a City 2010
desired output
<Emp>
<ID>1</id>
<name>1</name>
<cardTypes>
<cardType>
<Name> Amex </Name>
<exp> 1010 </exp>
</cardType>
<cardType>
<Name> City </Name>
<exp> 2010 </exp>
</cardType>
<cardTypes>
</Emp>
approach 1
(dataTable.AsEnumerable()
.GroupBy(r => r.ItemArray[1])
.Select(grp => new Emp()
{
id = r.itemarray[1],
name = r.itemarray[1],
cardTypes = grp.Select(t => new cardType()
{
field 1,
field 2
}).ToList()
}));
approach 2
return (from DataRow dr in dataTable.Rows
select new Emp
{
id = "",
name= "",
cardTypes = dataTable.AsEnumerable()
.Select(x => new cardType
{
name = ""
exp = ""
}).ToList(),
});
try this
return dataTable.AsEnumerable()
.GroupBy(x => x.Id)
.Select(x => new
{
id = x.Key,
name = x.Key,
cardTypes = x.Select(t => new
{
name = t.Name,
exp = t.Exp
})
});
This should work for you considering you will change the Expiry field in cardType to int
var results =
dt.AsEnumerable()
.GroupBy(row => row.ItemArray[0])
.Select(rows => new Emp
{
id = rows.Key,
name = rows.Key,
cardTypes = rows.Select(row => new cardType {Name = row.ItemArray[2], Expiry = row.ItemArray[3]})
});
If you desire to have DateTime, then decide how to convert. For example:
cardTypes = rows.Select(row => new cardType {Name = row.ItemArray[2], Expiry = new DateTime(row.ItemArray[3],1,1)})
If you want to serialize to xml your models, you should mark your models with [Serializable] attribute and just use XmlSerializer.
[Serializable]
class Emp
{
public int id {get;set;}
public int Name {get;set;}
public List<cardType> cardTypes {get;set;}
}
[Serializable]
class cardType
{
public int name {get;set;}
public DateTime Expiry {get;set;}
}
After that you can use following code:
static Dictionary<int, Emp> ConvertToDict(DataTable dt)
{
Dictionary<int, Emp> emps = new Dictionary<int, Emp>();
foreach (var dr in dt.AsEnumerable())
{
var id = (int)dr["ID"];
var name = (string)dr["Name"];
var cardTypeName = (string)dr["CardTypeName"];
var exp = (int)dr["Exp"];
Emp emp;
var cardType = new CardType { Name = cardTypeName, Exp = exp };
if (emps.TryGetValue(id, out emp))
{
emp.CardTypes.Add(cardType);
}
else
{
emps.Add(id, new Emp { ID = id, Name = name, CardTypes = new List<CardType> { cardType } });
}
}
return emps;
}
static List<string> Serialize<T>(IEnumerable<T> entities) where T:new()
{
var ser = new XmlSerializer(typeof(T));
var serializedEntities = entities.Select(entity =>
{
using (var sw = new StringWriter())
{
ser.Serialize(sw, entity);
return sw.ToString();
}
}).ToList();
return serializedEntities;
}
I have 3 classes that are defined like this:
class Customer
{
public string Name;
public string City;
public Order[] Orders;
}
class Order
{
public int Quantity;
public Product Product;
}
class Product
{
public string ProdName;
public decimal Price;
}
And I want to use LINQ in C# to print out the names that bought a specific product which in this case is 'ProdName'. I can't find a solution in order to go through all these 3 classes that could give me the name based on the product name.
I have tried something like this but it seems it doesn;t work:
var query = from c in customers where c.Order[0].Product.ProdName.Contains("Milk")
select c.Name;
foreach(var item in query)
{
Console.WriteLine(item);
}
This is how I set up the values for each class:
static public List<Customer> GetCustomerList()
{
var customerList = new List<Customer>
{
new Customer {Name = "Johny", City = "London", Orders = new Order[3] },
new Customer {Name = "Morgan", City = "Copenhagen", Orders = new Order[4]},
new Customer {Name = "Rasmus", City = "Amsterdam", Orders = new Order[1] }
};
return customerList;
}
static public List<Order> GetOrderList()
{
var orderList = new List<Order>
{
new Order { Quantity = 10, Product = new Product()},
new Order { Quantity = 5, Product = new Product()},
new Order { Quantity = 2, Product = new Product()}
};
return orderList;
}
static public List<Product> GetProductList()
{
var productList = new List<Product>
{
new Product { Name = "Cookie, bread", Price = 50 },
new Product { Name = "Cookie, Bread, Milk", Price = 85},
new Product { Name = "bags", Price = 38}
};
return productList;
}
static void Main(string[] args)
{
List<Customer> customers = GetCustomerList();
List<Order> orders = GetOrderList();
List<Product> products = GetProductList();
}
How can I linq all 3 classes together in order to get right result? any hints, please?
You need to build real, related, test data. Some usable fake data setup might look like:
// Create a single instance of each Product that could be used
var egg = new Product { Name = "Eggs", Price = 2.0 };
var bread = new Product { Name = "Bread", Price = 3.0 };
var fooBars = new Product { Name = "FooBars", Price = 2.5 };
var customerList = new List<Customer>
{
new Customer { Name = "Johny", City = "London", Orders = new List<Order>
{
new Order { Quantity = 3, Product = bread },
new Order { Quantity = 1, Product = egg },
new Order { Quantity = 2, Product = fooBars }
}},
new Customer { Name = "Morgan", City = "Copenhagen", Orders = new List<Order>
{
new Order { Quantity = 30, Product = bread }
}},
new Customer { Name = "Rasmus", City = "Amsterdam", Orders = new List<Order>
{
new Order { Quantity = 12, Product = fooBars }
}}
};
Please note that I used List<Order> instead of Order[], but you could switch it back. I also opted for a Name property in Product as you showed in your example code, but which doesn't match your class definition.
Now you can query. Let's see who bought bread:
var whoBoughtBread = customerList
.Where(c => c.Orders.Any(o => o.Product == bread))
.Select(c => c.Name);
Or
var whoBoughtBread2 = customerList
.Where(c => c.Orders.Any(o => o.Product.Name == "Bread"))
.Select(c => c.Name);
With the data structure you have one query would be:
customerList.Where(c => c.Orders.Any(o => o.Product.ProdName == prodName));
but as mentioned in the comments you have potentials for both a null collection and null values within the collection. I would highly recommend making sure you have non-null collections with non-null elements to avoid having to inject null-checking into your query. Otherwise you'll have to do sometihng like:
customerList.Where(c => c.Orders != null &&
c.Orders.Any(o => o != null &&
o.Product != null &&
o.Product.ProdName == prodName));
With C# 6's null-propagation operator (?.) you can shorten it a little:
customerList.Where(c => c.Orders != null &&
c.Orders.Any(o => o?.Product != null &&
o.Product.ProdName == prodName));
I have a class Items with properties (Id, Name, Code, Price).
The List of Items is populated with duplicated items.
For ex.:
1 Item1 IT00001 $100
2 Item2 IT00002 $200
3 Item3 IT00003 $150
1 Item1 IT00001 $100
3 Item3 IT00003 $150
How to remove the duplicates in the list using linq?
var distinctItems = items.GroupBy(x => x.Id).Select(y => y.First());
var distinctItems = items.Distinct();
To match on only some of the properties, create a custom equality comparer, e.g.:
class DistinctItemComparer : IEqualityComparer<Item> {
public bool Equals(Item x, Item y) {
return x.Id == y.Id &&
x.Name == y.Name &&
x.Code == y.Code &&
x.Price == y.Price;
}
public int GetHashCode(Item obj) {
return obj.Id.GetHashCode() ^
obj.Name.GetHashCode() ^
obj.Code.GetHashCode() ^
obj.Price.GetHashCode();
}
}
Then use it like this:
var distinctItems = items.Distinct(new DistinctItemComparer());
If there is something that is throwing off your Distinct query, you might want to look at MoreLinq and use the DistinctBy operator and select distinct objects by id.
var distinct = items.DistinctBy( i => i.Id );
This is how I was able to group by with Linq. Hope it helps.
var query = collection.GroupBy(x => x.title).Select(y => y.FirstOrDefault());
An universal extension method:
public static class EnumerableExtensions
{
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> enumerable, Func<T, TKey> keySelector)
{
return enumerable.GroupBy(keySelector).Select(grp => grp.First());
}
}
Example of usage:
var lstDst = lst.DistinctBy(item => item.Key);
You have three option here for removing duplicate item in your List:
Use a a custom equality comparer and then use Distinct(new DistinctItemComparer()) as #Christian Hayter mentioned.
Use GroupBy, but please note in GroupBy you should Group by all of the columns because if you just group by Id it doesn't remove duplicate items always. For example consider the following example:
List<Item> a = new List<Item>
{
new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
new Item {Id = 2, Name = "Item2", Code = "IT00002", Price = 200},
new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
new Item {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}
};
var distinctItems = a.GroupBy(x => x.Id).Select(y => y.First());
The result for this grouping will be:
{Id = 1, Name = "Item1", Code = "IT00001", Price = 100}
{Id = 2, Name = "Item2", Code = "IT00002", Price = 200}
{Id = 3, Name = "Item3", Code = "IT00003", Price = 150}
Which is incorrect because it considers {Id = 3, Name = "Item3", Code = "IT00004", Price = 250} as duplicate. So the correct query would be:
var distinctItems = a.GroupBy(c => new { c.Id , c.Name , c.Code , c.Price})
.Select(c => c.First()).ToList();
3.Override Equal and GetHashCode in item class:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public int Price { get; set; }
public override bool Equals(object obj)
{
if (!(obj is Item))
return false;
Item p = (Item)obj;
return (p.Id == Id && p.Name == Name && p.Code == Code && p.Price == Price);
}
public override int GetHashCode()
{
return String.Format("{0}|{1}|{2}|{3}", Id, Name, Code, Price).GetHashCode();
}
}
Then you can use it like this:
var distinctItems = a.Distinct();
Use Distinct() but keep in mind that it uses the default equality comparer to compare values, so if you want anything beyond that you need to implement your own comparer.
Please see http://msdn.microsoft.com/en-us/library/bb348436.aspx for an example.
Try this extension method out. Hopefully this could help.
public static class DistinctHelper
{
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
var identifiedKeys = new HashSet<TKey>();
return source.Where(element => identifiedKeys.Add(keySelector(element)));
}
}
Usage:
var outputList = sourceList.DistinctBy(x => x.TargetProperty);
List<Employee> employees = new List<Employee>()
{
new Employee{Id =1,Name="AAAAA"}
, new Employee{Id =2,Name="BBBBB"}
, new Employee{Id =3,Name="AAAAA"}
, new Employee{Id =4,Name="CCCCC"}
, new Employee{Id =5,Name="AAAAA"}
};
List<Employee> duplicateEmployees = employees.Except(employees.GroupBy(i => i.Name)
.Select(ss => ss.FirstOrDefault()))
.ToList();
Another workaround, not beautiful buy workable.
I have an XML file with an element called "MEMDES" with two attribute as "GRADE" and "SPD" to record the RAM module information.
There are lot of dupelicate items in SPD.
So here is the code I use to remove the dupelicated items:
IEnumerable<XElement> MList =
from RAMList in PREF.Descendants("MEMDES")
where (string)RAMList.Attribute("GRADE") == "DDR4"
select RAMList;
List<string> sellist = new List<string>();
foreach (var MEMList in MList)
{
sellist.Add((string)MEMList.Attribute("SPD").Value);
}
foreach (string slist in sellist.Distinct())
{
comboBox1.Items.Add(slist);
}
When you don't want to write IEqualityComparer you can try something like following.
class Program
{
private static void Main(string[] args)
{
var items = new List<Item>();
items.Add(new Item {Id = 1, Name = "Item1"});
items.Add(new Item {Id = 2, Name = "Item2"});
items.Add(new Item {Id = 3, Name = "Item3"});
//Duplicate item
items.Add(new Item {Id = 4, Name = "Item4"});
//Duplicate item
items.Add(new Item {Id = 2, Name = "Item2"});
items.Add(new Item {Id = 3, Name = "Item3"});
var res = items.Select(i => new {i.Id, i.Name})
.Distinct().Select(x => new Item {Id = x.Id, Name = x.Name}).ToList();
// now res contains distinct records
}
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}