Display only selected columns - c#

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

Related

Filter List inside List Linq

I have list say list of customers and inside each list there is another list of orders
Class Customer
{
int ID,
string Name
List<Order> orders
}
Class Order{
int ID,
string Name
}
Also have a integer list of filteredorderIds = {1,2,3,4}
I want to filter the list of customers who has got orderIds from filteredorderIds list.
So far I am stuck at query like
var filteredCustomers = Customers.Where(x => x.Orders.Any(filteredorderIds.contains(y => y.Id)));
please give credit to #Johnathan Barclay, since he posted faster than i typed example
void Main()
{
var customers = new List<Customer>(){
new Customer(){
ID =1,
Name = "Cust1",
orders = new List<Order>(){
new Order(){ID = 4, Name = "o11"},
new Order(){ID = 5, Name = "o12"},
new Order(){ID = 6, Name = "o13"}
}
},
new Customer(){
ID = 2,
Name = "Cust2",
orders = new List<Order>(){
new Order(){ID = 3, Name = "o21"},
new Order(){ID = 7, Name = "o22"},
new Order(){ID = 8, Name = "o23"}
}
}
};
customers.Where(w =>
w.orders.Any(w => filteredorderIds.Contains(w.ID))
).Dump();
}
List<int> filteredorderIds = new List<int>() { 1, 2, 3, 4 };
public class Customer
{
public int ID { get; set; }
public string Name { get; set; }
public List<Order> orders { get; set; }
}
public class Order
{
public int ID { get; set; }
public string Name { get; set; }
}

How to get same results from select as foreach when using GroupBy()

I would like to be able to attain the same results that I can get by using foreach on a grouping when using the select method and an anonymous method.
public class ExportData
{
public int Id { get; set; }
public string Colour { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public int Money { get; set; }
}
public class ExportDataDictionary
{
public IDictionary<string, object> ColumnData { get; set; } = new Dictionary<string, object>();
}
Given the two classes above as an example.
I create some data..
var dataCollection = new List<ExportData>
{
new ExportData { Name = "Name1", Age = 1, Colour = "Blue", Id = 1, Money = 10 },
new ExportData { Name = "Name1", Age = 2, Colour = "Red", Id = 2, Money = 20 },
new ExportData { Name = "Name1", Age = 2, Colour = "Green", Id = 3, Money = 30 },
new ExportData { Name = "Name2", Age = 1, Colour = "Yellow", Id = 4, Money = 40 },
new ExportData { Name = "Name3", Age = 2, Colour = "Blue", Id = 5, Money = 50 },
new ExportData { Name = "Name4", Age = 3, Colour = "Blue", Id = 6, Money = 10 }
};
Next I group this data by, for example, two properties as follows..
var dataGrouping = dataCollection.GroupBy(g => new { g.Name, g.Age });
I then create a list of ExportDataDictionaries and foreach through each group in the grouping, creating a new ExportDataDictionary each time and adding both of the keys to the dictionary.
var data = new List<ExportDataDictionary>();
foreach (var grouping in dataGrouping)
{
var datadictionary = new ExportDataDictionary();
datadictionary.ColumnData.Add("NAME", grouping.Key.Name);
datadictionary.ColumnData.Add("AGE", grouping.Key.Age);
data.Add(datadictionary);
}
The result is a collection of 5 ExportDataDictionaries with 2 Columns in each one that contain the pair of keys that correspond to each of the groupings.
My attempt to achieve the same with the Select method is shown below.
var data2 = new List<ExportDataDictionary>();
var mydata = dataGrouping.Select(d =>
{
var datadictionary = new ExportDataDictionary();
datadictionary.ColumnData.Add("NAME", d.Key.Name);
datadictionary.ColumnData.Add("AGE", d.Key.Age);
data2.Add(datadictionary);
return data2;
});
The result is of the type:
mydata = {System.Linq.Enumerable.WhereSelectEnumerableIterator<System.Linq.IGrouping<<>f__AnonymousType0<string, int>, ConsoleApp2.Program.ExportData>, System.Collections.Generic.List<ConsoleApp2.Program.ExportDataDictionary>>}
and it contains 5 items and each item contains 10 dictionaries. The 5 dictionaries that I expect are there with the same values as when using foreach but then there are 2 copies of each. I believe that this must be because it is creating the dictionaries for both of the keys used in the grouping. So, I am wondering how to only do this for one of the keys or just each group in the collection?
The requirement is that mydata should contain the same result as obtained by foreach in data variable
Any help much appreciated :)
Just Add .ToList() at the end of your last statement remove the data2.Add(datadictionary); statement and only return the datadictionary return datadictionary; like this
var mydata = dataGrouping.Select(d =>
{
var datadictionary = new ExportDataDictionary();
datadictionary.ColumnData.Add("NAME", d.Key.Name);
datadictionary.ColumnData.Add("AGE", d.Key.Age);
return datadictionary;
}).ToList();
I have run your code and checked and saw that mydata contains 5 items, and each item contains 2 ColumnData members.
Actually, your Linq query is only executed when you call the .ToList() function

