I have a list of employees, and all of them have another list nested which is called the EmployeeValuesCollection.
So my class is something like :-
public Employee(int employeeID, string jobTitle, int companyID,
List<EmployeeValues> employeeValuesCollection)
{
EmployeeID = employeeID;
JobTitle = jobTitle;
CompanyID = companyID;
EmployeeValuesCollection = employeeValuesCollection;
}
Now I wish to populate this object from another object with LINQ, and so far I have :-
List<DataFileRow> dataFiles = dfRow.Rows;
dataFiles
.ForEach(l => employeeList
.Add(new Employee(l.EmpID, l.JobTitle, l.CompID)));
That works however I do not know how to add the employeeValuesCollection in the statement. Is it possible to do?
So I was thinking something like :-
dataFiles
.ForEach(l => employeeList
.Add(new Employee(l.EmpID, l.JobTitle, l.CompID,
new List<EmployeeValuesCollection> .............)));
Thanks for your help and time.
It will be better to iterate through the input parameter employeeValuesCollection list inside the Employee constructor to create a local variable of type EmployeeValues and add it to the Employee class's instance list variable. If we use EmployeeValuesCollection = employeeValuesCollection;, we are actually assigning the reference. So if we modify some values of employeeValuesCollection, the same change will get reflected to EmployeeValuesCollection.
public Employee(int employeeID, string jobTitle, int companyID, List<EmployeeValues> employeeValuesCollection)
{
EmployeeID = employeeID;
JobTitle = jobTitle;
CompanyID = companyID;
foreach (var obj in employeeValuesCollection)
{
var empVal = new EmployeeValues() { Name = obj.Name};
EmployeeValuesCollection.Add(empVal);
}
And you can use the LINQ statement as
dataFiles.ForEach(l => employeeList.Add(new Employee(l.EmpID, l.JobTitle, l.CompID, l.EmployeeValuesCollection)));
You can use the Linq projection over each row. I'll do it in 'proper' linq:
employeeList = (from r in dfRow.Rows
select new Employee(r.EmpID, r.JobTitle, r.CompID,
new List<EmployeeValues>(/*parameter*/))).ToList();
Until you specify what EmployeeValues actually is (and which property on DataFileRow they're accessed from) it's difficult to say any more. But let's say it's just more properties, and I'll summise they are like key/value pairs (despite the name pluralisation on the type, that's a bit confusing).
I've taken the names here from your third comment on your question, and assumed that DataFileRow type exposes these values as an IEnumerable called EmployeeValues. Based on that - you can use an inner projection.
employeeList = (from r in dfRow.Rows
select new Employee(r.EmpID, r.JobTitle, r.CompID,
new List<EmployeeValue>((from v in r.EmployeeValues
select new EmployeeValues(v.title, v.value, v.isVisible))).ToList());
Related
So I have the gui set up so that I have two main listboxes. I'm still figuring out what kind of gui I want for this application so there is another one but that's not relevant. One listbox is a list of options for what you want to check for a department or an employee. The other is a list of departments. Right now I have the functionality for the name option to view the names of employees for a department. I just need to know how I can filter a list so that the only employees that show up are the ones who are in the chosen department after I click on the submit button. I figured I would use a lambda expression for that and it hasn't been working for me. I really want to know how to use lambda expressions better so please only give me a solution that involves using them. If it's impossible or if it would be more efficient to do something else then let me know.
File where I put reads and set dept array to file contents
//list of employees
public static List<Employee> EmployeeList = new List<Employee>();
//array to hold the options users have for interacting with info
public static string[] OptionsArr;
//array to hold the departments
public static string[] DeptsArr;
//skipping around same file to relevant code
//set the departments array to the contents of the depts file
DeptsArr = File.ReadAllLines("..\\..\\departments.txt");
Not sure if needed
Method for populating DeptListBox
private void UpdateDeptListBox()
{
//set up for new info
DeptListBox.Items.Clear();
//prevent interfence with update
DeptListBox.BeginUpdate();
//set department listbox to depts array
DeptListBox.DataSource = Program.DeptsArr;
DeptListBox.EndUpdate();
}
Problem Method - the submit button method
List<Employee> ResultList = new List<Employee>();
//name
if (OptionsListBox.SelectedIndex == 1)
{
//user selects Marketing department
if (DeptListBox.SelectedIndex == 0)
{
//problem is either with lambda exp or Program.DeptsArr comparison
foreach (Employee empl in Program.EmployeeList.Where(empl => empl.Dept.CompareTo(Program.DeptsArr[0]) == 0).ToList())
{
//this doesn't happen
ResultList.Add(empl);
}
for (int i = 0; i<ResultList.Count; i++)
{
ResultListBox.Items.Add(ResultList[i].Lname + " " + ResultList[i].Fname + " " + ResultList[i].Dept);
}
}
}
}
For me it can be helpful when I am having issues to break stuff down and look at smaller pieces. Are you sure the issue is your lambda function? It may be your options listbox != 1 or that the data is not being read in correctly.
As far as I can tell, this part should work. Although there are some issues with it:
foreach (Employee empl in Program.EmployeeList.Where(empl =>empl.Dept.CompareTo(Program.DeptsArr[0]) == 0).ToList())
{
//this doesn't happen
ResultList.Add(empl);
}
You could start with just the Employee lambda function and hard code the values. Maybe something like this which does indeed produce the correct results (Bob and Brandon)
List<Employee> ResultList = new List<Employee>();
List<Employee> EmployeeList = new List<Employee> {
new Employee{ Name = "Bob", Dept = "Accounting" },
new Employee{ Name = "Larry", Dept = "A" },
new Employee{ Name = "Margret", Dept = "B" },
new Employee{ Name = "Brandon", Dept = "Accounting" }
};
string[] DeptsArr = new string[2];
DeptsArr[0] = "Accounting";
DeptsArr[1] = "A";
//user selects Marketing department
if (departmentIndex == 0)
{
foreach (Employee empl in EmployeeList.Where(empl => empl.Dept.CompareTo(DeptsArr[0]) == 0).ToList())
{
ResultList.Add(empl);
}
}
However your lamda function inside a foreach loop is redundant. You can think of a lambda function as an instruction for running a foreach loop. A foreach loop by itself could look like this:
List<Employee> ResultList = new List<Employee>();
foreach (Employee empl in EmployeeList)
{
if(empl.Dept == DeptsArr[0])
{
ResultList.Add(empl);
}
}
You could get the same result as the foreach loop above, by using the following lamda function:
List<Employee> ResultList = EmployeeList.Where(empl => empl.Dept == DeptsArr[0]).ToList();
A final note is that the "ToList()" on the end of that lambda function is what executes the loop and returns the result as a List. Many times this is not required. Without the "ToList()" part an IEnumerable will be returned which you may be able to use instead. Using an IEnumerable instead of calling ToList() can have better performance in many scenarios.
If you want to test whether a specific value is in an array then you call Contains on that array, e.g.
var allEmployees = new List<Employee>();
// Populate allEmployees here.
var selectedEmployees = allEmployees.Where(e => selectedDepartments.Contains(e.Department)).ToArray();
The selectedEmployees array will contain only the Employee objects from the allEmployees list with a Department property value that is contained in the selectedDepartments array/collection.
In the following code that returns a list:
public List<Customer> GeAllCust()
{
var results = db.Customers
.Select(x => new { x.CustName, x.CustEmail, x.CustAddress, x.CustContactNo })
.ToList()
return results;
}
I get an error reporting that C# can't convert the list:
Error: Cannot implicitly convert type System.Collections.Generic.List<AnonymousType#1> to System.Collections.Generic.List<WebApplication2.Customer>
Why is that?
Here's a screenshot showing some additional information that Visual Studio provides in a tooltip for the error:
Is it right way to return some columns instead of whole table....?
public object GeAllCust()
{
var results = db.Customers.Select(x => new { x.CustName, x.CustEmail, x.CustAddress, x.CustContactNo }).ToList();
return results;
}
When you look the code:
x => new { ... }
This creates a new anonymous type. If you don't need to pull back only a particular set of columns, you can just do the following:
return db.Customers.ToList();
This assumes that Customers is an IEnumerable<Customer>, which should match up with what you are trying to return.
Edit
You have noted that you only want to return a certain subset of columns. If you want any sort of compiler help when coding this, you need to make a custom class to hold the values:
public class CustomerMinInfo
{
public string Name { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public int? ContactNumber { get; set; }
}
Then change your function to the following:
public List<CustomerMinInfo> GetAllCust()
{
var results = db.Customers.Select(x => new CustomerMinInfo()
{
Name = x.CustName,
Email = x.Email,
Address = x.Address,
ContactNumber = x.CustContactNo
})
.ToList();
return results;
}
This will work, however, you will lose all relationship to the database context. This means if you update the returned values, it will not stick it back into the database.
Also, just to repeat my comment, returning more columns (with the exception of byte arrays) does not necessarily mean longer execution time. Returning a lot of rows means more execution time. Your function is returning every single customer in the database, which when your system grows, will start to hang your program, even with the reduced amount of columns.
You are selecting to an anonymous type, which is not a Customer.
If you want to do (sort of) this, you can write it like this:
return db.Customers.Select(x => new Customer { Name = x.CustName, Email = x.CustEmail, Address = x.CustAddress, ContactNo = x.ContactNo }).ToList();
This assumes the properties on your Customer object are what I called them.
** EDIT ** Per your comment,
If you want to return a subset of the table, you can do one of two things:
Return the translated form of Customer as I specified above, or:
Create a new class for your business layer that only has only those four fields, and change your method to return a List<ShrunkenCustomer> (assuming ShunkenCustomer is the name that you choose for your new class.)
GetAllCust() is supposed to return a List of Customer, Select New will create a list of Anonymous Types, you need to return a list of Customer from your query.
try:
var results = db.Customers.Select( new Customer{CustName = x.CustName}).ToList(); //include other fields
I guess Customer is a class you have defined yourself?
The my suggestion would be to do something like the following:
var results = db.Customers.Select(x => new Customer(x.Custname, x.CustEmail, x.CustAddress, x.CustContactNo)).ToList();
The reason is that you are trying to return a list of Customer but the results from your link is an anonymous class containing those four values.
This would of course require that you have a constructor that takes those four values.
Basically whatever u got in var type, loop on that and store it in list<> object then loop and achieve ur target.Here I m posting code for Master details.
List obj = new List();
var orderlist = (from a in db.Order_Master
join b in db.UserAccounts on a.User_Id equals b.Id into abc
from b in abc.DefaultIfEmpty()
select new
{
Order_Id = a.Order_Id,
User_Name = b.FirstName,
Order_Date = a.Order_Date,
Tot_Qty = a.Tot_Qty,
Tot_Price = a.Tot_Price,
Order_Status = a.Order_Status,
Payment_Mode = a.Payment_Mode,
Address_Id = a.Address_Id
});
List<MasterOrder> ob = new List<MasterOrder>();
foreach (var item in orderlist)
{
MasterOrder clr = new MasterOrder();
clr.Order_Id = item.Order_Id;
clr.User_Name = item.User_Name;
clr.Order_Date = item.Order_Date;
clr.Tot_Qty = item.Tot_Qty;
clr.Tot_Price = item.Tot_Price;
clr.Order_Status = item.Order_Status;
clr.Payment_Mode = item.Payment_Mode;
clr.Address_Id = item.Address_Id;
ob.Add(clr);
}
using(ecom_storeEntities en=new ecom_storeEntities())
{
var Masterlist = en.Order_Master.OrderByDescending(a => a.Order_Id).ToList();
foreach (var i in ob)
{
var Child = en.Order_Child.Where(a => a.Order_Id==i.Order_Id).ToList();
obj.Add(new OrderMasterChild
{
Master = i,
Childs = Child
});
}
}
I am new to linq and I am writing a process which I believe can be improved.
To make it simple :
I get a list of objects.
I check in a db table which object has a row in the database.
I return a list of objects with aditional boolean exists/not exists.
I have the following simple POCO
public class Project
{
public Guid? Id {get; set;}
public string name {get; set;}
}
Notice not always I have an id and therefor I should skip this object.
(strange but this is only very close to my requirement in reallife)
Here is my Code - need improvement:
// Get List OF Project Guids
List<Project> ProjectList = GetProjects()
IEnumerable<Guid?> projectsIDs = from package in packages select package.Key;
List<Guid?> prjGuidsList = projectsIDs.ToList();
// Sends the list of Guids and return only the one that exists in the db
// will be implemented with select.. where.. in..
List<Guid?> dbProjects = FilterSharedVersions(prjGuidsList);
// create a new object that will contain the true false value
List<ProjectsToken> tokens = packages.Select(subject => new ProjectsToken
{
Id = subject.id
MetaKey = subject.Name,
exists = dbProjects.contains(subjecy.id)
}
).ToList();
return tokens;
The Contains method of the List class will perform very poorly in O(n). You need to build a HashSet instead, then the method Contains will perform in O(1).
var dbProjects = new HashSet<Guid?>(FilterSharedVersions(prjGuidsList));
var tokens = packages.Select(subject => new ProjectsToken
{
Id = subject.id
MetaKey = subject.Name,
exists = dbProjects.Contains(subjecy.id)
}
).ToList();
I am struggeling a little with trying to write the LINQ query to do the following,
public class MyClass
{
public int ID {get; set;}
public int Name {get; set;}
public IEnumerable<string> SomeIEnumerable {get; set;}
}
I am trying to create the object using LINQ from a DB query that would return something as follows,
ID Name SomeString
0 NameA "MyValue"
1 NameB "Value1"
1 NameB "Value2"
2 NameC "Value"
The LINQ is pretty easy to write as well,
from DataRow row in dataSet.Tables[0].Rows
select new MyClass
{
ID = (int)row["ID"],
Name = row["Name"].ToString(),
SomeIEnumerable = new List<string>{ row["SomeString"].ToString() }
};
The tricky part is how do I turn this into a Dictionary where
dictionary[1].SomeIEnumerable = {"Value1", "Value2"}
A simple ToDictionary would throw an ArgumentException
The main issue here, is how do I handle the fact that the keyis not distinct, and be able to lookup in the temporary dictionary the existing value to add to it the values I am interested in.
You can also your grouping, and then use IGrouping to retrieve the list of items in the group
Sample code
var simulatedData = Enumerable.Range(0,10).Select(x=> new {Key=x%3+1, Index=x});
var dict = simulatedData.GroupBy(x=>x.Key).ToDictionary(x=>x.Key, x=>x.Select(t=>t.Index));
Instead of calling ToDictionary, call ToLookup. :)
Answer thanks to #Tilak is,
from DataRow row in dataSet.Tables[0].Rows
group row by new
{
ID = (int) row["ID"],
Name = row["Name].ToString()
} into rowGroup
select new MyClass
{
ID = rowGroup.Key.ID,
Name = rowGroup.Key.Name,
SomeIEnumerable =
from row in rowGroup
select row["SomeString"].ToString()
};
I am trying to bind Row to gridview which will not only contain fields of Category table but other tables as well. So I had properties set for all those fields. But since the columns in the table could change frequently I need to dynamically change the properties as well. I found the following one way to do it (using hashtable). But I can't bind the hashtable values to gridview.
How to solve this problem better?
public partial class Form1 : Form
{
public class Row
{
// properties
public Hashtable Properties = new Hashtable();
}
public Form1()
{
InitializeComponent();
DataClasses1DataContext context = new DataClasses1DataContext();
var st = from c in context.Categories
select c;
var p = from pr in context.Products
select p;
Row r = new Row();
//List<Row> listrow = new List<Row>();
foreach (var item in st)
{
r.Properties.Add(item.Description, item.Description);
}
this.gridControl1.DataSource = r.Properties.Values;
}
}
Hashtable.Values is an ICollection - but you need IList to bind data. Ideally, you really want a typed list. Why use Hashtable at all here? I doubt you have enough rows to need it...
Instead, use a typed list (List<T> or BindingList<T>). Note that you can only bind to a single type in a grid. It isn't at all clear to me what you want to display in the grid, since at the moment you are only adding the description, but at the simplest level:
this.gridControl1.DataSource = context.Categories.ToList();
or (better):
this.gridControl1.DataSource = context.Categories.ToBindingList();
This won't help you put both Products and Categories into a single grid... but then, nothing will if they aren't the same type. One thing that might work is an anonymous type of the common properties:
var query = (from c in context.Categories
select new {Id = c.CategoryId, Name = c.Category,
Description = c.Description }).Concat(
from p in context.Products
select new {Id = p.ProductId, Name = p.Product,
Description = p.ProductDescription });
this.gridControl1.DataSource = query.ToList();
Note, however, that anonymous types are immutable (non-editable) - hence no point in using ToBindingList(). The other option is to declare your own class for the purpose, which means it can be editable too.