Convert Linq Query Expression into Linq Lambda Expression - c#

I have written this Linq Query for two class stduents and universites to extract students those are in XXX university.
I like to use the linq lambda expression instead query. I tried to convert but failed. Can anyone help.
IEnumerable<Student> Students = from student in this.students
join university in universities
on student.UniversityId equals university.Id
where university.Name == "XXX"
select student;
Now, How should I use Where operator?
var students = this.students.Join(universities,
s => s.UniversityId,
u => u.Id,
(std, uni) => std);

You can use below code sample.
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
class University
{
public int Id { get; set; }
public string Name { get; set; }
}
class Student
{
public int Id { get; set; }
public int UniversityId { get; set; }
}
class Program
{
static void Main(string[] args)
{
IList<University> universities = new List<University>
{
new University { Id = 1, Name = "u1" },
new University { Id = 2, Name = "u2" }
};
IList<Student> students = new List<Student>
{
new Student { Id = 1, UniversityId = 1 },
new Student { Id = 2, UniversityId = 1 },
new Student { Id = 3, UniversityId = 2 },
new Student { Id = 4, UniversityId = 2 }
};
IEnumerable<Student> stus = universities
.Join(students, university => university.Id, student => student.UniversityId, (university, student) => new { university, student })
.Where(j => j.university.Name == "u2")
.Select(j => j.student);
}
}
}

Related

Using where in LINQ select new statement for specific columns

I'm working on a class assignment and got a bit lost in LINQ.
I have 3 tables, 'oltandok' contains the data of persons, 'preferenciak' contains the preferred vaccine of that person with 3 columns:
an FK for table oltandok
a number indicating the order of preferences (1 is highest, 6 is lowest preferred)
an FK for another table containing the data on the vaccines called 'vakcinak'
I would like to display the data in a DataGridView the following way:
Personal data and the preferred vaccines in different columns:
Pref1 - Name of the vaccine where pref == 1
Pref2 - Name of the vaccine where pref == 2
etc.
This is where I am with my code, but I'm not sure how to select the preferences properly.
manu_rogz.DataSource = ( from x in context.oltandok
join y in context.preferencia on x.TAJ equals y.oltandok_FK
select new
{
TAJ = x.TAJ,
Nev = x.nev,
Szuletesnap = x.birthdate,
Pref1 = ???
Pref2 = ???
}
).ToList();
Because the preferenciak table contains multiple rows per person, you will need to perform some grouping.
Here is some very rough code which illustrates one way to do that.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var persons = new List<Person> { new Person { ID = 11, PersonName = "Alice" }, new Person { ID = 22, PersonName = "Bob" } };
var vaccines = new List<Vaccine> { new Vaccine(){ ID = 111, VaccineName= "Pfizer" }, new Vaccine(){ ID = 222, VaccineName = "Moderna" } };
var preferences = new List<VaccPref>
{
new VaccPref() { Person_FK = 11, Preference = 1, Vaccine_FK = 111 },
new VaccPref() { Person_FK = 11, Preference = 2, Vaccine_FK = 222 },
new VaccPref() { Person_FK = 22, Preference = 1, Vaccine_FK = 222 },
new VaccPref() { Person_FK = 22, Preference = 2, Vaccine_FK = 111 }
};
var prefsWithVaccNames = preferences.Join(vaccines, p => p.Vaccine_FK, v => v.ID, (pref, vaccine) => new Tuple<VaccPref, string>(pref, vaccine.VaccineName));
var groupedPrefs = prefsWithVaccNames.GroupBy(p => p.Item1.Person_FK);
var personPrefs = new List<PersonPrefs>();
foreach (var group in groupedPrefs)
{
personPrefs.Add(
new PersonPrefs()
{
Person_FK = group.Key,
Pref1 = group.Single(v => v.Item1.Preference == 1).Item2,
Pref2 = group.Single(v => v.Item1.Preference == 2).Item2,
});
}
var personPrefsWithPersonNames =
personPrefs.Join(
persons,
pp => pp.Person_FK,
p => p.ID,
(pp, p) => new NamedPersonPrefs() { Name = p.PersonName, Pref1 = pp.Pref1, Pref2 = pp.Pref2 }).ToArray();
}
}
class Person
{
public int ID { get; set; }
public string PersonName { get; set; }
}
class VaccPref
{
public int Person_FK { get; set; }
public int Preference { get; set; }
public int Vaccine_FK { get; set; }
}
class Vaccine
{
public int ID { get; set; }
public string VaccineName { get; set; }
}
class PersonPrefs
{
public int Person_FK { get; set; }
public string Pref1 { get; set; }
public string Pref2 { get; set; }
}
class NamedPersonPrefs
{
public string Name { get; set; }
public string Pref1 { get; set; }
public string Pref2 { get; set; }
}
This is a self-contained C# program which should produce a result similar to what you're after. You will of course need to adjust the class definitions (and change the table names) to suit your needs.
I've used LINQ's fluent syntax but you can use the SQL-like version if you prefer.