Group By Multiple Column In LINQ in C#

I have a class like as follows:
public class ActualClass
{
public string BookName { get; set; }
public string IssuerName { get; set; }
public DateTime DateOfIssue { get; set; }
public bool Status { get; set; }
}
It has following data in the table:
I would like to group them by IssuerName and DateOfIssue for the following viewModel class:
public class ViewModel
{
public string IssuerName { get; set; }
public DateTime DateOfIssue { get; set; }
public List<string> Books { get; set; }
}
And data will be displayed as follows: (Screenshot data will be replaced by the previous table data after successful grouping)
Special attention: Is there anything wrong in my ViewModel according to my expectation?
I tried a lot after following some stackoverflow answers but none did work for me. Any help will be highly appreciated.
The code I have tried:
var viewmodel = from b in db.BookIssues
group b by new
{
b.IssuerName,
b.DateOfIssue
}
into g
select new ViewModel()
{
Name = g.key.IssuerName,
DateOfIssue = g.Key.DateOfIssue,
Books = g.ToList() //Actually this line of code is not working
};
Books = g.ToList() //Actually this line of is not working
Probably because Books property is type of List<string>, not List<ActualClass>.
Can you please try this query, I added b.Select(bn => bn.BookName).ToList() to extract only names of books:
var books = new List<ActualClass>
{
new ActualClass { BookName = "A", DateOfIssue = new DateTime(2015, 10, 10, 10, 10, 0), IssuerName = "1" },
new ActualClass { BookName = "B", DateOfIssue = new DateTime(2015, 10, 10, 10, 10, 0), IssuerName = "1" },
new ActualClass { BookName = "C", DateOfIssue = new DateTime(2015, 10, 10, 10, 10, 0), IssuerName = "1" },
new ActualClass { BookName = "D", DateOfIssue = new DateTime(2015, 10, 10, 10, 10, 0), IssuerName = "2" },
new ActualClass { BookName = "E", DateOfIssue = new DateTime(2015, 10, 10, 12, 10, 0), IssuerName = "2" },
new ActualClass { BookName = "F", DateOfIssue = new DateTime(2015, 10, 10, 12, 10, 0), IssuerName = "2" }
};
var result = books.GroupBy(x => new { x.IssuerName, x.DateOfIssue })
.Select(b => new ViewModel
{
Books = b.Select(bn => bn.BookName).ToList(),
// Accessing to DateOfIssue and IssuerName from Key.
DateOfIssue = b.Key.DateOfIssue,
IssuerName = b.Key.IssuerName
});
I grouped by: x.IssuerName, x.DateOfIssue. I did that by passing anonymous type in GroupBy() with following manner: x => new { x.IssuerName, x.DateOfIssue }.
Now they are in key and you can access to IssuerName and DateOfIssue from KEY in SELECT statement like in following: b.Key.IssuerName and b.Key.DateOfIssue.
if you need to select list of books from group result, you need Books = v.Select(c=>c.BookName).ToList() also note that in case of you have time in issue date time you may need to group by only the date part using EntityFunctions.TruncateTime function. if you only storing date only then you can ignore this function.
var viewmodel = db.BookIssues.GroupBy(x=>new {IssuerName =x.IssuerName, DateOfIssue=EntityFunctions.TruncateTime(x.DateOfIssue) })
.Select(v=>new ViewModel(){IssuerName =v.Key.IssuerName, DateOfIssue = v.Key.DateOfIssue, Books = v.Select(c=>c.BookName).ToList() })
.ToList();

