Please help me complete the following query:
var results = from species in db.Species
join cats in db.Categories on species.CategoryId equals cats.CategoryId
join groups in db.Groups on cats.GroupId equals groups.GroupId
group groups by groups into g
select new GroupWithCategoryChildren
{
Group = g.Key,
Categories = (cats from above query for this same g.Key group)???
};
I could do this really easily with SQL, but I'm struggling with LINQ. I want to end up with a List of each Group ALONG WITH the cats that joined to it in the above/main single query, ideally without re-querying for that data again in a separate query.
Thanks a lot
I'm not sure I understood correctly but I think this is all you need: Instead of group groups by groups do group cats by groups and then the Key will be the groups and the values of the group will be all the matching categories.
var results = from species in db.Species
join cats in db.Categories on species.CategoryId equals cats.CategoryId
join groups in db.Groups on cats.GroupId equals groups.GroupId
group cats by groups into g
select new GroupWithCategoryChildren
{
Group = g.Key,
Categories = g.ToList()
};
I used this to test it:
List<dynamic> Species = new List<dynamic>()
{
new { A = "1", CategoryId = 1 },
new { A = "2", CategoryId = 2 },
};
List<dynamic> Categories = new List<dynamic>
{
new { B = "1", CategoryId = 1, GroupId = 1 },
new { B = "2", CategoryId = 2, GroupId = 1 },
};
List<dynamic> Groups = new List<dynamic>
{
new { C = "1", GroupId = 1 }
};
//result: { Group: { C = "1", GroupId = 1 }, Categories (1,2)}
Related
I am having two lists:
ListA:
[
{
Id = 1,
Name = "A",
Summary = ""
},
{
Id = 2,
Name = "B",
Summary = ""
}
]
ListB:
[
{
Id = 1,
Value = "SomeThing"
},
{
Id = 2,
Value = "EveryThing"
}
]
I want to join that two list using LINQ and want to return ListA which value is update as Below
[
{
Id = 1,
Name = "A",
Summary = "SomeThing"
},
{
Id = 2,
Name = "B",
Summary = "EveryThing"
}
]
I am joining ListA and ListB based on Id and assigning value to summary.
I tried below approach:
var query = from obj1 in ListA
join obj2 in ListB on obj1.Id equals obj2.Id
select obj1.Summary = obj2.Value, return obj1;
**=>so here i want assign data from obj2 to obj1 then want to return obj1 **
is that possible or how we can do this?
You could also update the existing ListA with a simple loop
foreach (var itemA in ListA)
{
itemA.Summary = ListB.FirstOrDefault(x => x.Id == itemA.Id)?.Value;
}
Join approach
var query = ListA.Join(ListB,
ia => ia.Id,
ib => ib.Id,
(ia, ib) => new aItem() //type of ListA here
{
Id = ia.Id,
Name = ia.Name,
Summary = ib.Value
});
You could try to join the two lists like this:
var listA = new List<ClassA>();
var listB = new List<ClassB>();
var list = listA.Join(listB, a => a.Id, b => b.Id, (a, b) =>
new ClassA
{
Id = a.Id,
Name = a.Name,
Summary = b.Value
});
Using method syntax Enumerable.Join is the easier one to use here:
var result = listA.Join(listB, // Join ListA and ListB
a => a.Id, // from every a in ListA take a.Id
b => b.Id, // from every b in ListB take b.Id
(a, b) => new // when they match, take the a and the b
{ // to create a new object with properties
Id = a.Id,
Name = a.Name,
Summary = b.Value,
});
Note that the result is of an anonymous type, If you want a result with the same type as the items in ListA (let's say they are of class A), change the last part of the join:
(a, b) => new A() // when they match, take the a and the b
{ // to create a new object of class A
Id = a.Id,
Name = a.Name,
Summary = b.Value,
});
Assume I have the following data:
var workers = new[]
{
new { Name = "John", Id = 1 },
new { Name = "Greg", Id = 2 },
new { Name = "Jack", Id = 3 },
new { Name = "Josh", Id = 4 },
new { Name = "Jill", Id = 5 },
new { Name = "Jane", Id = 6 }
};
var contracts = new[]
{
new { ContractNumber="1", WorkerId=1, ContractDate = new DateTime(2017,6,30) },
new { ContractNumber="2", WorkerId=2, ContractDate = new DateTime(2017,7,10) },
new { ContractNumber="3", WorkerId=2, ContractDate = new DateTime(2017,7,15) },
new { ContractNumber="4", WorkerId=5, ContractDate = new DateTime(2017,7,20) },
new { ContractNumber="5", WorkerId=1, ContractDate = new DateTime(2017,7,25) }
};
What I need to do is to select the first worker who has the minimum quantity of contracts where contract date greater or equals to:
var fromDate = new DateTime(2017, 7, 1);
excluding the workers with the following Id:
int[] exceptWorkerIds = new int[] {1, 4};
If several workers have a similar minimum quantity of contracts then select the worker with the first name in alphabetical order.
I resolved this task the following way.
Firstly, for each worker left join contracts. If contract exists my helper property ContractExists = 1, if not then 0.
var query =
from w in workers.Where(x => !exceptWorkerIds.Contains(x.Id))
join c in contracts.Where(x => x.ContractDate >= fromDate)
on w.Id equals c.WorkerId into workerContracts
from wc in workerContracts.DefaultIfEmpty()
select new {WorkerId = w.Id, WorkerName = w.Name, ContractExists = wc == null ? 0: 1};
This query gives me the following result:
Secondly, I group the obtained results by WorkerId, WorkerName getting the sum of contracts and order data by sum and worker name:
var result =
(from q in query
group q.ContractExists by new {q.WorkerId, q.WorkerName} into g
orderby g.Sum(), g.Key.WorkerName
select new
{
WorkerId = g.Key.WorkerId,
WorkerName = g.Key.WorkerName,
WorkerContractsCount = g.Sum()
}).ToList().Take(1);
Take(1) gives me the top 1 of resulted data:
The question: Is there a way to do it with the only query or any simpler or elegant manner then I did? If yes, does this help to boost productivity of query execution?
Rather than doing join (which multiplies the data) followed by group by you could use group join (what actually your query is using before you do from wc in workerContracts.DefaultIfEmpty()).
The other logic is pretty much the same - workerContracts.Count() gives you the desired quantity of contracts, so apply the desired order, take the first and you are done:
var result =
(from w in workers.Where(x => !exceptWorkerIds.Contains(x.Id))
join c in contracts.Where(x => x.ContractDate >= fromDate)
on w.Id equals c.WorkerId into workerContracts
let workerContractsCount = workerContracts.Count()
orderby workerContractsCount, w.Name
select new
{
WorkerId = w.Id,
WorkerName = w.Name,
WorkerContractsCount = workerContractsCount
})
.FirstOrDefault();
With maybe less Wheres and Join than Ivan Stoev's answer, here is a more compact version :
var result = workers
.Where(w => !exceptWorkerIds.Contains(w.Id))
.Select(w => new {
Name = w.Name,
Id = w.Id,
Nb = contracts
.Count(c => c.WorkerId == w.Id && c.ContractDate >= new DateTime(2017,7,1))
})
.OrderBy(w => w.Nb).ThenBy(w => w.Name).FirstOrDefault();
if(result != null)
Console.WriteLine(result.Name);
else
Console.WriteLine("Result not found");
Explanation : for each worker except the ones we don't want to check, we count the number of contract associated which date is later or equal to 2017,7,1, we then sort it by this number and by name, and take the first one.
I have this query :
from m in _ctx.MaterialRequestContractorDetails
where m.MaterialRequestContractorId == MRCId
join mat in _ctx.MaterialDescriptions on m.MaterialDescriptionId equals mat.Id
join l in _ctx.Lines on m.LineId equals l.Id
join sheet in _ctx.Sheets on m.SheetId equals sheet.Id
join joint in _ctx.Joints on m.SheetId equals joint.SheetId
join testjoint in _ctx.TestPackageJoints on joint.Id equals testjoint.Id
join testpack in _ctx.TestPackages on testjoint.TestPackageId equals testpack.Id
I have connection between:
MaterialRequestContractorDetails and sheet
MaterialRequestContractorDetails and line
but I don't have any connection between MaterialRequestContractorDetails and testpackage.
I can have a connection with testpackage using joint and testpackagejoint, but this connection makes a problem and my records repeat with similar data based on joint row
I mean if I have 2 records in my joint table, my result repeats 2 times with similar data.
What you want to do is a GroupJoin function. In a query syntax it will look like this:
var result = (from m in MaterialRequestContractorDetails
where m.MaterialRequestContractorId == MRCId
join mat in MaterialDescriptions on m.MaterialDescriptionId equals mat.Id
join l in Lines on m.LineId equals l.Id
join sheet in Sheets on m.SheetId equals sheet.Id
join joint in (from joint in Joints
join testjoint in TestPackageJoints on joint.Id equals testjoint.Id
join testpack in TestPackages on testjoint.TestPackageId equals testpack.Id
select new { joint.SheetId, testpack })
on m.SheetId equals joint.SheetId into tt
select new { Details = m, Line = l, TestPacks = tt.Select(x => x.testpack).ToList() }).ToList();
What I actually did is separate all the "second half" of the joins into a nested select and then joined it with the main part, but when doing so used the into keyword (GroupJoin).
With the testing data bellow I got 1 record with the MRCId of 1 and in the record had a list of TestPackage containing 2 items.
List<dynamic> MaterialRequestContractorDetails = new List<dynamic>
{
new { MaterialRequestContractorId = 1, MaterialDescriptionId = 1, LineId = 1, SheetId = 1},
new { MaterialRequestContractorId = 2, MaterialDescriptionId = 2, LineId = 2, SheetId = 2},
};
List<dynamic> MaterialDescriptions = new List<dynamic>
{
new { Id = 1 }, new { Id = 2 },
};
List<dynamic> Lines = new List<dynamic> { new { Id = 1 } };
List<dynamic> Sheets = new List<dynamic> { new { Id = 1 } };
List<dynamic> Joints = new List<dynamic> { new { SheetId = 1, Id = 1 } };
List<dynamic> TestPackageJoints = new List<dynamic>
{
new { Id = 1, TestPackageId = 1 },
new { Id = 1, TestPackageId = 2 },
};
List<dynamic> TestPackages = new List<dynamic>
{
new { Id = 1 }, new { Id = 2 },
};
int MRCId = 1;
I have following sql result:
Table Result
Goal is to group this result by ProjectId and SequenceId. The later JSON Result should look like this:
[
{
"ProjectId": 1,
"ProjectName": "Testprojekt 1",
"Sequences": [
{
"SequenceId": 2,
"SequenceName": "ESN_Tauschen"
},
{
"SequenceId": 3,
"SequenceName": "Demontage"
}
]
},
{
"ProjectId": 2,
"ProjectName": "Testprojekt 2",
"Sequences": [
{
"SequenceId": 3,
"SequenceName": "Demontage"
}
]
}
]
My current linq expression gives me following result:
[
{
"ProjectId": 1,
"Sequences": [
2,
3
]
},
{
"ProjectId": 2,
"Sequences": [
3
]
}
]
var context = new ReworkPlace();
var result = from p in context.Projects
join rs in context.ReworkStations on p.ProjectId equals rs.ProjectId
join l in context.ReworkStationReworkConfigurationLinkSets on rs.ReworkStationId equals
l.ReworkStationId
join rc in context.ReworkConfigurations on l.ReworkConfigurationId equals rc.ReworkConfigurationId
join s in context.Sequences on rc.SequenceId equals s.SequenceId
group s.SequenceId by p.ProjectId into g
select new
{
ProjectId = g.Key,
Sequences = g.ToList()
};
return Json(result, JsonRequestBehavior.AllowGet);
I dont know how I have to adapt my linq expression to inlcude the not grouped properties like ProjectName, SequenceId and SequenceName into my json result.
Would be nice if somebody could help me.
To get the desired result without totally rewriting the query, replace the grouping part:
group s.SequenceId by p.ProjectId into g
select new
{
ProjectId = g.Key,
Sequences = g.ToList()
};
with something like this:
group new { p, s } by p.ProjectId into g
let p = g.FirstOrDefault().p
select new
{
ProjectId = g.Key,
ProjectName = p.Name,
Sequences =
(from e in g
group e.s by e.s.SequenceId into g2
let s = g2.FirstOrDefault()
select new
{
SequenceId = g2.Key,
SequenceName = s.Name
}).ToList()
};
The trick is to include between group and by the data that will be needed inside the grouping (in addition to the Key which is what you put after by).
To get additional fields, you either include them in the grouping key, or if they are one and the same for the defined grouping key, use the FirstOrDefault to get them from the first record in the grouping as in the above example.
I have the following LINQ query but i want to modify it that I want to group by staffId and pick only those records whose ObservationDate is Max for each staffId.
from ob in db.TDTObservations.OfType<TDTSpeedObservation>()
select new
{
Id = ob.ID,
AcademicYearId = ob.Teachers.FirstOrDefault().Classes.FirstOrDefault().AcademicYearID,
observationDate = ob.ObservationDate,
schoolId = ob.Teachers.FirstOrDefault().Classes.FirstOrDefault().SchoolID,
staffId=ob.Teachers.FirstOrDefault().ID
};
var observations =
from ob in db.TDTObservations.OfType<TDTSpeedObservation>()
select new {
Id = ob.ID,
AcademicYearId = ob.Teachers.FirstOrDefault().Classes.FirstOrDefault().AcademicYearID,
observationDate = ob.ObservationDate,
schoolId = ob.Teachers.FirstOrDefault().Classes.FirstOrDefault().SchoolID,
staffId=ob.Teachers.FirstOrDefault().ID
};
var result = from o in observations
group o by o.staffId into g
select g.OrderByDescending(x => x.observationDate).First();
what about this: hereby you first group your entries (Teachers) by their ID together and then from each group (grp) you pick that one with the latest ObservationDate
var observations = from d in db.TDTObservations.OfType<TDTSpeedObservation>()
group d by d.Teachers.FirstOrDefault().ID into grp
select grp.OrderByDescending(g => g.ObservationDate).FirstOrDefault();