How To: Joining three lists using Linq to objects - linq

Problem is with the addresses not being outputted
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LinqToObjects
{
class Program
{
static void Main(string[] args)
{
var customers = Customer.GetAllCustomers();
var addresses = Address.GetAllAddresses();
var addressRelations = AddressRelation.GetAllAddressRelations();
var results = customers
.Join(addressRelations,
c => c.CustomerID,
ar => ar.CustomerID,
(c, ar) => new
{
CustomerName = c.FirstName + " " + c.LastName,
CustomerID = c.CustomerID,
AddressRelID = ar.AddressID
});
var resultsJoined = results
.GroupJoin(addresses,
ar => ar.AddressRelID,
a => a.AddressID,
(ar, a) => new
{
CustomerName = ar.CustomerName,
AddressLine = addresses.Select(b => b.StreetAddress).FirstOrDefault()
});
foreach(var item in resultsJoined)
{
Console.WriteLine(item.CustomerName);
Console.WriteLine(item.AddressLine);
Console.WriteLine("-----------------");
}
}
}
public class AddressRelation
{
public int AddressRelationID { get; set; }
public int CustomerID { get; set; }
public int AddressID { get; set; }
public AddressRelation(int id, int customerId, int addressId)
{
AddressRelationID = id; CustomerID = customerId; AddressID = addressId;
}
public static List<AddressRelation> GetAllAddressRelations()
{
var AllAddressRelations = new List<AddressRelation>();//simulate data returned from db
var addressRelation1 = new AddressRelation(1, 1, 1);
var addressRelation2 = new AddressRelation(2, 3, 3);
var addressRelation3 = new AddressRelation(3, 2, 2);
AllAddressRelations.Add(addressRelation1);
AllAddressRelations.Add(addressRelation2);
AllAddressRelations.Add(addressRelation3);
return AllAddressRelations;
}
}
public class Address
{
public int AddressID { get; set; }
public string StreetAddress { get; set; }
public Address(int id, string streetAddress)
{
AddressID = id; StreetAddress = streetAddress;
}
public static List<Address> GetAllAddresses()
{
var AllAddresses = new List<Address>();
Address customer1Address = new Address(1, "Elm St");
Address customer2Address = new Address(2, "Willow Way");
Address customer3Address = new Address(3, "Linq Ln");
AllAddresses.Add(customer1Address);
AllAddresses.Add(customer2Address);
AllAddresses.Add(customer3Address);
return AllAddresses;
}
}
public class Customer
{
public int CustomerID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Customer(int id,string firstName, string lastName)
{
CustomerID = id; FirstName = firstName; LastName = lastName;
}
public static List<Customer> GetAllCustomers()
{
var AllCustomers = new List<Customer>();
var customer1 = new Customer(1, "James", "T");
var customer2 = new Customer(2, "Donnie", "H");
var customer3 = new Customer(3, "Sarah", "H");
AllCustomers.Add(customer1);
AllCustomers.Add(customer2);
AllCustomers.Add(customer3);
return AllCustomers;
}
}
}