Compare list against other list and modify

Supposed that I have these classes
public class Subject
{
public int Id { get; set; }
public string Category { get; set; }
public string Type { get; set; }
}
public class Student
{
public int Id { get; set; }
public List<MySubject> MySubjects { get; set; }
}
public class MySubject
{
public int Id { get; set; }
public string Category { get; set; }
public string Type { get; set; }
public string Schedule { get; set; }
public string RoomNumber { get; set; }
}
sample data
var subjects = new List<Subject>()
{
new Subject(){ Id = 1, Category = "Mathematics", Type = "Algebra" },
new Subject(){ Id = 2, Category = "Computer Science", Type = "Pascal" }
};
var student = new Student()
{ Id = 1, MySubjects = new List<MySubject>() {
new MySubject() {Id = 1, Category = "Mathematics", Type = "Algebra" },
new MySubject() {Id = 3, Category = "Mathematics", Type = "Trigonometry"},
}
};
//TODO: Update list here
student.MySubjects.ForEach(i => Console.WriteLine("{0}-{1}-{2}\t", i.Id, i.Category, i.Type));
the above line of code returns
1-Mathematics-Algebra
3-Mathematics-Trigonometry
which is incorrect. I need to return this
1-Mathematics-Algebra
2-Computer Science-Pascal
Basically I would like to modify and iterate the student.MySubjects and check its contents against subjects.
I would like to remove the subjects (3-Mathematics-Trigonometry) that are not present in the subjects and also ADD subjects that are missing (2-Computer Science-Pascal).
Can you suggest an efficient way to do this by searching/comparing using Category + Type?
Try like below.
// Remove those subjects which are not present in subjects list
student.MySubjects.RemoveAll(x => !subjects.Any(y => y.Category == x.Category && y.Type == x.Type));
// Retrieve list of subjects which are not added in students.MySubjects
var mySubjectsToAdd = subjects.Where(x => !student.MySubjects.Any(y => y.Category == x.Category && y.Type == x.Type))
.Select(x => new MySubject() {
Id = x.Id,
Category = x.Category,
Type = x.Type
}).ToList();
// If mySubjectsToAdd has any value then add it into student.MySubjects
if (mySubjectsToAdd.Any())
{
student.MySubjects.AddRange(mySubjectsToAdd);
}
student.MySubjects.ForEach(i => Console.WriteLine("{0}-{1}-{2}\t", i.Id, i.Category, i.Type));
// make an inner join based on mutual values to filter out wrong subjects.
var filteredList =
from mySubject in student.MySubjects
join subject in subjects
on new { mySubject.Category, mySubject.Type }
equals new { subject.Category, subject.Type }
select new MySubject { Id = mySubject.Id, Category = mySubject.Category, Type = mySubject.Type };
// make a left outer join to find absent subjects.
var absentList =
from subject in subjects
join mySubject in filteredList
on new { subject.Category, subject.Type }
equals new { mySubject.Category, mySubject.Type } into sm
from s in sm.DefaultIfEmpty()
where s == null
select new MySubject { Id = subject.Id, Category = subject.Category, Type = subject.Type };
student.MySubjects = filteredList.ToList();
student.MySubjects.AddRange(absentList.ToList());