How to Use Effeciently Where Clause or Select in LINQ Parallel in Large Dataset

I'm having approx 250,000 records as marked as Boss, each Boss has 2 to 10 Staff. Daily I need to get the details of the Staff. Approx there are 1,000,000 staff. I'm using Linq to get the Unique list of Staff who are worked in daily basis. Consider the following C# LINQ and Models
void Main()
{
List<Boss> BossList = new List<Boss>()
{
new Boss()
{
EmpID = 101,
Name = "Harry",
Department = "Development",
Gender = "Male",
Employees = new List<Person>()
{
new Person() {EmpID = 102, Name = "Peter", Department = "Development",Gender = "Male"},
new Person() {EmpID = 103, Name = "Emma Watson", Department = "Development",Gender = "Female"},
}
},
new Boss()
{
EmpID = 104,
Name = "Raj",
Department = "Development",
Gender = "Male",
Employees = new List<Person>()
{
new Person() {EmpID = 105, Name = "Kaliya", Department = "Development",Gender = "Male"},
new Person() {EmpID = 103, Name = "Emma Watson", Department = "Development",Gender = "Female"},
}
},
..... ~ 250,000 Records ......
};
List<Person> staffList = BossList
.SelectMany(x =>
new[] { new Person { Name = x.Name, Department = x.Department, Gender = x.Gender, EmpID = x.EmpID } }
.Concat(x.Employees))
.GroupBy(x => x.EmpID) //Group by employee ID
.Select(g => g.First()) //And select a single instance for each unique employee
.ToList();
}
public class Person
{
public int EmpID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
}
public class Boss
{
public int EmpID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
public List<Person> Employees { get; set; }
}
In the above LINQ I'm getting the List of Distinct Employees or Staff, the list contains more than 1,000,000 records. From the Obtained List I need to search "Raj"
staffList.Where(m => m.Name.ToLowerInvariant().Contains("Raj".ToLowerInvariant()));
For this operation, it took more than 3 to 5 minutes to get the result.
How could I make it more efficient. Kindly assist me...
If you change Boss to inherit from Person ( public class Boss : Person ) not only do you not need to duplicate your properties in Person and Boss, you don't have to create all new Person instances for each Boss, because a Boss is already a Person:
IEnumerable<Person> staff = BossList
.Concat(BossList
.SelectMany(x => x.Employees)
)
.DistinctBy(p => p.EmpId)
.ToList()
Where DistinctByis defined as
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
var seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
Also, in your comparison, you're converting every Name to lowercase and doing the comparison - that's a lot of string creation that you don't need. Instead, try something like
staffList.Where(m => m.Name.Equals("Raj", StringComparison.InvariantCultureIgnoreCase));
Also, be aware that your use of Contains would also match names like Rajamussenand mirajii - possibly not what you were expecting.
Would it work for you to change staffList to a Dictionary? A better search algorithm as those from Dictionary and SortedList would get you the most improvement.
I've tested the code below and it runs in just a few seconds.
private static void Main()
{
List<Boss> BossList = new List<Boss>();
var b1 = new Boss()
{
EmpID = 101,
Name = "Harry",
Department = "Development",
Gender = "Male",
Employees = new List<Person>()
{
new Person() {EmpID = 102, Name = "Peter", Department = "Development", Gender = "Male"},
new Person() {EmpID = 103, Name = "Emma Watson", Department = "Development", Gender = "Female"},
}
};
var b2 = new Boss()
{
EmpID = 104,
Name = "Raj",
Department = "Development",
Gender = "Male",
Employees = new List<Person>()
{
new Person() {EmpID = 105, Name = "Kaliya", Department = "Development", Gender = "Male"},
new Person() {EmpID = 103, Name = "Emma Watson", Department = "Development", Gender = "Female"},
}
};
Random r = new Random();
var genders = new [] {"Male", "Female"};
for (int i = 0; i < 1500000; i++)
{
b1.Employees.Add(new Person { Name = "Name" + i, Department = "Department" + i, Gender = genders[r.Next(0, 1)], EmpID = 200 + i });
b2.Employees.Add(new Person { Name = "Nam" + i, Department = "Department" + i, Gender = genders[r.Next(0, 1)], EmpID = 1000201 + i });
}
BossList.Add(b1);
BossList.Add(b2);
Stopwatch sw = new Stopwatch();
sw.Start();
var emps = BossList
.SelectMany(x =>
new[] {new Person {Name = x.Name, Department = x.Department, Gender = x.Gender, EmpID = x.EmpID}}
.Concat(x.Employees))
.GroupBy(x => x.EmpID) //Group by employee ID
.Select(g => g.First());
var staffList = emps.ToList();
var staffDict = emps.ToDictionary(p => p.Name.ToLowerInvariant() + p.EmpID);
var staffSortedList = new SortedList<string, Person>(staffDict);
Console.WriteLine("Time to load staffList = " + sw.ElapsedMilliseconds + "ms");
var rajKeyText = "Raj".ToLowerInvariant();
sw.Reset();
sw.Start();
var rajs1 = staffList.AsParallel().Where(p => p.Name.ToLowerInvariant().Contains(rajKeyText)).ToList();
Console.WriteLine("Time to find Raj = " + sw.ElapsedMilliseconds + "ms");
sw.Reset();
sw.Start();
var rajs2 = staffDict.AsParallel().Where(kvp => kvp.Key.Contains(rajKeyText)).ToList();
Console.WriteLine("Time to find Raj = " + sw.ElapsedMilliseconds + "ms");
sw.Reset();
sw.Start();
var rajs3 = staffSortedList.AsParallel().Where(kvp => kvp.Key.Contains(rajKeyText)).ToList();
Console.WriteLine("Time to find Raj = " + sw.ElapsedMilliseconds + "ms");
Console.ReadLine();
}
public class Person
{
public int EmpID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
}
public class Boss
{
public int EmpID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
public List<Person> Employees { get; set; }
}
}
Output1:
Output2 (using .AsParallel() on searches):
In other words, if you can't use some faster data structure, up can speed your search up just by changing form
staffList.Where(m => m.Name.ToLowerInvariant().Contains("Raj".ToLowerInvariant()));
to
staffList.AsParallel().Where(m => m.Name.ToLowerInvariant().Contains("Raj".ToLowerInvariant()));

