I have created a query that only shows students that have a date of birth that is less than 1990. I am trying to express this same query as a Lambda query but do not know how to go about it. This is my code so far:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int ID { get; set; }
public DateTime DateOfBirth { get; set; }
}
class LINQ2
{
static void Main()
{
IEnumerable<Student> students = new List<Student>()
{
new Student {FirstName = "Jim", LastName = "Smith", DateOfBirth = new DateTime(1990, 5, 21), ID = 1},
new Student {FirstName = "Diane", LastName = "Sawyer", DateOfBirth = new DateTime(1992, 11, 1), ID = 2},
new Student {FirstName = "Steve", LastName = "Thomas", DateOfBirth = new DateTime(1994, 4, 4), ID = 3},
new Student {FirstName = "Pablo", LastName = "Dicaz", DateOfBirth = new DateTime(1973, 3, 30), ID = 4},
new Student {FirstName = "Hannu", LastName = "Korppi", DateOfBirth = new DateTime(1988, 6, 16), ID = 5},
new Student {FirstName = "Marie", LastName = "St. Claude", DateOfBirth = new DateTime(1982, 1, 19), ID = 6}
};
IEnumerable<Student> query = from s in students
where s.DateOfBirth.Year < 1990
orderby s.FirstName
select s;
foreach (Student stud in query)
{
Console.WriteLine(stud.FirstName);
Console.ReadLine();
}
}
}
}
If you mean the extension method syntax by Lambda query then you can do it like this:
var query = students
.Where(s => s.DateOfBirth.Year < 1990)
.OrderBy(s => s.FirstName);
Related
My GroupBy is performing well. I'm getting the Output
I need to Sort the Group Names
The Brown Color Block, represents the Group.
The Red Color Box within the Brown Color Block, represents the Manager
Peter Block (Brown Box) Should Come first
Raj Block (Brown Box) Should Come Second
Sunny Block (Brown Box) Should Come Third
Each Block Should Group By Boss(Manager) and Assistant (Boss don't have the
SID). After GroupBy the Name should be in Sorted Order, within the Group
the Assistant Names are also in the Sorted Order.
The Model Classes:
public class Person
{
public int ID { get; set; }
public int SID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
public string Role { get; set; }
}
public class Boss
{
public int ID { get; set; }
public int SID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
public string Role { get; set; }
public List<Person> Employees { get; set; }
}
The Main Functionality Source Code:
void Main()
{
List<Boss> BossList = new List<Boss>()
{
new Boss()
{
ID = 101,
Name = "Sunny",
Department = "Development",
Gender = "Male",
Role = "Manager",
Employees = new List<Person>()
{
new Person() {ID = 101, SID = 102, Name = "Peter", Department = "Development", Gender = "Male", Role = "Assistant"},
new Person() {ID = 101, SID = 103, Name = "Emma Watson", Department = "Development", Gender = "Female", Role = "Assistant"},
}
},
new Boss()
{
ID = 104,
Name = "Raj",
Department = "Development",
Gender = "Male",
Role = "Manager",
Employees = new List<Person>()
{
new Person() {ID = 104, SID = 105, Name = "Kaliya", Department = "Development", Gender = "Male", Role = "Assistant"},
new Person() {ID = 104, SID = 103, Name = "Emma Watson", Department = "Development", Gender = "Female", Role = "Assistant"},
},
},
new Boss()
{
ID = 102,
Name = "Peter",
Department = "Development",
Gender = "Male",
Role = "Manager",
Employees = new List<Person>()
{
new Person() {ID = 102, SID = 105, Name = "Kaliya", Department = "Development", Gender = "Male", Role = "Assistant"},
new Person() {ID = 102, SID = 103, Name = "Emma Watson", Department = "Development", Gender = "Female", Role = "Assistant"},
}
}
};
List<Person> EmpList = BossList.SelectMany(i =>
new[] {
new Person()
{
ID = i.ID,
SID = i.SID,
Name = i.Name,
Gender = i.Gender,
Department = i.Department,
Role = i.Role
}
}.Concat(i.Employees)
).ToList().GroupBy(s => s.ID).SelectMany(h => h.GroupBy(g => g.SID).SelectMany(u => u.OrderBy(k=> k.Name))).ToList();
}
You can do by adding the ThenBy extension method after the Order by to apply the secondary sort. In fact, the ThenBy can be called multiple times for sorting on multiple property. I have modified the last line of your code to show how you can achieve this.
).ToList().GroupBy(s => s.ID).SelectMany(h => h.GroupBy(g => g.SID).SelectMany(u => u.OrderBy(k=> k.Name).ThenBy(l => l.<<secondproperty>>))).ToList();
The datastructure already establishes the groups. There is no need to re-group.
List<Person> result = (
from boss in BossList
order by boss.Name
let orderedEmployees = boss.Employees.OrderBy(emp => emp.Name)
let bossPerson = new Person(boss)
let people = new List<Person>() { bossPerson }.Concat(orderedEmployees)
from person in people
select person).ToList();
If you prefer the lambda syntax:
List<Person> result = BossList
.OrderBy(boss => boss.Name)
.SelectMany(boss => {
IEnumerable<Person> orderedEmployees = boss.Employees.OrderBy(emp => emp.Name);
Person bossPerson = new Person(boss);
return new List<Person>() { bossPerson }.Concat(orderedEmployees);
})
.ToList();
I have a list of Person and a list of BirthDates and I would like to intersect person.BirthYear with BirthDates.Year
Intersects seems to need both lists to be of same type so my question is how to have a func or so which will allow to me write my own custom matching logic?
You can perform a Join which doesn't needs both sequences to be of same type.
This answer should help you with the syntax.
Here is code snippet for your case:
using System;
using System.Linq;
namespace Intersects
{
class Person
{
public string Name { get; set; }
public DateTime BirthDate { get; set; }
}
class Program
{
static void Main(string[] args)
{
var persons = new[]
{
new Person() {Name = "Jack", BirthDate = new DateTime(1990, 1, 1)},
new Person() {Name = "Joe", BirthDate = new DateTime(1970, 9, 9)},
new Person() {Name = "Ivan", BirthDate = new DateTime(1991, 2, 2)},
};
var birthDates = new[]
{
new DateTime(1990, 1, 1),
new DateTime(1991, 2, 2),
new DateTime(1991, 3, 3),
};
var joined = from p in persons
join bd in birthDates
on p.BirthDate.Year equals bd.Year // Your own custom logic
select p;
foreach (var person in joined)
{
Console.WriteLine(person.Name);
}
// Output:
//Jack
//Ivan
//Ivan
}
}
}
suppose that I have this List of Employees representing a table of employees.
public class Employee
{
public string Name { get; set; }
public string Department { get; set; }
public string Function { get; set; }
public decimal Salary { get; set; }
public DateTime EntryDate { get; set; }
public static List<Employee> GetEmployeesList()
{
return new List<Employee>() {
new Employee() { EntryDate = new DateTime(2011, 05, 01), Name = "Fons", Department = "Finance", Function = "Trader", Salary = 6500 },
new Employee() { EntryDate = new DateTime(2013, 05, 02), Name = "Mary", Department = "Finance", Function = "BusinessAnalyst", Salary = 2500 },
new Employee() { EntryDate = new DateTime(2012, 04, 03), Name = "Alex", Department = "Finance", Function = "Trader", Salary = 2100 },
new Employee() { EntryDate = new DateTime(2013, 05, 04), Name = "Jim", Department = "R&D", Function = "Trainer", Salary = 3300 },
new Employee() { EntryDate = new DateTime(2010, 06, 05), Name = "Ellen", Department = "Dev", Function = "Developer", Salary = 2500 },
new Employee() { EntryDate = new DateTime(2000, 09, 06), Name = "Mike", Department = "Dev", Function = "Consultant", Salary = 5100 },
new Employee() { EntryDate = new DateTime(1999, 03, 07), Name = "Jack", Department = "R&D", Function = "Developer", Salary = 6100 },
new Employee() { EntryDate = new DateTime(1989, 01, 08), Name = "Demy", Department = "Dev", Function = "Consultant", Salary = 3300 }};
}
}
I want to be able to select only desired columns to be displayed.
Someting like :
public static List<Employee> SelectColumnsFromTable(List<Employee> employees, int[] selectedColumns)
{
// only select colums 1, 3 and 4
}
I have seen that it is possible with SQL and GridView, but in my case, the result will be printed on the console.
Is it possible using C# and Linq ?
As I understand the question, it is important to select specific properties of a class based on their index. If the relevant indices are provided to you by the user, you can use reflection to access the properties dynamically. The key points are Type.GetProperties and PropertyInfo.GetValue. I've put together a small sample to demonstrate:
using System;
using System.Collections.Generic;
using System.Linq;
public class Employee
{
public int Id {get; set;}
public string FirstName { get; set;}
public string LastName {get; set;}
}
public class Test
{
private static string[] GetColumnValues(Employee emp, params int[] cols)
{
var props = emp.GetType().GetProperties();
var values = new List<string>();
foreach(var i in cols)
{
if (i >= 0 && i < props.Length)
{
object value = props[i].GetValue(emp, null);
values.Add(value == null ? string.Empty : value.ToString());
}
}
return values.ToArray();
}
public static void Main()
{
var emp = new Employee() { Id = 1, FirstName = "John", LastName = "Smith" };
var values = GetColumnValues(emp, 0, 2);
Console.WriteLine(string.Join("\t", values));
}
}
Please note that referencing the properties by their index might not be very deterministic of you change the implementation later on. So selecting by the property's name might be more stable. Also, the column selector function GetColumnValues does not return Employees, but the values as a string array so you can use it in String.Join. You can use the function in Linq:
var rows = from x in listOfEmps select GetColumnValues(x, 0, 2);
foreach(var row in rows)
Console.WriteLine(string.Join("\t", row));
var items = (from i in v.db.DatabaseName
orderby i.EmpID descending
select new {i.Name, i.Function,i.Salary}).ToList();
var list = dt.AsEnumerable()
.Where(row => (int)row["demoid"] > 5)//your condition here
.Select(row => new
{
demoid = Convert.ToInt32(row["demoid"]),
demoname = row["demoname"] != null ?
row["demoname"].ToString() :
String.Empty
}).ToList();
Or you can define class:
public class myClass
{
public int demoid;
public string demoname;
}
and then:
List<myClass> list = dt.AsEnumerable()
.Where(row => (int)row["demoid"] > 5)
.Select(row => new myClass
{
demoid = Convert.ToInt32(row["demoid"]),
demoname = row["demoname"] != null ?
row["demoname"].ToString() :
String.Empty
}).ToList<myClass>();
this is selecting a particular value to a list. However you can use IList<myClass> classcollection= new List<myClass>(); and then add the particular list to class1 based on condition.
Note: here class collection can hold multiple list as u want the columns 1,3,4
Lets say I have a class called Person:
public class Person
{
public int Age { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
And a list of persons:
Person { Age = 20, FirstName = "John", LastName = "Joe" }
Person { Age = 20, FirstName = "John", LastName = "Joe" }
Person { Age = 10, FirstName = "James", LastName = "Dokes" }
What I want to have is a (new or old with a new property) list that groups the person by age, first name and last name AND I also want to know how many times that object has been grouped.
So the result of the above would be:
Person { Age = 20, FirstName = "John", LastName = "Joe", Count = 2 }
Person { Age = 10, FirstName = "James", LastName = "Dokes", Count = 1 }
Simple:
people.GroupBy(x => new { x.Age, x.FirstName, x.LastName })
.Select(x => new { x.Key.Age, x.Key.FirstName, x.Key.LastName, Count = x.Count() });
Just before I posted I saw answer already from JustAnotherUserYouMayKnow, so was going to cancel, but am posting anyway just to show same result using only the GroupBy, with the resultSelector parameter (instead of separate projection)
var personList = new List<Person> {
new Person { Age = 20, FirstName = "John", LastName = "Joe" },
new Person { Age = 20, FirstName = "John", LastName = "Joe" },
new Person { Age = 10, FirstName = "James", LastName = "Dokes"}};
var results = personList.GroupBy(per => new { per.Age, per.FirstName, per.LastName },
(key, items) => new { key.Age, key.FirstName, key.LastName, Count = items.Count() } );
Non-proc alternative:
List<Person> ls = new List<Person>();
ls.Add (new Person() { Age = 20, FirstName = "John", LastName = "Joe" });
ls.Add(new Person() { Age = 20, FirstName = "John", LastName = "Joe" });
ls.Add(new Person() { Age = 10, FirstName = "James", LastName = "Dokes" });
var result = (from cur in ls
group cur by new { Age = cur.Age, Name = cur.FirstName, LastName = cur.LastName }
into tmpResult
select new { tmpResult.Key.Age,tmpResult.Key.LastName,tmpResult.Key.Name,Count = tmpResult.Count()});
I have the following tables:
Person(Id, FirstName, LastName)
{
(1, "John", "Doe"),
(2, "Peter", "Svendson")
(3, "Ola", "Hansen")
(4, "Mary", "Pettersen")
}
Sports(Id, Name)
{
(1, "Tennis")
(2, "Soccer")
(3, "Hockey")
}
SportsPerPerson(Id, PersonId, SportsId)
{
(1, 1, 1)
(2, 1, 3)
(3, 2, 2)
(4, 2, 3)
(5, 3, 2)
(6, 4, 1)
(7, 4, 2)
(8, 4, 3)
}
Looking at the tables, we can conclude the following facts:
John plays Tennis
John plays Hockey
Peter plays Soccer
Peter plays Hockey
Ola plays Soccer
Mary plays Tennis
Mary plays Soccer
Mary plays Hockey
Now I would like to create a Linq2Sql query which retrieves the following:
Get all Persons who play Hockey and Soccer
Executing the query should return: Peter and Mary
Anyone has any idea's on how to approach this in Linq2Sql?
One of the great things about Linq is that you don't HAVE to write this all as one monolithic query because it won't actually execute until you enumerate the results anyway. You could write a single query, but you don't have to. Instead, you can write this as multiple, separate queries, increasing the readability, and clarifying your intent.
var sportIds = Sports
.Where(s => s.Name == "Hockey" || s.Name == "Soccer")
.Select(s => s.Id);
var people = Person.Where(p => SportsPerPerson
.Count(spp => (spp.PersonId == p.Id)
&& sportIds.Contains(spp.SportId)) == 2);
First, we get the collection of sport Ids we're interested in. Then, we find all the people with two sports in the first list. Although it's expressed as multiple queries, Linq will compress it all into one operation for us when we finally enumerate the results.
EDIT: Here is a complete test class illustrating the query:
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace L2STest
{
public class Sport
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class SportPerPerson
{
public int Id { get; set; }
public int PersonId { get; set; }
public int SportId { get; set; }
}
[TestClass]
public class SportsTest
{
private List<Person> persons;
private List<Sport> sports;
private List<SportPerPerson> sportsPerPerson;
[TestInitialize]
public void MyTestInitialize()
{
persons = new List<Person>
{
new Person {Id = 1, FirstName = "John", LastName = "Doe"},
new Person {Id = 2, FirstName = "Peter", LastName = "Svendson"},
new Person {Id = 3, FirstName = "Ola", LastName = "Hansen"},
new Person {Id = 4, FirstName = "Marv", LastName = "Petterson"},
};
sports = new List<Sport>
{
new Sport {Id = 1, Name = "Tennis"},
new Sport {Id = 2, Name = "Soccer"},
new Sport {Id = 3, Name = "Hockey"},
};
sportsPerPerson = new List<SportPerPerson>
{
new SportPerPerson {Id = 1, PersonId = 1, SportId = 1},
new SportPerPerson {Id = 2, PersonId = 1, SportId = 3},
new SportPerPerson {Id = 3, PersonId = 2, SportId = 2},
new SportPerPerson {Id = 4, PersonId = 2, SportId = 3},
new SportPerPerson {Id = 5, PersonId = 3, SportId = 2},
new SportPerPerson {Id = 6, PersonId = 3, SportId = 1},
new SportPerPerson {Id = 7, PersonId = 4, SportId = 2},
new SportPerPerson {Id = 8, PersonId = 4, SportId = 3},
};
}
[TestMethod]
public void QueryTest()
{
var sportIds = sports
.Where(s => s.Name == "Hockey" || s.Name == "Soccer")
.Select(s => s.Id);
var people = persons.Where(p => sportsPerPerson
.Count(spp => (spp.PersonId == p.Id)
&& sportIds.Contains(spp.SportId)) == 2);
Assert.AreEqual(2, people.Count());
Assert.AreEqual("Peter", people.First().FirstName);
Assert.AreEqual("Marv", people.Last().FirstName);
}
}
}