From a one to many situation how do I get common items in Entity Framework

I just started with Entity Framework and I was having difficulty generating a query for the following situation.
I currently have two model classes Student and Sport. A student can play multiple sports. This is what my models look like
public class DbContext : DbContext
{
public DbContext(): base("name=DbContext")
{
}
public DbSet<Student> MyStudents { get; set; }
public DbSet<Sport> MySports { get; set; }
}
public class Student
{
public List<Sport> Actions { get; set; }
public string Name { get; set; }
}
public class Sport
{
public string SportName { get; set; }
}
My question is how do I get a list of all sports played by all the students? In short I am looking for common sports. So basically in the following case
Student A played Sports : Soccer , Tennis , Bowling
Student B played Sports : Soccer , Tennis ,
Student C played Sport : Tennis
Then only Tennis should be returned
Using the DB schema you've provided you can get the common sports checking sports of each student:
var sports = new[]
{
new Sport { SportName = "Tennis" },
new Sport { SportName = "Soccer" },
new Sport { SportName = "Bowling" }
};
var students = new[]
{
new Student
{
Name = "Student 1",
Actions = sports
},
new Student
{
Name = "Student 2",
Actions = new[] { sports[0], sports[1] }
},
new Student
{
Name = "Student 3",
Actions = new[] { sports[0] }
}
};
// Or
var sports = context.Sports;
var students = context.Students;
// In case students' sports are objects (as in this sample) you can use such a query:
var commonSports = sports.Where(sport =>
students.All(student => student.Actions.Contains(sport)));
// In case you're going to check the sports by name, this:
var commonSports = sports.Where(sport =>
students.All(student => student.Actions.Any(studSport =>
studSport.SportName == sport.SportName)));
Console.WriteLine($"Comon sports: {string.Join(",", commonSports.Select(i => i.SportName))}");
// To get only names of common sports:
var sportNames = commonSports.Select(i => i.SportName);
Console.Read();
If you use a relational database it would be easier and (as for me) more logical to implement many-to-many relationship as described here:
var context = new DbContext()
var unique = context.MyStudents.SelectMany(student => student.Actions.Select(sport => sport.SportName)).Distinct();
you just do this :
var commonSports = Context.Students.SelectMany(x=>x.Actions).GroupBy(x => x.SportName).Where(x=>x.Count()==items.Count(c=>c.Actions!=null)).Select(x=>x.Key).ToList();
I hope it been helpful .
To achieve this you might want to first set up some kind of model class, this isn't strictly necessary but might make things clearer for you:
public class StudentWithSports()
{
public string Name {get;set;}
public List<string> Sports {get;set;}
}
You can then populate your model from your context:
using(var context = new DbContext())
{
List<StudentWithSports> list = context
.Students
.Include(stu => stu.Actions)
.Select(stu => new StudenWithSports
{
Name = stu.Name,
Sports = stu.Actions.Select(act => act.SportName).ToList()
}).ToList();
}
If you don't want to create a model you could just do:
var list = context
.Students
.Include(stu => stu.Actions)
.Select(stu => new {
Name = stu.Name,
Sports = stu.Actions.Select(act => act.SportName).ToList()
}).ToList();
Which will give you a list of anonymous objects with the same properties.
The essence of my answer is the linq query, but I created a couple of classes to model your EF classes to show it works.
Student student1 = new Student
{
Name = "John",
Actions = new List<Sport>
{
new Sport { SportName = "Tennis" },
new Sport { SportName = "Soccer" },
new Sport { SportName = "Bowling" }
}
};
Student student2 = new Student
{
Name = "Mary",
Actions = new List<Sport>
{
new Sport { SportName = "Tennis" },
new Sport { SportName = "Soccer" }
}
};
Student student3 = new Student
{
Name = "Jane",
Actions = new List<Sport>
{
new Sport { SportName = "Tennis" }
}
};
IEnumerable<Student> students = new List<Student>
{
student1,
student2,
student3
};
var query = from s in students
select new
{
s.Name,
Sports = from sp in s.Actions
select sp.SportName
};
var result = query.ToList();
for (int i = 0; i < result.Count(); i++)
{
Console.Write(result[i].Name + " played sports: ");
foreach (var sport in result[i].Sports)
Console.Write(" " + sport);
Console.WriteLine();
}
Well your Db design isn't right because you have many to many relation between MyStudents and MySports tables. You have to add joint table between Students and Sports. You can call it StudentsSports
public class DbContext : DbContext
{
public DbContext(): base("name=DbContext")
{
}
public DbSet<Student> MyStudents { get; set; }
public DbSet<StudentsSport> StudentsSports { get; set; }
public DbSet<Sport> MySports { get; set; }
}
public class Student
{
public int ID { get; set; }
public List<StudentsSport> Actions { get; set; }
public string Name { get; set; }
}
public class Sport
{
public int ID { get; set; }
public string SportName { get; set; }
}
public class StudentsSport
{
public int ID { get; set; }
[ForeignKey(Student)]
public int StudentID { get; set; }
[ForeignKey(Sport)]
public int SportID { get; set; }
}
Then you can just do
var listOfActions = MyStudents.Select(s => s.Actions.Select(a => a.SportID));
var intersection = listOfActions
.Skip(1)
.Aggregate(
new HashSet<T>(listOfActions.First()),
(h, e) => { h.IntersectWith(e); return h; }
);
EDIT:
If you have students without sports then you will always get empty intersection list. If you don't want that then you will have to filter them
var listOfActions = MyStudents.Select(s => s.Actions.Select(a => a.SportID)).Where(c => c.Any());

