I have the following class:
public class Relation {
public Int32 SourceId { get; set; }
public Int32 TargetId { get; set; }
}
And the following list:
List<Relation> relations = service.GetRelations();
I need to select the SourceIds which are related to ALL targets.
So, given the following example with pairs (SourceId, TargetId):
(1, 1), (1, 2), (2, 1), (3, 2)
In this case TargetId can be 1 or 2.
And the only SourceId that is related to all TargetIds (1, 2) is SourceId 1.
SourceId 2 is only related to TargetId 1 and SourceId 3 is only related to TargetId 2.
How can I do this?
You need to collect all possible targets ids:
var input = new []
{
new Relation(1, 1),
new Relation(1, 2),
new Relation(2, 1),
new Relation(3, 2),
};
var allTargetId = input.Select(x => x.TargetId).Distinct().ToArray();
Then group by source id and in each group check that all group members presented in allTargetId:
var result = input.GroupBy(x => x.SourceId, x => x.TargetId)
.Where(g => allTargetId.All(x => g.Contains(x)))
.Select(g => g.Key)
.ToArray();
Note: to make this code work I've added a constructor to your Relation class and it looks like
public class Relation
{
public Relation(int sourceId, int targetId)
{
SourceId = sourceId;
TargetId = targetId;
}
public Int32 SourceId { get; set; }
public Int32 TargetId { get; set; }
}
EDIT
To get Relation's you can use this query:
var result = input.GroupBy(x => x.SourceId)
.Where(g => allTargetId.All(x => g.Select(y => y.TargetId).Contains(x)))
.SelectMany(g => g)
.ToArray();
Please note that I've tested it only with linq2objects so I'm not sure how it will be translated to SQL
The following code does what you asked for. It has a form of unit test so you can check different scenarios
[Fact]
public void FindSourcesThatTargetAll()
{
var list = new List<Relation>
{
new Relation(1, 1), new Relation(1, 2), new Relation(2, 1), new Relation(3, 2)
};
var allTargets = list.Select(x => x.TargetId).Distinct().OrderBy(x=>x).ToList();
var dict = list.GroupBy(x => x.SourceId).ToDictionary(x => x.Key,
grouping => grouping.Select(y => y.TargetId).Distinct().OrderBy(x=>x).ToList());
var sourcesThatTargetAll = dict.Where(x => x.Value.Count == allTargets.Count).Select(x => x.Key).ToList();
Assert.Single(sourcesThatTargetAll);
Assert.Equal(1, sourcesThatTargetAll.First());
}
Basically, I did:
Find all the targets.
For every source find all targets (distinct is important) and group it by the source in the dictionary (dict variable)
From the above dictionary select all source which matches all targets (count in the example is enough, but you can make a more complicated comparison)
A simple way to achieve this would be to group the records by TargetId, and then find an intersection of all the SourceId
var groups = relations.GroupBy(r => r.TargetId).ToArray();
if (groups.Length > 0) {
var set = new HashSet<int>(groups[0]);
for (int i = 1; i < groups.Length; ++i)
set.IntersectWith(groups[i].Select(r => r.SourceId));
}
At the end of this set will contain all the SourceIds that are related to all TargetIds
public class Relation
{
public Int32 SourceId { get; set; }
public Int32 TargetId { get; set; }
}
public Int32?[] FindRelation(Relation[] relations)
{
List<Int32?> sourceIds = new List<int?>;
var countOfTargets = relations.Select(x => x.TargetId).Distinct().Count();
var relationsGroupedBySource = relations.GroupBy(x => x.SourceId);
foreach (var group in relationsGroupedBySource)
{
var distinctGroup = group.Distinct();
if (distinctGroup.Count() == countOfTargets)
{
sourceIds.Add(distinctGroup.Select(x => x.SourceId).First());
}
}
return sourceIds.ToArray();
}
public void Test()
{
Relation[] relations = {
new Relation() { SourceId = 1, TargetId = 1 },
new Relation() { SourceId = 1, TargetId = 2 },
new Relation() { SourceId = 2, TargetId = 1 },
new Relation() { SourceId = 3, TargetId = 2 }
};
var sourceIds = FindRelation(relations);
}
Related
I have fourtables:
CL_ProductType
CL_InsuranceProduct
PR_Product
PR_ProductInsuranceProduct (aggregation table for PR_Product and CL_InsuranceProduct)
I need left join for PR_ProductInsuranceProduct and I've done it with SelectMany() selector.
The problem is that this query has groupBy method, and I need to extract the max(ID_ProductInsuranceProduct).
My question is: How to extract in .SelectMany() the highist value of ID_ProductInsuranceProduct?
SQL that works:
select p.ID_Product,p.Name, p.Code, p.InProduction, MAX(pip.ID_ProductInsuranceProduct)
from PR_Product p
join CL_ProductType pt ON p.ID_ProductType = pt.ID_ProductType
left join PR_ProductInsuranceProduct pip ON p.ID_Product = pip.ID_Product
join CL_InsuranceProduct ip ON pip.ID_InsuranceProduct = ip.ID_InsuranceProduct
GROUP BY p.ID_Product,p.Name, p.Code, p.InProduction
My code in C# and LINQ Lambda:
var query = DBContext.PR_Product
.Where(m => m.Active)
.Where(nameFilter)
.Where(activationDateFilter)
.Where(closureDateFilter)
.Where(productTypeFilter)
.Where(subgroupFilter)
.Where(inproductionFilter)
.Where(answerFilter)
.Where(insuranceProductFilter)
.Where(excludePidsFilter)
.Join(DBContext.CL_ProductType, p => p.ID_ProductType, pt => pt.ID_ProductType,
(p, pt) => new { p, pt = pt.Name })
.GroupJoin(DBContext.PR_ProductInsuranceProduct,
p => p.p.ID_Product,
pip => pip.ID_Product,
(p1, pip) => new { Products = p1, ProductInsuranceProduct = pip })
.SelectMany
(
x => x.ProductInsuranceProduct.DefaultIfEmpty(),
(x, y) => new
{
x.Products.p.ID_Product,
x.Products.p.Name,
x.Products.p.Code,
x.Products.p.ActivationDate,
x.Products.p.ClosureDate,
x.Products.pt,
x.Products.p.InProduction,
//Here I want to fill in to my custom property max for ID_ProductInsuranceProduct, MaxId is a custom property in a model
x.Products.p.MaxId = x.ProductInsuranceProduct.Max(pip => pip.ID_ProductInsuranceProduct)
})
.GroupBy(x =>
new
{
x.ID_Product,
x.Name,
x.Code,
x.ActivationDate,
x.ClosureDate,
x.pt,
x.InProduction,
});
I assume, beacause it's a SelectMany, that my code returns "flatten" data into one single table, therefore, my method Max, its input is bad, because its not a collection?
Can I do left join in linq with just .Select()?
My continuation of the code, when I execute the query:
count = query.Count();
var list = query
.OrderBy(x => x.FirstOrDefault().Code)
.DoPaging(pageSize, pageIndex)
.ToList();
List<PR_Product> products =
(from m in list
select new PR_Product
{
ID_Product = m.Key.ID_Product,
Name = m.Key.Name,
Code = m.Key.Code,
ActivationDate = m.Key.ActivationDate,
ClosureDate = m.Key.ClosureDate,
ActivationDateString = m.Key.ActivationDate.ToString("d", new CultureInfo(DALParams.LCID, false)),
ClosureDateString = m.Key.ClosureDate.ToString("d", new CultureInfo(DALParams.LCID, false)),
ProductType = m.Key.pt,
InProduction = m.Key.InProduction
//MaxId = implemention...
}).ToList();
Given the following objects:
public class PR_Product
{
public int ID_Product { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public bool InProduction { get; set; }
}
public class PR_ProductInsuranceProduct
{
public int ID_ProductInsuranceProduct { get; set; }
public int ID_Product { get; set; }
}
I've create in memory collections to demonstrate how to extract max(property) from a left join in LINQ with a lambda expression:
var product = new List<PR_Product>
{
new PR_Product { ID_Product = 1, Name = "TestName1", Code = "TestCode1", InProduction = true },
new PR_Product { ID_Product = 2, Name = "TestName2", Code = "TestCode3", InProduction = true },
new PR_Product { ID_Product = 3, Name = "TestName3", Code = "TestCode3", InProduction = true }
};
var productInsurance = new List<PR_ProductInsuranceProduct>
{
new PR_ProductInsuranceProduct { ID_ProductInsuranceProduct = 111, ID_Product = 1 },
new PR_ProductInsuranceProduct { ID_ProductInsuranceProduct = 222, ID_Product = 1 },
new PR_ProductInsuranceProduct { ID_ProductInsuranceProduct = 333, ID_Product = 3 },
};
var result = product
.GroupJoin(productInsurance,
prProduct => prProduct.ID_Product,
insuranceProduct => insuranceProduct.ID_Product,
(prProduct, insuranceProduct) => new { prProduct, insuranceProduct })
.SelectMany(arg => arg.insuranceProduct.DefaultIfEmpty(), (prProduct, insuranceProducts) => new { prProduct })
.GroupBy(arg => new { arg.prProduct.prProduct.ID_Product, arg.prProduct.prProduct.Name, arg.prProduct.prProduct.Code, arg.prProduct.prProduct.InProduction})
.Select(grouping => new
{
grouping.Key.ID_Product,
grouping.Key.Name,
grouping.Key.Code,
Max_ID_ProductInsuranceProduct = grouping.FirstOrDefault()?.prProduct.insuranceProduct.DefaultIfEmpty().Max(insuranceProduct => insuranceProduct?.ID_ProductInsuranceProduct)
});
Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented));
Hope this helps.
I have a list of values, as below , the number at left side represents the object to be removed / inserted, where the "insert/remove" is action taken
1 insert
2 insert
3 insert
2 remove
4 insert
2 insert
3 remove
5 insert
3 insert
5 remove
And final result i want is "5 remove".
So if the position of removed object in list is bigger than position of same object from insert, then it will be removed, others will leave intact(1,2,3,4 will be inserted). can we do it with LINQ?
Let's say you have class Operation with 2 properties ID and CRUD
public class Operation
{
public int ID { get; set; }
public string CRUD { get; set; }
}
var result = list.GroupBy(x => x.ID)
.ToDictionary(x => x.Key, x => x.Last().CRUD);
//if you want to have same List<Operation>
list = list.GroupBy(x => x.ID)
.Select(x=> new Operation { ID = x.Key, CRUD = x.Last().CRUD }).ToList();
Here dotNetFiddle
EDIT:
public static void Main()
{
List<Operation> list = new List<Operation>();
Operation o1 = new Operation();
o1.ID = 1;
o1.CRUD = "Insert";
list.Add(o1);
Operation o2 = new Operation();
o2.ID = 2;
o2.CRUD = "Insert";
list.Add(o2);
Operation o3 = new Operation();
o3.ID = 3;
o3.CRUD = "Insert";
list.Add(o3);
Operation o4 = new Operation();
o4.ID = 2;
o4.CRUD = "Remove";
list.Add(o4);
Operation o5 = new Operation();
o5.ID = 4;
o5.CRUD = "Insert";
list.Add(o5);
Operation o6 = new Operation(){ID=2, CRUD = "Insert"};
list.Add(o6);
Operation o7= new Operation(){ID=3, CRUD = "Remove"};
list.Add(o7);
Operation o8= new Operation(){ID=5, CRUD = "Insert"};
list.Add(o8);
Operation o9= new Operation(){ID=3, CRUD = "Insert"};
list.Add(o9);
Operation o10= new Operation(){ID=5, CRUD = "Remove"};
list.Add(o10);
var result = list.GroupBy(x => x.ID).ToDictionary(x => x.Key, x => x.Last().CRUD);
foreach(var item in result)
{
Console.WriteLine(item.Key + ": " + item.Value);
}
list = list.GroupBy(x => x.ID)
.Select(x=> new Operation { ID = x.Key, CRUD = x.Last().CRUD }).ToList();
Console.WriteLine("List values");
foreach(var item in list)
{
Console.WriteLine(item.ID + ": " + item.CRUD);
}
//output for 5 is Remove.
}
public class Operation
{
public int ID { get; set; }
public string CRUD { get; set; }
}
Not sure to understand the question but yoiu can check if the higgest ID is equal to "remove", like that :
public class MyList
{
public int Id { get; set; }
public string ToDo { get; set; }
}
List<MyList> MyList = new List<MyList>();
if ( MyList.OrderBy(c => c.Id).LastOrDefault().ToDo == "remove")
{
// Code to remove what need to be removed
}
I have 2 tables:
specs {specId, desc, createdby, lastupdatedby}
users {userid, username}
I want the below linq query need to be written in pure lambda expression
from spec in specs
from user in users.where(x => x.userId== spec.createdby).DefaultIfEmpty()
from updatedUser in users.where(x => x.userId== spec.lastupdatedbyby).DefaultIfEmpty()
select new {
spec = spec
user = user,
updatedUser = updatedUser
}
Please assist.
Data would be like say:
spec[{1, test, 1234, 2345},{2, test1, 1234, null}]
users[{1234, Allen},{2345, Dwayne}]
So the result should be
[{1, test, Allen, Dwayne}, {2, test1, Allen, null}]
Let's start with these classes:
class Specs {
public int specId { get; set; }
public string desc { get; set; }
public int createdby { get; set; }
public int lastupdatedby { get; set; }
}
class Users {
public int userId { get; set; }
public string username { get; set; }
}
class UpdatedUser {
public int userId {get; set;}
public string username { get; set; }
}
Now the Linq query, for convenience I have created some example data:
var specs = new Specs[]
{
new Specs{specId = 1, desc = "Spec1", createdby=1, lastupdatedby=1},
new Specs{specId = 2, desc = "Spec2", createdby=2, lastupdatedby=3},
new Specs{specId = 3, desc = "Spec3", createdby=3, lastupdatedby=1},
new Specs{specId = 4, desc = "Spec4", createdby=3, lastupdatedby=3},
};
var user = new Users[]
{
new Users{userId = 1, username = "User1"},
new Users{userId = 2, username = "User2"},
};
var updatedUser = new UpdatedUser[]
{
new UpdatedUser{userId = 1, username = "UpdatedUser1"},
new UpdatedUser{userId = 2, username = "UpdatedUser2"},
};
var result = specs
.GroupJoin(user,
s => s.createdby,
u => u.userId,
(s, u) => u.Select(x => new {spec = s, user = x})
.DefaultIfEmpty(new {spec = s, user = (Users)null}))
.SelectMany(g => g)
.GroupJoin(updatedUser,
firstJoin => firstJoin.spec.lastupdatedby,
uu => uu.userId,
(firstJoin, uu) =>
uu.Select(y => new {spec = firstJoin.spec, user = firstJoin.user, updatedUser = y})
.DefaultIfEmpty(new {spec = firstJoin.spec, user = firstJoin.user, updatedUser = (UpdatedUser) null}))
.SelectMany(g1 => g1)
.ToList();
The GroupJoin extension method help you obtain a tuple with all the elements of the starting table with a list of elements of the joined table.
Now if you enumerate the results:
result.ForEach(item => {
Console.WriteLine(item.spec.desc);
Console.WriteLine(item.user != null ? item.user.username : "NULL");
Console.WriteLine(item.updatedUser != null ? item.updatedUser.username : "NULL");
Console.WriteLine();
});
You obtain this:
Spec1
User1
UpdatedUser1
Spec2
User2
NULL
Spec3
NULL
UpdatedUser1
Spec4
NULL
NULL
You can try
var list = specs.Join(users,
s => s.lastupdatedby,
u => u.userid,
(s, u) => new { specs = s, users = u })
.Select(x => new {
specId = x.specs.specId,
desc = x.specs.desc,
createdby=x.specs.createdby,
username=x.users.username
}).ToString();
LEFT JOIN MULTIPLE TABLES
------------------------
create table Rama(rid int,name varchar(80),zip int);
create table Kris(kid int,rid int, Title varchar(80),zip int);
insert into Rama values(1,'Robert Rama' ,10001),
(2,'Roger Raju' ,10002),
(3,'Kevin Kali' ,10003),
(4,'Mark Mutu' ,10004)
insert into Kris values(0,0,'NP' ,10001),
(1,1,'VP' ,10001),
(2,2,'AVP',10002)
//Lambda expression
//Download https://www.linqpad.net/Download.aspx
//Create tables as given below and connect linqpad to your db
//select C# statement(s) on linqpad and run below
var v =
from r in Ramas
join k in Kris
on new { r.Rid, r.Zip } equals new { k.Rid, k.Zip }
into resultGroups
from k in resultGroups.DefaultIfEmpty()
select new { r.Rid, r.Name, k.Title };
v.Dump();
I would like to get the count of a number of entity tables and assign them to a single object which holds the counted values.
I am using a union because I want to execute a single query against the database.
I have written the following code but this will return a separate counts view model for each group by, instead Id like to assign the values to the properties of a single counts view model.
var counts =
_db.Departments.All()
.Select(c => new {key = 1, count = 0})
.Union(_db.Students.All().Select(c => new {key = 2, count= 0}))
.GroupBy(c=>c.key)
.Select(x => new CountsVm()
{
DepartmentCount = x.Count(d => d.key == 1),
StudentCount = x.Count(s => s.key == 2)
});
public class CountsVm
{
public int StudentCount { get; set; }
public int DepartmentCount { get; set; }
}
Here is a solution which will produce one query
var countsQuery =
_db.Departments.All()
.Select(p => new { key = 1, count = 0 })
.Union(_db.Students.All().Select(p => new { key = 2, count = 0 }))
.GroupBy(p => p.key)
.Select(p => new { key = p.Key, count = p.Count() }).ToList();
var counts = new CountsVm()
{
DepartmentCount =
countsQuery.Where(p => p.key == 1)
.Select(p => p.count)
.FirstOrDefault(),
StudentCount =
countsQuery.Where(p => p.key == 2)
.Select(p => p.count)
.FirstOrDefault()
};
Do you just need to call count on each entry table separately?
var counts = new CountsVm()
{
DepartmentCount = _db.Departments.All().Count(),
StudentCount = _db.Students.All().Count()
};
If I understand it correctly you could do something like: (i've done only using linq, but the return null inside a select it's not a good practice). A foreach would server you better)
var countsVm = new CountsVm(){
DepartmentCount = 0,
StudentCount = 0
};
var counts =
_db.Departments.All()
.Select(c => new {key = 1, count = 0})
.Union(_db.Students.All().Select(c => new {key = 2, count= 0}))
.GroupBy(c=>c.key)
.Select(x => {
countsVm.DepartmentCount += x.Count(d => d.key == 1);
countsVm.StudentCount += x.Count(s => s.key == 2);
return null;
});
public class CountsVm
{
public int StudentCount { get; set; }
public int DepartmentCount { get; set; }
}
Try to remove All from query and run FirstOrDefault()
var counts =
_db.Departments.
.Select(c => new {key = 1, count = 0})
.Union(_db.Students.Select(c => new {key = 2, count= 0}))
.GroupBy(c=>c.key)
.Select(x => new CountsVm()
{
DepartmentCount = x.Count(d => d.key == 1),
StudentCount = x.Count(s => s.key == 2)
}).FirstOrDefault();
public class CountsVm
{
public int StudentCount { get; set; }
public int DepartmentCount { get; set; }
}
I have two class lists teams, and matches
In my teams class I have a int id and string name and on the matches class I have int team1Id and
team2Id, I want to know if there's a way to count on matches list how many matches a team participate..
Like
if(teamlist.id == matcheslist.team1 || teamlist.id == matcheslist.team2) count++;
Sorry if I didn't explain very well, english isn't my first language.
EDIT1:
Here the lists,
public List teams= new List();
public List matches = new List();
Team and Match are my classes, with basic information, id and name for Team and id, team1 and team2 for Match, I tried to use find but it only return one result
Given this sort of setup:
class Team {
public int TeamId { get; set; }
}
class Match {
public Team[] Teams { get; set; }
}
var matches = new List<Match>() {
new Match() {
Teams = new Team[] {
new Team() { TeamId = 1 },
new Team() { TeamId = 2 }
}
},
new Match() {
Teams = new Team[] {
new Team() { TeamId = 1 },
new Team() { TeamId = 15 }
}
}
};
You can count them like so:
var teamOneGameCount = matches.Count(match => match.Teams.Any(team => team.TeamId == 1));
var teamTwoGameCount = matches.Count(match => match.Teams.Any(team => team.TeamId == 2));
var teamFifteenGameCount = matches.Count(match => match.Teams.Any(team => team.TeamId == 15));
here is linq example:
List<Teamlist> item1 = new List<Teamlist>();
List<Matcheslist> item2 = new List<Matcheslist>();
var count = item1.Count(c => item2.Any(c2 => c2.Id2 == c.Id1));
I think you want something like:
List<teamlist> list = new List<teamlist>();
int count = 0;
list.Add(team1);
list.Add(team2);
...
foreach(teamlist tl in list)
{
if(teamlist.id == matcheslist.team1 || teamlist.id == matcheslist.team2) count++;
}
Is that "List" keyword you need ?
Or you need a LINQ query operation like:
using System.Linq;
...
List<int> list = new List<int>();
list.AddRange(new []{1,2,3,4,5,6});
int count = list.Count(n => n > 2); // 4