I have two custom lists.
public class Model1
{
public string EmpGuid { get; set; }
public string EmployeeName { get; set; }
public DateTime Birthdate { get; set; }
}
public class Model2
{
public string EmpGuid { get; set; }
public string EmployeeName { get; set; }
public DateTime Anniversary{ get; set; }
}
Now I create 2 lists from these classes like so :-
var List1 = new List<Model1>();
var List2 = new List<Model2>();
In my Model classes the values for EmpGuid is unique and is the same in both lists. Now I want to create one single class with the following properties
public class Model3
{
public string EmpGuid { get; set; }
public string EmployeeName { get; set; }
public DateTime Birthdate { get; set; }
public DateTime Anniversary{ get; set; }
}
Here I want to combine both the list ie. List1 and List 2 grouping them with the EmpGuid such that I have a single List of type List where I have the Birthday and anniversary for each EmpGuid.
The final output should be :-
EDIT
List 1 - EmpGuid - abc
EmpName - emp1
Birthday - something
EmpGuid - xyz
EmpName - emp2
Birthday - something2
List 2 - EmpGuid - abc
EmpName - emp1
Anniversary - somethingAnniversary
EmpGuid - mno
empName - emp3
Anniversary - somethingAnniversary3
List3 - EmpGuid - abc
EmpName - emp1
Birthday - something
Anniversary - somethingAnniversary
EmpGuid - xyz
EmpName - emp2
Birthday - something2
Anniversary - null
EmpGuid - mno
EmpName - emp3
Birthday - null
Anniversary - somethingAnniversary3
How can I achieve this?
Any help is appreciated
You can do this by using LINQ's Join method:
var List3 = List1.Join(List2, l1 => l1.EmpGuid, l2 => l2.EmpGuid,
(l1, l2) => new Model3 {
EmpGuid = l1.EmpGuid,
EmployeeName = l1.EmployeeName,
BirthDate = l1.BirthDate,
Anniversary = l2.Anniversary}).ToList();
This combines the two lists by comparing the EmpGuids and creates for each combination a new Model3 instance.
One problem is that you said the EmployeeName can be different, so you have to decide which one you take or how you combine them.
Unfortunatly there is no full outer join in LINQ, so you will need to this a little more manually. One possibility is to add the missing elements after the above query like this:
List3.AddRange(List1.Where(l1 => List2.All(l2 => l1.EmpGuid != l2.EmpGuid)
.Select(l1 => new Model3 {
EmpGuid = l1.EmpGuid,
EmployeeName = l1.EmployeeName,
BirthDate = l1.BirthDate,
Anniversary = null}));
and the same the other way around:
List3.AddRange(List2.Where(l2 => List1.All(l1 => l1.EmpGuid != l2.EmpGuid)
.Select(l2 => new Model3 {
EmpGuid = l2.EmpGuid,
EmployeeName = l2.EmployeeName,
Anniversary = l2.Anniversary}));
This takes all elements from the one list with GUIDs that don't appear in the second list and creates the appropriate Model3 instances. (Note that DateTime is a value type and cannot be null, by default it's DateTime.MinValue).
You will make a join on both lists, similar to SQL:
var result = from m1 in list1
join m2 in list2 on m1.EmpGuid == m2.EmpGuid
select new Model3 { EmpGuid = m1.EmpGuid, EmployeeeName = m1.EmployeeName ...};
However this will allways use the EmployseeName from Model1. If you want to select both names you can use this approach;
var result = from m1 in list1
join m2 in list2 on m1.EmpGuid == m2.EmpGuid
select new Model3
{
EmpGuid = m1.EmpGuid,
EmployeeeName1 = m1.EmployeeName,
EmployeeeName2 = m2.EmployeeName,
...
};
Also have a look at the MSDN.
Related
I have 3 tables and I'm trying to get a combined result with a sum of one field of them.
I'm working with C#, .NET, Entity Framework 7 and SQL Server.
I need to get the city's Name of each result, but I store the idCity
Brand table:
public byte IdBrand { get; set; }
public string Name { get; set; } = null!;
Bundles table:
public int IdBundle { get; set; }
public short IdCity{ get; set; }
public short IdBrand { get; set; }
public decimal? Volume { get; set; }
Cities:
public short IdCity { get; set; }
public string Name { get; set; } = null!;
I've tried this linq query and got almost the result I want but the city field is failing and I got stuck...
var volume = context.Bundles
.GroupBy(city => city.IdCity)
.Select(cad => new
{
CITY = context.Cities.Local.ToList().ElementAt(cad.Key)!.Name,
BRAND1 = cad.Where(c => c.IdBrand == 1).Sum(c => c.Volume),
BRAND2 = cad.Where(c => c.IdBrand == 19).Sum(c => c.Volume)
}).ToList();
I get this result that I expect but the CITY is not correct, I think because the cad.Key is not the same than Cities Index
I also tried:
context.Cities.ToList()
.Where(i => context.Bundles.Any(a=> i.IdCity == a.IdCity))
.Select(x=> x.Name)
CITY
BRAND1
BRAND2
LONDON
10.2
12
MOSCOU
11.4
1
PARIS
9.1
0.4
I guess that the cad.Key is not what I need to use to get the ElementAt Cities but how can I get the city .Name from another table in the Select? Or what is the best way to perform this query?
Try the following query, it should have better performance:
var query =
from b in context.Bundles
group b by b.IdCity into g
select new
{
IdCity = g.Key,
BRAND1 = g.Sum(c => c.IdBrand == 1 ? c.Volume : 0),
BRAND2 = g.Sum(c => c.IdBrand == 19 ? c.Volume : 0)
} into agg
join city in context.Cities on agg.IdCity equals city.Id
select new
{
CITY = city.Name,
BRAND1 = agg.BRAND1,
BRAND2 = agg.BRAND2
};
I have list of objects which returned from database, sometimes it may have even 25k to 50k records.
I am grouping them right now with one order to make the group of corresponding items.
But also I need to take the max version of the objects in that list for each grouped list where so many duplicate versions also will be available.
public class Items
{
public int ID { get; set; }
public string ProductName { get; set; }
public int ProductId { get; set; }
public int Version { get; set; }
}
public class ItemInfo : ReleaseInfo
{
public virtual IGrouping<int,Items> ItemList { get; set; }
}
For example,
var groupedList = (from cn in ItemList
group cn by cn.ProductId into itemGroup
orderby itemGroup.Key ascending
select new ItemInfo
{
cn.ProductId = itemGroup.Key,
ProductName = itemGroup.FirstOrDefault().ProductName,
ItemList= itemGroup
});
This Itemlist is returned from DB and columns are below,
ID, Name, ProductName, ProductId and Version
So now we need to check with same ID and ProductId is there any items duplicated for each groupedList, if then we need to take max value of that using the Version column and keep only that in the list.
Example dataset:
First Grouped List:
**ROW 1** ID - 1, Product ID - 1 Version 1
**ROW 2** ID - 1, Product ID - 1 Version 2
**ROW 3** ID - 1, Product ID - 1 Version 3
Second Grouped List:
**ROW 1** ID - 1, Product ID - 2 Version 1
**ROW 2** ID - 2, Product ID - 2 Version 2
from the above data it need to pass ID-1, product ID- 1, Version 3 from First list and second row from second list to the next steps.
How we can do the group by, If it is one single entity I can do orderbydescending and take the first element but i need to loop through the list of items where other entities also available.
I'm giving this answer as per my understanding, If I miss anything or miss understood your requirement please comment, I'll update the answer.
Assuming your classes are as below:
public class Items
{
public int ID { get; set; }
public string ProductName { get; set; }
public int ProductId { get; set; }
public int Version { get; set; }
}
public class ItemInfo
{
public virtual IEnumerable<IGrouping<int, Items>> ItemList { get; set; }
}
LINQ:
List<Items> itemLists = new List<Items>();
var filteredItems = itemLists.Where(x => x.Version == itemLists.Where(y => y.ProductId == x.ProductId).Max(z => z.Version));
var itemsInfo = new ItemInfo { ItemList = filteredItems.GroupBy(x => x.ProductId) };
I got this application that reads the employee data from the database, only problem the boss of an employee is set in employee_id and not in name, which is what I am looking for.
I got a List filled with all the employees. So I am looking for a way to query it with LINQ through the list to link the employee_id to the Firstname/Lastname.
Example: The employee with the ID 1 is Nancy and her boss is Andrew, but it doesn't say Andrew it says 2. But it should say Andrew
I also added 2 images below to add to my explanation.
Listview of the employees
Reading out the list of employees into the ListView
First, load the employees into some local variable (you'll need it later):
List<Employee> employees = Database.getEmployees();
Then edit your foreach cycle:
// 'employee' is better because here it is really just one specific employee
foreach (Employee employee in employees)
Now you can get the name of the boss like this (in foreach cycle):
string boss = employees.FirstOrDefault(x => x.ID == employee.ReportsTo)?.FirstName;
(You need at least C# 6.0 for the ? operator.)
So you need to Left Join ID with Boss and get the Boss Info if found:
var employees = Database.getEmployees();
var employeesWithBoss = (from e in employees
join b in employees
on e.ID equals b.Boss into leftJoin
from boss in leftJoin.DefaultIfEmpty()
select new
{
Employee = e,
BossFirstName = boss == null ? null : boss.FirstName,
BossLastName = boss == null ? null : boss.LastName
}).ToList();
foreach (var employee in employeesWithBoss)
{
// do your normal work here, you now
// have employee.BossFirstName and employee.BossLastName
}
you can use lambda to achieve this.
class Program
{
static void Main(string[] args)
{
List<Employee> employees = new List<Employee>();
employees.Add(new Employee() { EmployeeName = "Nancy", EmployeeId = 1, BossId = 2 });
employees.Add(new Employee() { EmployeeName = "Andrew", EmployeeId = 2, BossId = 0 });
employees.Add(new Employee() { EmployeeName = "Janet", EmployeeId = 1, BossId = 2 });
var employeesWithBossName = employees.Join(employees,
emp1 => emp1.BossId,
emp2 => emp2.EmployeeId,
(emp1, emp2) => new { EmployeeName = emp1.EmployeeName, BossName = emp2.EmployeeName });
foreach (var item in employeesWithBossName)
{
Console.WriteLine("{0} {1}", item.EmployeeName, item.BossName);
}
Console.Read();
}
}
public class Employee
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public int BossId { get; set; }
}
Hope this will help.
I'd wish to create a list of the following class (List):
public class StatusData
{
public DateTime? Date { get; set; }
public int RegisteredUsers { get; set; }
public int Orders { get; set; }
public decimal Import { get; set; }
}
This list should be filled with with information of another 2 lists: a List<Contact> and a List<Purchase>. Both classes have the following shape:
public class Contact
{
public DateTime? RegisterDate { get; set; }
public string Name { get; set; }
}
public class Purchase
{
public DateTime? PurchaseDate { get; set; }
public int PurchaseID {get;set;}
public int Import { get; set; }
}
The idea is that the List should, for every day, which is the amount of Registered users, the amount of purchases and the total import of these purchases. So, in essence, both just share the Date.
However, I can't come with a good LINQ expression to create the List. One solution I considered is to retrieve all the distinct dates among the 2 lists and then iterate over them to retrieve the information I want, but I think this approach is rather "ugly". Is there any way to do what I want to do easily?
EDIT: An example of what I want would be the following:
Contacts
--------
Name RegisteredDate
David 10/10/2013
Paul 09/10/2013
Gina 10/10/2013
Roger 09/10/2013
Rose 05/10/2013
Jean 07/10/2013
Mark 04/10/2013
Lisa 04/10/2013
Purchases
-----------
ID PurchaseDate Import
1 10/10/2013 10
2 10/10/2013 10
3 10/10/2013 20
4 04/10/2013 15
5 04/10/2013 15
6 07/10/2013 20
7 07/10/2013 2
8 07/10/2013 2
Expected result
----------------
Date RegisteredUsers Purchases Import
04/10/2013 2 2 30
05/10/2013 1 0 0
07/10/2013 1 3 24
09/10/2013 2 0 0
10/10/2013 2 3 40
Kind regards
var contacts = new List<Contact>();
var purchases = new List<Purchase>();
var dates = contacts.Select(x => x.RegisterDate.Value)
.Concat(purchases.Select(x => x.PurchaseDate.Value))
.Distinct();
var data = from date in dates
join c in contacts on date equals c.RegisterDate.Value into registered
join p in purchases on date equals p.PurchaseDate.Value into purchased
select new StatusData {
Date = date,
RegisteredUsers = registered.Count(),
Orders = purchases.Count(),
Import = purchases.Sum(x => x.Import)
};
It will return StatusData for all days, when at least one registration OR one purchase was made.
So there are several separate tasks here. The first thing that you're doing is grouping your contacts by the registration date. Next, you're joining that result to the Purchases table based on the date, aggregating count/sums on two different columns. LINQ has a method for each of these two operations:
var query = from contact in contacts
group contact by contact.RegisterDate into contactGroup
join purchase in purchases
on contactGroup.Key equals purchase.PurchaseDate into purchaseGroup
select new StatusData
{
Date = contactGroup.Key,
RegisteredUsers = contactGroup.Count(),
Orders = purchaseGroup.Count(),
Import = purchaseGroup.Sum(p => p.Import),
};
This is really two separate queries with their results merged at the end.
var contact = from c in Contact
group c by c.RegisterDate into c
select new StatusData
{
Date = c.Key,
RegisteredUsers = c.Count(),
Orders = 0,
Import = 0,
};
var purchases = from p in Purchases
group p by p.PurchaseDate into p
select new StatusData
{
Date = p.Key,
RegisteredUsers = 0,
Orders = p.Count(),
Import = p.Sum(x => x.Import),
};
var final = from x in contact.Concat(purchases)
group x by x.Date into x
select new StatusData
{
Date = x.Key,
RegisteredUsers = x.Sum(y => y.RegisteredUsers),
Orders = x.Sum(y => y.Orders),
Import = x.Sum(y => y.Import),
};
I am new to LINQ and sorry if my question have been asked
I have 2 classes
public class Person
{
int ID {get;set;}
string FirstName {get;set;}
string LastName {get;set;}
}
and
public class House
{
int ID {get;set;}
string Address {get;set;}
string ZipCode {get;set;}
int PersonId {get;set;}
}
I am saving the list of houses in a IEnumerable List
IEnumerable<House> ListHouses = GetAllHouses();
GetAllHouses return the list of houses from the database
I want to use Lamda select in LINQ in order to do the following
var st = ListHouses .Select(h => new
{
id = h.ID,
Address= h.Address,
Zip= h.ZipCode ,
PersonFirstName = GetPersonByID(h.PersonId ).FirstName,
PersonLastname = GetPersonByID(h.PersonId ).lastname
});
Where GetPersonByID returns an object of Type Person that has the given ID. and then I take his first name and last name.
My question is this:
Instead of Getting the Person 2 times for the variables (personFirstName and PersonLastName) Is there a way I can get it one time and then used it. Something like
PersonForId = GetPersonByID(h.PersonId)
PersonFirstName = PersonLastName.FirstName,
PersonLastname = PersonLastName.lastname
I'm looking for something similar to Join in SQL where you join a value from another table.
Thanks you very much for any help
You're extremely close! Using your code (and making all properties on House and Person public), here is a method using the LINQ Join method:
var st = GetAllHouses().Join(GetAllPersons(),
outerKey => outerKey.PersonId,
innerKey => innerKey.ID,
(house, person) => new
{
house.ID,
house.Address,
house.ZipCode,
PersonFirstName = person.FirstName,
PersonLastname = person.LastName
});
Note: I would recommend the GetAllPersons() and the GetAllHouses() methods return IQueryable rather than IEnumerable. Doing so will build the expression (including the join), which means LINQ-to-SQL (or Entities) will build a proper SQL statement with the JOIN included, instead of enumerating the collections and then joining.
Additional information on such can be found here: Returning IEnumerable<T> vs. IQueryable<T>
using System;
using System.Linq;
class Customer
{
public int ID { get; set; }
public string Name { get; set; }
}
class Order
{
public int ID { get; set; }
public string Product { get; set; }
}
class Program
{
static void Main()
{
// Example customers.
var customers = new Customer[]
{
new Customer{ID = 5, Name = "Sam"},
new Customer{ID = 6, Name = "Dave"},
new Customer{ID = 7, Name = "Julia"},
new Customer{ID = 8, Name = "Sue"}
};
// Example orders.
var orders = new Order[]
{
new Order{ID = 5, Product = "Book"},
new Order{ID = 6, Product = "Game"},
new Order{ID = 7, Product = "Computer"},
new Order{ID = 8, Product = "Shirt"}
};
// Join on the ID properties.
var query = from c in customers
join o in orders on c.ID equals o.ID
select new { c.Name, o.Product };
// Display joined groups.
foreach (var group in query)
{
Console.WriteLine("{0} bought {1}", group.Name, group.Product);
}
}
}
Output
Sam bought Book
Dave bought Game
Julia bought Computer
Sue bought Shirt