How to join data inside array of objects in MongoDB

I have an order collection that looks like :
{
"_id" : ObjectId("5d732c3eb52e554e5ce19d96"),
"UpdatedDate" : ISODate("2019-09-07T04:04:14.223Z"),
"customerId" : ObjectId("5d579deac9406a6db80960b7"),
"items" : [
{
"product" : ObjectId("5d231c3fb52e554e5ce12d14"),
"quantity" : 2
},
{
"product" : ObjectId("5836bc0b291918eb42966320"),
"quantity" : 1
}
]
}
while product collection :
[
{
"_id" : ObjectId("5d231c3fb52e554e5ce12d14"),
"name" : "Coffee"
},
{
"_id" : ObjectId("5836bc0b291918eb42966320"),
"name" : "Cake"
}
]
How should I do the join through MongoDB .Net driver in order to get the product name and customer name projected into the result ?
Thank you
here's a full example of how to join 2 collections using the IMongoQueryable interface of the driver. below code uses MongoDB.Entities for brevity but joining part is exactly the same for the official driver. simply replace DB.Queryable<Order>() with collection.AsQueryable()
using MongoDB.Entities;
using MongoDB.Driver.Linq;
using System;
using System.Linq;
namespace StackOverflow
{
public class Program
{
public class Customer : Entity
{
public string Name { get; set; }
public Many<Order> Orders { get; set; }
public Customer() => this.InitOneToMany(() => Orders);
}
public class Order : Entity
{
public One<Customer> Customer { get; set; }
public OrderItem[] Items { get; set; }
}
public class Product : Entity
{
public string Name { get; set; }
public decimal Price { get; set; }
}
public class OrderItem //embedded entity inside order
{
public One<Product> Product { get; set; }
public int Quantity { get; set; }
}
private static void Main(string[] args)
{
//initialize db connection
new DB("cafe-db", "localhost", 27017);
//create some products
var cake = new Product { Name = "Cake", Price = 9.99m };
var coffee = new Product { Name = "Coffee", Price = 6.66m };
cake.Save();
coffee.Save();
//create a customer
var customer = new Customer { Name = "Mike Posner" };
customer.Save();
//create an order
var order = new Order
{
Customer = customer.ToReference(),
Items = (new[]
{
new OrderItem {
Product = cake.ToReference(),
Quantity = 2
},
new OrderItem {
Product = coffee.ToReference(),
Quantity = 1
}
})
};
order.Save();
//link the order to the customer
customer.Orders.Add(order);
// retrieve the data needed to generate an order summary/ invoice given just an order id
var orderID = order.ID;
var result = DB.Queryable<Order>()
.Where(o => o.ID == orderID)
.SelectMany(o => o.Items,
(o, i) => new { productID = i.Product.ID, productQTY = i.Quantity })
.Join(
DB.Queryable<Product>(), //foregign collection
x => x.productID, //local field
p => p.ID, //foreign field
(x, p) => new //projection
{
productName = p.Name,
productPrice = p.Price,
productQty = x.productQTY
})
.ToList();
var customerName = DB.Find<Order>()
.One(orderID)
.Customer.ToEntity()
.Name;
Console.WriteLine($"CUSTOMER: {customerName}");
foreach (var x in result)
{
Console.WriteLine($"PRODCUT: {x.productName} | QTY: {x.productQty} | PRICE: {x.productPrice}");
}
Console.Read();
}
}
}