The query isn't very expressive. If I was going to join three lists using LinqToObjects, I'd do this:
var query =
from c in customers
join xr in addressRelations on c.CustomerId equals xr.CustomerId
join a in addresses on xr.AddressId equals a.AddressId
select new {Customer = c, Address = a};
Looks like another mistake. I bet that AddressRelId is the key to the AddressRelation table, and not what you want use to connect to the Address table.
.GroupJoin(addresses,
ar => ar.Address**Rel**ID,
a => a.AddressID,
In response to comment:
var query = customers
.Join(addressRelations,
c => c.CustomerId,
xr => xr.CustomerId,
(c, xr) => new {c, xr})
.Join(addresses,
x => x.xr.AddressId,
a => a.AddressId,
(x, a) => new {c = x.c, xr = x.xr, a = a})
.Select(x => new {Customer = x.c, Address = x.a});

It's returning the first customer address because you've told it to:
AddressLine = addresses.Select(b => b.AddressLine1).FirstOrDefault()
Here, addresses is all addresses. I suspect you just want:
AddressLine = a.Select(b => b.AddressLine1).FirstOrDefault()

Related

linq distinct with object selection

I have the following linq statement:
consumers = data.Select(x => new Consumer()
{
firstname = x.firstname,
lastname = x.lastname,
house = x.sublocationid,
floornr = x.floor,
appnr = x.roomnr
})
.Distinct()
.ToList();
Somehow this does not return distinct datasets. I assume it has something to do with the selection of the object? The distinct function is therefore not comparing the attributes directly but rather the objects? I am not understanding it fully unfortunately but in ms sql this statement works fine.
I also tried the following but it does not return a List object and I would need to use var or something else and I need a List of Consumer() objects.
consumers = data.Select(x => new Consumer()
{
firstname = x.firstname,
lastname = x.lastname,
house = x.sublocationid,
floornr = x.floor,
appnr = x.roomnr
})
.GroupBy(x => new { x.firstname, x.lastname, x.haus, x.etage, x.appnr })
.ToList();
You can do/try this:
public class Consumer
{
public string firstname { get; set; }
public string lastname { get; set; }
public string floor { get; set; }
public string house { get; set; }
}
List<Consumer> objConsumer = new List<Consumer>()
{
new Consumer(){ firstname="Govind", lastname="Sahu", house="298",floor="1st Floor"},
new Consumer(){ firstname="Govind", lastname="Sahu", house="298",floor="1st Floor"},
new Consumer(){ firstname="Govind1", lastname="Sahoo", house="297",floor="1st Floor"}
};
1st approach:
var obj = objConsumer.GroupBy(s => new {
s.firstname,
s.lastname,
s.house,
s.floor,
}).Select(o=>o.FirstOrDefault()).ToList();
2nd approach:
var obj1 = objConsumer.Select(s => new { firstname = s.firstname, lastname = s.lastname, house = s.house, floor = s.floor }).Distinct().ToList();
I found another solution by using DistinctBy from MoreLinq. It returns the right result when I use it like this
consumers = data.Select(x => new Consumer()
{
firstname = x.firstname,
lastname = x.lastname,
house = x.sublocationid,
floor = x.floor,
appnr = x.roomnr
})
.DistinctBy(x=> new
{
x.firstname,
x.lastname,
x.floor,
x.appnr,
x.house
})
.ToList();

How do I use LINQ group by clause to return unique employee rows?

I'm pretty new to LINQ, and I can't for the life of me figure this out. I've seen lots of posts on how to use the group by in LINQ, but for some reason, I can't get it to work. This is so easy in ADO.NET, but I'm trying to use LINQ. Here's what I have that is relevant to the problem. I have marked the part that doesn't work.
public class JoinResult
{
public int LocationID;
public int EmployeeID;
public string LastName;
public string FirstName;
public string Position;
public bool Active;
}
private IQueryable<JoinResult> JoinResultIQueryable;
public IList<JoinResult> JoinResultIList;
JoinResultIQueryable = (
from e in IDDSContext.Employee
join p in IDDSContext.Position on e.PositionID equals p.PositionID
join el in IDDSContext.EmployeeLocation on e.EmployeeID equals el.EmployeeID
where e.PositionID != 1 // Do not display the super administrator's data.
orderby e.LastName, e.FirstName
// ***** Edit: I forgot to add this line of code, which applies a filter
// ***** to the IQueryable. It is this filter (or others like it that I
// ***** have omitted) that causes the query to return multiple rows.
// ***** The EmployeeLocationsList contains multiple LocationIDs, hence
// ***** the duplicates employees that I need to get rid of.
JoinResultIQueryable = JoinResultIQueryable
.Where(e => EmployeeLocationsList.Contains(e.LocationID);
// *****
// ***** The following line of code is what I want to do, but it doesn't work.
// ***** I just want the above join to bring back unique employees with all the data.
// ***** Select Distinct is way too cumbersome, so I'm using group by.
group el by e.EmployeeID
select new JoinResult
{
LocationID = el.LocationID,
EmployeeID = e.EmployeeID,
LastName = e.LastName,
FirstName = e.FirstName,
Position = p.Position1,
Active = e.Active
})
.AsNoTracking();
JoinResultIList = await JoinResultIQueryable
.ToListAsync();
How do I get from the IQueryable to the IList only returning the unique employee rows?
***** Edit:
Here is my current output:
[4][4][Anderson (OH)][Amanda][Dentist][True]
[5][4][Anderson (OH)][Amanda][Dentist][True]
[4][25][Stevens (OH)][Sally][Dental Assistant][True]
[4][30][Becon (OH)][Brenda][Administrative Assistant][False]
[5][30][Becon (OH)][Brenda][Administrative Assistant][False]
Actually you do not need grouping here, but Distinct. Ordering before Distinct or grouping is useless. Also AsNoTracking with custom projection is not needed.
var query =
from e in IDDSContext.Employee
join p in IDDSContext.Position on e.PositionID equals p.PositionID
join el in IDDSContext.EmployeeLocation on e.EmployeeID equals el.EmployeeID
where e.PositionID != 1 // Do not display the super administrator's data.
select new JoinResult
{
LocationID = el.LocationID,
EmployeeID = e.EmployeeID,
LastName = e.LastName,
FirstName = e.FirstName,
Position = p.Position1,
Active = e.Active
};
query = query.Distinct().OrderBy(e => e.LastName).ThenBy(e => e.FirstName);
JoinResultIList = await query.ToListAsync();
The problem is that few employees have more than one location is causing the results to be repeated.You can handle it in multiple ways. Im using Let clause to tackle the issue in the below example
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int EmployeeID { get; set; }
public int PositionID { get; set; }
}
public class EmployeeLocation
{
public int EmployeeID { get; set; }
public int LocationID { get; set; }
}
public class Position
{
public int PositionID { get; set; }
public string Position1 { get; set; }
}
public class Location
{
public int LocationID { get; set; }
}
public class JoinResult
{
//Suggestion : Insetad of LocationID there should be a varibale that has all the locations of an employee
public IEnumerable<int> LocationIDs;
public int LocationID;
public int EmployeeID;
public string LastName;
public string FirstName;
public string Position;
public bool Active;
}
//Setting up mock data
List<Position> positions = new List<Position>();
positions.Add(new Position() { Position1 = "Dentist", PositionID = 2 });
positions.Add(new Position() { Position1 = "Dental Assistant", PositionID = 3 });
positions.Add(new Position() { Position1 = "Administrative Assistant", PositionID = 4 });
List<Employee> employees = new List<Employee>();
employees.Add(new Employee() { EmployeeID = 4, FirstName = "Amanda", LastName = "Anderson (OH)", PositionID = 2 });
employees.Add(new Employee() { EmployeeID = 25, FirstName = "Sally", LastName = "Stevens (OH)", PositionID = 3 });
employees.Add(new Employee() { EmployeeID = 30, FirstName = "Brenda", LastName = "Becon (OH)", PositionID = 4 });
List<Location> locations = new List<Location>();
locations.Add(new Location() { LocationID = 4 });
locations.Add(new Location() { LocationID = 5 });
List<EmployeeLocation> employeeLocation = new List<EmployeeLocation>();
employeeLocation.Add(new EmployeeLocation() { LocationID = 4, EmployeeID = 4 });
employeeLocation.Add(new EmployeeLocation() { LocationID = 5, EmployeeID = 4 });
employeeLocation.Add(new EmployeeLocation() { LocationID = 4, EmployeeID = 25 });
employeeLocation.Add(new EmployeeLocation() { LocationID = 4, EmployeeID = 30 });
employeeLocation.Add(new EmployeeLocation() { LocationID = 5, EmployeeID = 30 });
var result = (from e in employees
join p in positions on e.PositionID equals p.PositionID
let employeeLocations = (from el in employeeLocation where el.EmployeeID == e.EmployeeID select new { LocationID = el.LocationID })
where e.PositionID != 1 // Do not display the super administrator's data.
orderby e.LastName, e.FirstName
select new JoinResult
{
LocationID = employeeLocations.Select(p=>p.LocationID).First()//Here its just selecting the first location,
LocationIDs = employeeLocations.Select(p=> p.LocationID),//This is my suggestion
EmployeeID = e.EmployeeID,
LastName = e.LastName,
FirstName = e.FirstName,
Position = p.Position1,
}).ToList();
Okay. So here is the solution I came up with. I installed the morelinq NuGet package, which contains a DistinctBy() method. Then I added that method to the last line of the code shown in my problem.
JoinResultIList = JoinResultIQueryable
.DistinctBy(jr => jr.EmployeeID)
.ToList();

Linq Split properties of Class and assign it to another Custom Class

I have a Complex Situation now and i am terribly stuck. Kindly Let me know if you can share some light to it.
I have a
List Which will have the Following properties
public class Categories
{
public string DisplayName { get; set; }
public string ValueCode { get; set; }
public string Count { get; set; }
}
This will have Values like
Category1/SubCategory1
cat1/sc1
5
Category1/SubCategory2
cat1/sc2
4
Category 2/Subcategory1
cat2/sc1
5
Category 2/Subcategory2
cat2/sc2
23
I created a Custom Class to fill in the values
public class JobCateogry
{
public string DisplayName { get; set; }
public string ValueCode { get; set; }
public string Count { get; set; }
public List<JobCateogry> SubCategories { get; set; }
}
I have to Split the String in the Code Value and assign it to the SubCategory.
Like My Final out of jobCategory would be
Category1
Cat1
9
SubCategory1
sub1
5
SubCateogry2
sub2
4
I tried to Split the string and assign it to the new class in two step first by splitting and then by assiging. But i am sure i am doing it the wrong way, because the moment i split, i loose the count .
var lstCategory = Categories
.Where(i => i.count > 0)
.Select(item => item.valueCode.Split('/')
.Select(k =>(k)).ToList();
List<JobCategories> jobcategories = lstCategory
.Select(item => item.Split(QueryStringConstants.CAT_SEPERATOR.ToCharArray()[0]))
.GroupBy(tokens => tokens[0].Trim(), tokens => tokens[1])
.Select(g => new JobCategories(g.Key, g.DisplayName,g.ToList(),)).ToList();
Can you please help?
A bit weird task
It might not be the best solution and it only works with the two layers :-), and i tried keeping a lot of linq for the fun of it
anyway hope it can get you moving forward.
full code snippet https://gist.github.com/cbpetersen/db698def9a04ebb2abbc
static void Main(string[] args)
{
var cats = new[]
{
new Categories { Count = "5", ValueCode = "cat1/sc1", DisplayName = "Category1/SubCategory1" },
new Categories { Count = "4", ValueCode = "cat1/sc2", DisplayName = "Category1/SubCategory2" },
new Categories { Count = "5", ValueCode = "cat2/sc1", DisplayName = "Category2/Subcategory1" },
new Categories { Count = "23", ValueCode = "cat2/sc2", DisplayName = "Category2/Subcategory2" }
};
var categories = cats.Select(x => x.DisplayName.Split('/')[0]).Distinct();
var list = new List<JobCateogries>();
foreach (var category in categories)
{
var a = new JobCateogries
{
ValueCode = cats.Where(x => x.DisplayName.Split('/')[0] == category)
.Select(x => x.ValueCode.Split('/')[0]).FirstOrDefault(),
DisplayName = category,
SubCategories = cats.Where(x => x.DisplayName.Split('/')[0] == category)
.Select(x => new JobCateogries
{
SubCategories = new List<JobCateogries>(),
Count = x.Count,
DisplayName = x.DisplayName.Split('/')[1],
ValueCode = x.ValueCode.Split('/')[1]
}).ToList(),
};
a.Count = a.SubCategories.Select(x => int.Parse(x.Count)).Sum().ToString();
list.Add(a);
}
list.ForEach(x => Print(x));
Console.ReadKey();
}
public static void Print(JobCateogries category, int indent = 0)
{
var prefix = string.Empty.PadLeft(indent);
Console.WriteLine(prefix + category.DisplayName);
Console.WriteLine(prefix + category.ValueCode);
Console.WriteLine(prefix + category.Count);
category.SubCategories.ForEach(x => Print(x, indent + 4));
}

How do I combine columns together

I have 2 lists that contain different data but have similar columns.
Basically I want to join these lists but then merge the similar columns into 1.
var listCombo= List1.Where(a=>DataIds.Contains(a.dataId)).DefaultIfEmpty()
.Join(List2,
list1 => list1.key,
list2 => list2.key,
(L1,L2) => new
{
L2.key,
L2.dataId,
L2.dataValue,
L2.date,
L1.secId,
L1.dataId,
L1.dataValue
});
I'd like to combine the dataId and dataValue columns together. How do I do that?
So I can just say listCombo.dataId or listCombo.dataValue rather than having to uniquely name them.
If I understand your question, you're trying to combine the two fields into one. Assuming that you have a Class
public class List1 {
public int dataId {get;set;}
public string dataValue {get;set;}
public string combinedValue {get;set;}
}
No you can use like,
var listCombo= List1.Where(a=>DataIds.Contains(a.dataId)).DefaultIfEmpty()
.Join(List2,
list1 => list1.key,
list2 => list2.key,
(L1,L2) => new List1
{
dataId = L2.dataId,
dataValue = L2.dataValue
combinedValue = L2.dataId + " - " L2.dataValue
});
I would use POCO methods for a list1, list2, and a third list which is a combo of both. I would be sure that if you are combining unlike types you do appropriate casts or if you want complex types you can define them as their own properties or what not.
static void Main(string[] args)
{
List<A> lisA = new List<A> {new A {AId = 1, AName = "Brett"}, new A {AId = 2, AName = "John"}};
List<B> lisB = new List<B> {new B { BId = 1, BName = "Doe" }, new B { BId = 2, BName = "Howard" } };
List<C> lisC = lisA.Join(lisB,
list1 => list1.AId,
list2 => list2.BId,
(L1, L2) => new C
{
CId = L1.AId,
CName = L1.AName + " " + L2.BName
}).ToList();
lisC.ForEach(
n => Console.WriteLine(n.CName + "\n")
);
}
public class A
{
public int AId { get; set; }
public string AName { get; set; }
}
public class B
{
public int BId { get; set; }
public string BName { get; set; }
}
public class C
{
public int CId { get; set; }
public string CName { get; set; }
}

Get Count from entity framework

Quite new to EF, basically i want to convert this SQL query:
SELECT
PSKU.ProductSKUID,
PSKU.ProductSKUName,
W.WarehouseID,
W.WarehouseName,
SA.SystemAreaName,
COUNT(SLI.ProductSKUID) AS QTY
FROM dbo.StockLineItem AS SLI INNER JOIN
dbo.ProductSKU AS PSKU ON PSKU.ProductSKUID = SLI.ProductSKUID INNER JOIN
dbo.Warehouse AS W ON W.WarehouseID = SLI.WarehouseID INNER JOIN
dbo.SystemArea AS SA ON SA.SystemAreaID = SLI.SystemAreaID
WHERE (SA.SystemAreaID = 1)
AND W.WarehouseID = #WarehouseID
GROUP BY PSKU.ProductSKUID, PSKU.ProductSKUName, W.WarehouseName, SA.SystemAreaName, W.WarehouseID
To an effective EF statement. This is what i Have so far, my Model class and the method:
[Serializable]
public class StockReturnMethod
{
public int ProductSKUID { get; set; }
public int WarehouseID { get; set; }
public int LotID { get; set; }
public string LotName { get; set; }
public int AreaID { get; set; }
public string AreaName { get; set; }
public int BinID { get; set; }
public string BinName { get; set; }
}
public class DALStockMovement
{
scmEntitiesPrimaryCon entities = new scmEntitiesPrimaryCon();
public List<AvailibleStock> AvailibleStockQty(int warehouseID)
{
var rows = (from PLA in entities.ProductLocationAssignments
from W in entities.Warehouses
from SLI in entities.StockLineItems
from SA in entities.SystemAreas
from PSKU in entities.ProductSKUs
where W.WarehouseID == warehouseID
select new AvailibleStock() { WarehouseID = W.WarehouseID, ProductSKUID = PSKU.ProductSKUID, ProductSKUName = PSKU.ProductSKUName, WarehouseName = W.WarehouseName, Status = SA.SystemAreaName, QtyUnassigned = SLI.ProductSKUID }).ToList();
return rows;
}
Any Advice to get this to an Effective EF Statement would be appreciated
I actually used this tool called Linqer, since I had the SQL
I just popped it into that tool and it generated the Linq for me.
Here is what came out:
var SKUStock = (from sli in entities.StockLineItems
where
sli.SystemArea.SystemAreaID == 1 &&
sli.WarehouseID == warehouseID
group new { sli.ProductSKU, sli.Warehouse, sli.SystemArea, sli } by new
{
ProductSKUID = (System.Int32?)sli.ProductSKU.ProductSKUID,
sli.ProductSKU.ProductSKUName,
sli.Warehouse.WarehouseName,
sli.SystemArea.SystemAreaName,
WarehouseID = (System.Int32?)sli.Warehouse.WarehouseID
} into g
select new AvailibleStock()
{
ProductSKUID = (int)(System.Int32?)g.Key.ProductSKUID,
ProductSKUName = g.Key.ProductSKUName,
WarehouseID = (int)(System.Int32?)g.Key.WarehouseID,
WarehouseName = g.Key.WarehouseName,
Status = g.Key.SystemAreaName,
QtyUnassigned = (int)(Int64?)g.Count(p => p.sli.ProductSKUID != null)
}).ToList();
return SKUStock;
It returns exactly what i need :).

Resources