Doing multiple joins within a LINQ statement

Can someone help me translate the following SQL query into a LINQ format.
SELECT a.ID,
a.HostID,
h.URL,
a.SourceURL,
a.TargetURL,
c.Value,
a.ExtFlag
FROM Link a
INNER JOIN Host h
ON h.ID = a.HostID
INNER JOIN Ref c
ON a.ResponseCode = c.SubType
AND c.Type = 'HTTP Status'
Many Thanks
I think it would be something like:
var result = from a in Context.DGApprovedLink
join h in Context.DGHost on a.HostID equals h.ID
join c in Context.DGConfig on a.ResponseCode equals c.SubType
where c.Type == "HTTP Status"
select new {
a.ID,
a.HostID,
h.URL,
a.SourceURL,
a.TargetURL,
c.Value,
a.ExtFlag };
Create Unit test class using MStest and copy the code
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace LinqTest.Test
{
public class Employee
{
public int EmpId { get; set; }
public string Name { get; set; }
public DateTime? DOB { get; set; }
public decimal Salary { get; set; }
public DateTime DOJ { get; set; }
public bool IsActive { get; set; }
}
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public double Price { get; set; }
}
public class BookOrder
{
public int OrderId { get; set; }
public int EmpId { get; set; }
public int BookId { get; set; }
public int Quantity { get; set; }
}
[TestClass]
public class Linqtest
{
List<Employee> Employees;
List<Book> Books;
List<BookOrder> Orders;
[TestInitialize]
public void InitializeData()
{
Employees = new List<Employee>();
Books = new List<Book>();
Orders = new List<BookOrder>();
Employees.Add(new Employee(){EmpId = 1, Name ="Test1" , DOB = new DateTime(1980,12,15),IsActive = true,Salary = 4500});
Employees.Add(new Employee() { EmpId = 11, Name = "Test2", DOB = new DateTime(1981, 12, 15), IsActive = true, Salary = 3500 });
Employees.Add(new Employee() { EmpId = 5, Name = "Test3", DOB = new DateTime(1970, 2, 15), IsActive = true, Salary = 5500 });
Employees.Add(new Employee() { EmpId = 8, Name = "Test4", DOB = new DateTime(1978, 1, 15), IsActive = true, Salary = 7500 });
Employees.Add(new Employee() { EmpId = 9, Name = "Test5", DOB = new DateTime(1972, 2, 5), IsActive = true, Salary = 2500 });
Employees.Add(new Employee() { EmpId = 10, Name = "Test6", DOB = new DateTime(1980, 10, 8), IsActive = false, Salary = 5500 });
Employees.Add(new Employee() { EmpId = 15, Name = "Test7", DOB = new DateTime(1983, 11, 25), IsActive = true, Salary = 3500 });
Books.Add(new Book(){BookId = 2, Price = 24.99,Title = "British Food"});
Books.Add(new Book() { BookId = 5, Price = 4.99, Title = "Holidays in UK" });
Books.Add(new Book() { BookId = 7, Price = 7.99, Title = "UK Laws" });
Orders.Add(new BookOrder(){EmpId = 1,OrderId = 1,BookId = 2,Quantity = 3});
Orders.Add(new BookOrder() { EmpId = 1, OrderId = 1, BookId = 5, Quantity = 1 });
Orders.Add(new BookOrder() { EmpId = 1, OrderId = 2, BookId = 7, Quantity = 5 });
Orders.Add(new BookOrder() { EmpId = 11, OrderId = 3, BookId = 2, Quantity = 3 });
Orders.Add(new BookOrder() { EmpId = 11, OrderId = 4, BookId = 7, Quantity = 3 });
}
[TestMethod]
public void CheckEmpCount()
{
var res = Employees
.Where(e => e.EmpId > 5)
.Where(t =>t.Salary>=5000);
Assert.AreEqual(2,res.Count());
res = Employees
.Where(e => e.EmpId > 5);
Assert.AreEqual(5,res.Count());
}
[TestMethod]
public void TestGroupBy()
{
var res = from e in Employees
group e by e.Salary;
Assert.AreEqual(5,res.Count());
var res1 = Employees.GroupBy(e => e.Salary);
Assert.AreEqual(5, res1.Count());
}
[TestMethod]
public void TestJoin()
{
var res = from o in Orders
join Employee e in Employees
on o.EmpId equals e.EmpId
where o.EmpId == 11
select o;
Assert.AreEqual(2,res.Count());
}
[TestMethod]
public void TestJoinData()
{
var res = from o in Orders
join Employee e in Employees
on o.EmpId equals e.EmpId
join Book b in Books
on o.BookId equals b.BookId
orderby e.EmpId
select new {o.OrderId, e.Name, b.Title, b.Price};
Assert.AreEqual("Test1", res.First().Name);
}
}
}

Categories