Fill object from dataset using grouping in Linq

I am trying to load the data from datatable to objects using Linq. Below is my scenario. I have below table structure and data:
seq name id class
1 Rajesh 101 B
1 kumar 102 B
1 sandeep 104 A
2 Myur 105 B
2 Bhuvan 106 C
3 Siraz 107 A
Below is my class structures
public class student
{
public string name {get;set;}
public string id { get; set; }
public string meritClass { get; set; }
}
public class stdGroup
{
public int seqId{get;set;}
public List<student> students;
}
As a final output I should get a Student constructed for each seq. stdGroup object should be created grouping by seq [three objects].
Example:
stdGroup object 1 would contain 3 student objects
stdGroup object 2 would contain 2 student objects
Can anyone please help me.
This should do what you need (assuming by DataTable you mean DataTable):
List<stdGroup> stdGroups = myDataTable
.AsEnumerable()
.GroupBy(a => a.Field<int>("Seq"), a => new student() { id = a.Field<string>("Id"), name = a.Field<string>("name"), meritClass = a.Field<string>("class") })
.Select(a => new stdGroup() { seqId = a.Key, students = a.ToList() })
.ToList();
To break it down, firstly, get the datatable rows into a state where we can use linq,
.AsEnumerable()
Now, do the groupings - selecting seq as the key for the group, and build a student object for every entry, which will get assigned to the corresponding group.
.GroupBy(a => a.Field<int>("Seq"), a => new student() { id = a.Field<string>("Id"), name = a.Field<string>("name"), meritClass = a.Field<string>("class") })
Now, for each group create the stdGroup object, and populate the seq property from our group keys, and take the content of each group, and assign that to the students property.
.Select(a => new stdGroup() { seqId = a.Key, students = a.ToList() })
Finally, and optionally, convert to a list instead of enumerable.
.ToList();
You can also check out my implementation:
public class dbStudent
{
public int seq;
public string name;
public int id;
public string meritClass;
}
public class student
{
public string name { get; set; }
public int id { get; set; }
public string meritClass { get; set; }
}
public class stdGroup
{
public int seqId { get; set; }
public List<student> students { get; set; }
}
class Program
{
static void Main(string[] args)
{
var dbStudebts = new List<dbStudent>();
dbStudebts.Add(new dbStudent { seq = 1, name = "Rajesh", id = 101, meritClass = "B" });
dbStudebts.Add(new dbStudent { seq = 1, name = "kumar", id = 102, meritClass = "B" });
dbStudebts.Add(new dbStudent { seq = 1, name = "sandeep", id = 104, meritClass = "A" });
dbStudebts.Add(new dbStudent { seq = 2, name = "Myur", id = 105, meritClass = "B" });
dbStudebts.Add(new dbStudent { seq = 2, name = "Bhuvan", id = 106, meritClass = "C" });
dbStudebts.Add(new dbStudent { seq = 3, name = "Siraz", id = 107, meritClass = "A" });
var result = (from o in dbStudebts
group o by new { o.seq } into grouped
select new stdGroup()
{
seqId = grouped.Key.seq,
students = grouped.Select(c => new student()
{
name = c.name,
id = c.id,
meritClass = c.meritClass
}).ToList()
}).ToList();
}
}

Categories