I have one table which is looking like this
ID
UserID
UserEncryptValue
1
1
abcd
2
2
1234
3
3
qwert
4
1
rstuv (Common value for user 1 and 2)
5
2
rstuv (Common value for user 1 and 2)
6
2
78901 (Common value for user 2 and 3)
7
3
78901 (Common value for user 2 and 3)
8
1
Hello123 (Common value for user 1,2 and 3)
9
2
Hello123 (Common value for user 1,2 and 3)
10
3
Hello123 (Common value for user 1,2 and 3)
Now I want to find if user 1 and 2 or 1, 2 and 3 have common value or not with use of Linq.
Assuming you're mapping that table to an actual object like 'UserData' like this:
public class UserData
{
public int Id { get; set; }
public int UserId { get; set; }
public string UserEncryptValue { get; set; }
}
You can get the common values like this (userData is a list of UserData and represents your data):
var searchId = 1;
var commonValues = userData.GroupBy(user => user.UserEncryptValue)
.Where(grp => grp.Count() > 1 && grp.Any(usr => usr.UserId == searchId))
.SelectMany(u => u);
This groups on the UserEncryptValue and only selects groups that have more than 1 value (has a match) and at least 1 of the user ids is equal to the searchId.
Table.Where(n => Table.Any(o => !(o === n) && o.UserEncryptValue == n.UserEncryptValue)).Select(n => n.UserID)
Will return a collection of user id's for members of collection Table where at least on other member of the table has the same value UserEncryptValue but is not the same object
Learn LINQ to understand how this works and what you can do to tweak it.
One way is to use GroupBy. In this case you would group by UserEncryptValue.
You can then examine each group and check which users are in each group.
Related
Below are the records where we are trying to group the records by the following OR conditions:
Name is same
Email is same
Phone is same
Is there a way in LINQ to Group By with Or condition?
Name Email Phone Id
--- --------- ------------ ----------
Rohan rohan#s.com NULL 1
R. Mehta rohan#s.com 9999999999 2
Alex alex#j.com 7777777777 3
Lisa John john#j.com 6666666666 4
Lisa lisa#j.com 6666666666 5
Siri siri#s.com NULL 6
RM info#s.com 9999999999 7
Lisa NULL NULL 8
Lisa John m#s.com 7777777757 9
Output Expected
Group 1:
Key: Rohan
RecordIds: 1,2,7 (As `Id:1` has same email as `Id:2`, `Id:2` has same
phone number as `Id:7`.)
Group 2:
Key: Lisa John
RecordIds: 4,5,8,9 (As `Id:4` has same phone number as `Id:5`. While `Id:5`
has the same name as `Id:8`. As `Id:9` has the same name
as `Id: 4`, include that)
3 and 6 are not part of the output as the output are only group with more than 1 record
Key can be anything I just put in a random key.
If record 9 had email-id: rohan#s.com then:
Output
Group 1:
Key: Rohan
RecordIds: 1,2,7,4,5,8,9
NOTE: Input is SQL table to be read through LINQ to SQL. So query performance too has to be taken into account.
Crud Solution:
A dirty solution would be the following:
Group the records by Name -> store result in var gl-1
Group the records by Email -> store result in var gl-2
Group the records by Phone -> store result in var gl-3
Take each result in gl-1 check if corresponding id is present in gl-2,gl-3. If so include those ids in gl-1
Take each result in gl-2 check if corresponding id is present in any result in gl-1 is so, include the exclusive ids to gl-1 record. If the loop encounters a result which is not present in gl-1, include it as a result in gl-1.
Do step 5 for gl-3.
GroupBy requires some definition of "equality". You could define an EqualityComparer with the logic you want, but you'll get inconsistent results. Your grouping breaks the transitive property of equality needed for grouping. In other words, if A=B and B=C then A=C must be true.
For example, the following pairs of items would be in the same group ("equal"):
A, B, C and A, D, E
A, D, E and F, G, E
but
A, B, C and F, G, E
would not be in the same group.
To get the output you want (e.g. item 9 in multiple groups) you'd need to use standard looping to recursively find all items that are "equal" to the first, then all items that are "equal" to that group, then all items that are "equal" to the third group, etc. Linq is not going to be very helpful here (except possibly for the searching within each recursive call).
Linq queries run linear which means once it has passed a new possible group it cant go back and work with it.
lets asume
public class aperson
{
public string Name;
public string Email;
public string Phone;
public int ID;
public aperson(string name,string email,string phone,int id)
{
Name = name;
Email = email;
Phone = phone;
ID = id;
}
}
example
new aperson("a","a#","1",1),
new aperson("b","b#","2",2),
new aperson("a","c#","2",3)
Iteration 1: create group 1 with ("a","a#","1") values
Iteration 2: create group 2 with ("b","b#","2") values
Iteration 3: here the system will have to group it with either group 1 or with group 2 but not both.
To fix this your iterator will have to go back to group 2 and group 1 and join them.
To solve this you will have to break it into steps.
Step1. Create the groups
Step2. Group by the created groups.
I think there are much better ways to do this. I am just illustrating the flow how this problem needs to be approached and why.
Code for solution
public static Dictionary<string, int> group = new Dictionary<string, int>();
public static void adduniquevalue(aperson person,int id)
{
if (person.Email != null && !group.Keys.Contains(person.Email))
{
group.Add(person.Email, id);
}
if (person.Phone != null && !group.Keys.Contains(person.Phone))
{
group.Add(person.Phone, id);
}
if (person.Name != null && !group.Keys.Contains(person.Name))
{
group.Add(person.Name, id);
}
}
public static void CreateGroupKeys(aperson person)
{
int id = group.Count;
List<int> groupmatches = new List<int>();
if (person.Email != null && group.Keys.Contains(person.Email))
groupmatches.Add(group[person.Email]);
if (person.Phone != null && group.Keys.Contains(person.Phone))
groupmatches.Add(group[person.Phone]);
if (person.Name != null && group.Keys.Contains(person.Name))
groupmatches.Add(group[person.Name]);
if (groupmatches.GroupBy(x=>x).Count() > 1)
{
int newid = groupmatches[0];
group.Keys.Where(key => groupmatches.Contains(group[key]))
.ToList()
.ForEach(key => { group[key] = newid; });
}
if (groupmatches.Count == 0)
adduniquevalue(person, id);
else adduniquevalue(person, groupmatches[0]);
}
public static int GetGroupKey(aperson person)
{
if (person.Email != null && group.Keys.Contains(person.Email))
return group[person.Email];
if (person.Phone != null && group.Keys.Contains(person.Phone))
return group[person.Phone];
if (person.Name != null && group.Keys.Contains(person.Name))
return group[person.Name];
else return 0;
}
This will create your groups in a dictionary which you could use in a normal group by method later on.
Like so:
people.ForEach(x => CreateGroupKeys(x));
var groups = people.GroupBy(x => GetGroupKey(x)).ToList();
I have a list like below with more column. First two column is for entity A and second two column is for entity B. As you can see there is one to many relationship between these entities. So, I want to group this list to custom model.
QID ORDERNUMBER OPID POINT
1888 1 6902 4
1888 1 6903 3
1888 1 6904 2
1888 1 6905 1
1889 2 6906 4
1889 2 6907 3
1889 2 6908 2
1889 2 6909 1
1890 3 6910 4
1890 3 6911 3
1890 3 6912 2
1890 3 6913 1
First two column for Question object and other two column is for Options object list. This is my model
public class MyModel
{
public Question Question { get; set; }
public List<Option> Options { get; set; }
}
I need to group above table by QID. After that I need to take group key to Question object and assign QID's groups to list of Option object.
Simple demonstrade for one instance of MyModel should be;
MyModel > Question > QID:1888
ORDERNUMBER:1
> Options > [0] : OPID:6902
POINT:4
[1] : OPID:6903
POINT:3
[2] : OPID:6904
POINT:2
[3] : OPID:6905
POINT:1
Firstly, GroupBy QID, ORDERNUMBER, then, for each group, init the question and his questions
var models = list.GroupBy(q => new { q.QID, q.ORDERNUMBER})
.Select(g => new Model()
{
Question = new Question() { QID = g.Key.QID, ORDERNUMBER =g.Key.ORDERNUMBER} ,
Options = new List<Option>(g.ToList().Select(i => new Option() { OPID = i.OPID, POINT = i.POINT }))
})
I want to perform condition on last record of my below model :
public partial class TestPart
{
public int Id { get; set; }
public int TestId { get; set; }
public int Status { get; set; }
public virtual ICollection<Job> Jobs { get; set; }
}
Query :
var query = context.TestPart.OrderByDescending(tp=>tp.Id)
.Take(1)
.Where(tp => tp.TestId == 100 &&
tp.Status == 1 &&
tp.Jobs.All(j => j.Type == "Recurring")
Here I want to get Id of TestPart whose status = 1 and all jobs are recurring but this should only consider checking last record of test part
But I am unable to select Id of last TestPart in above query.
Update :
Id TestId Status
1 100 1
2 100 2
3 100 0
4 100 1
5 101 1
so here I want to filter out data based on TestId and then select last record for that specific TEST and then check out whether all job types are recurring for that last selected TestPart id i.e in above case TestPartId=4.
The explanation is a bit fragmented. In order to make sure that I'm answering to the right problem, these are my assumptions:
One Test has many TestPart children.
You want the last TestPart of a given test, not just the last entry of the table (regardless of test id).
You're trying to ascertain if this last item fits the criteria, thus making the end result of your code a boolean value.
You need to split the data retrieval and data validation steps here.
When you merge them, you get different results. You ask for the last item that fits the criteria. This means that in a list of 10 items (numbered 1 through 10 chronologically) you might end up getting item 8 if it fits the criteria and 9 and 10 do not fit the criteria.
From your description, I surmise that's not what you want. You want to take item 10 (regardless of whether it fits the criteria, and only then check if this item fits the criteria or not.
Think of it this way:
I want the last person named John who entered this building.
I want to see if the last person who entered the building is named John.
Your code is trying to do the first. But what you really want to do is the second.
The correct version of your code:
//Get the last testpart of the test.
TestPart item = context.TestPart
.Include(tp => tp.Jobs) //possibly optional dependent on lazy/eager loading.
.OrderByDescending(tp=>tp.Id)
.First(tp => tp.TestId == 100);
//Check if this item fits the criteria
bool isValid =
item.Status == 1
&& item.Jobs.All(j => j.Type == "Recurring");
isValid contains your answer.
Edit - just for completeness
There are ways to merge this into one query, but this makes the code easily prone to misinterpretation.
bool isLastItemValid = context.TestPart
.Where(tp => tp.TestId == 100)
.OrderByDescending(tp => tp.Id)
.Take(1)
.Any(tp =>
tp.Status == 1
&& tp.Jobs.All(j => j.Type == "Recurring");
This gives you the same result. It relies on the "trick" that calling Any() on a list with only one item really just evaluates the one item.
While technically correct, I find this version unnecessarily complicated, less readable, and more prone to developer misinterpretation.
Replace .Take(1).Where() with FirstOrDefault()
TestPart item = context.TestPart.OrderByDescending(tp => tp.Id)
.FirstOrDefault(tp => tp.TestId == 100 &&
tp.Status == 1 &&
tp.Jobs.All(j => j.Type == "Recurring");
int result = item.Id;
I think the appropriate thing to do is break it into steps. I do love a big LINQ statement like the next guy, but only when it elegantly represents the required logic. In this case you're to get a record, check its status, and return its ID, so why not express that in ROC?
var lastPart = context.TestPart.OrderByDescending(tp=>tp.Id)
.First();
bool match = (lastPart.TestId == 100 &&
lastPart.Status == 1 &&
lastPart.Jobs.All( j => j.Type == "Recurring"));
if (match) return lastPart.Id;
Relevant: Writing ROC (Really Obvious Code).
Here I have small table Like Employee
EmpId EmpName EmpType
1 John 1
2 Mick 3
3 Smith 2
I wrote a simple Linq query finding details Like
public Users GetUsers(int Id)
{ var x = from n in db.Users
where n.Username == Id
select n;
return x.FirstOrDefault();
}
when its get result as EmpId=1,EmpName=John,Emptype=1 Insted of 1 i need Admin from enum
enum TypeofEmp{
Admin=1,
HttpRequest=2,
Devoloper=3
};
How can i get these values
You can use Enum.GetName method:
Enum.GetName( typeof( TypeofEmp ), value );
Also, if you want to convert an int value to instance of your enum, you can do simple cast, for example:
var enumInstance = ( TypeofEmp )1;
Enum.GetValues returns an array of all values;
foreach(var value in Enum.GetValues(typeof(TypeofEmp)))
{
Console.WriteLine($"{(TypeofEmp)value} (integer value: {(int)value})");
}
// output:
Admin (integer value: 1)
HttpRequest (integer value: 2)
Devoloper (integer value: 3)
I have a generic list which needs to be filter based on another list (say, List<string>).
public class Model
{
public string ID { get; set;}
public string Make { get; set;}
}
List<Model> lstModel = new List<Model>();
And the lstModel is as follows
ID Make
---- -----------
5 MARUTI
4 BENZ
3 HYUNDAI
2 HONDA
1 TOYOTA
And i have another list which contains only car makers,ie
List<string> lstMakers = new List<string>() {"MARUTI", "HONDA"};
1) I need to filter lstModel which contains only items in lstMakers.
The output would be
ID Make
---- -----------
5 MARUTI
2 HONDA
2) Based on output (1), need another list of ids with 1 increment to each item in descending order,
The output would be List<int> ie,
6
5
3
2
Note: Using lambda expression / linq is more preferable
1 )
var list1 = lst.Where(x=>lstMakers.Contains(x.Make)).ToList();
2)
var list2 = list1.Select(x=>int.Parse(x.ID)+1)
.Concat(list1.Select(x=>int.Parse(x))
.OrderByDescending(x=>x)
.ToList();
Use Enumerable.Join and OrderByDescending:
var models = from maker in lstMakers
join model in lstModel
on maker equals model.Make
select model;
List<int> result = models
.Select(m => int.Parse(m.ID) + 1)
.OrderByDescending(i => i)
.ToList();
However, this selects two ints since only two models match. Your result contains 4 ints. I assume that your result is not related to your sample, is it?
but i need both the item and its incremental value,...
Now it's clear, use Enumerable.SelectMany with an array:
List<int> result = models
.Select(m => int.Parse(m.ID))
.SelectMany(id => new int[]{ id, id + 1 })
.OrderByDescending(id => id)
.Distinct()
.ToList();