My goal is to compare to lists and update values on list A with values from list B. Along with that, I want it to work like a left join and keep all values from list A even if they didn't get updated and detect multiple matches.
What I have tried so far is below.
The Setup
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UpdateMe { get; set; }
public static List<Person> CreateList()
{
return new List<Person>
{
new Person { LastName = "Barton", FirstName = "Clint" },
new Person { LastName = "Stark", FirstName = "Tony" },
new Person { LastName = "Parker", FirstName = "Peter" }
};
}
public static List<Person> CreateListTwo()
{
return new List<Person>
{
new Person { LastName = "Barton", FirstName = "Clint", UpdateMe = "Updated"},
new Person { LastName = "Stark", FirstName = "Tony", UpdateMe = "Updated"},
};
}
}
public class FirstLastNameMatch : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.FirstName == y.FirstName
&& x.LastName == y.LastName;
}
public int GetHashCode(Person obj)
{
unchecked
{
var hash = 17;
hash = hash * 23 + obj.FirstName.GetHashCode();
hash = hash * 23 + obj.LastName.GetHashCode();
return hash;
}
}
}
Implementation
static void Main()
{
var listA = Person.CreateList();
var listB = Person.CreateListTwo();
//Attempt
var result = listA.Join(
listB,
x => x,
y => y,
(x, y) => new Person
{
FirstName = x.FirstName,
LastName = x.LastName,
UpdateMe = y.UpdateMe
},
new FirstLastNameMatch()
);
foreach (var person in result)
{
Console.WriteLine($"Name: {person.FirstName} {person.LastName} " +
$"UpdateMe: {person.UpdateMe} ");
}
}
There problem I run into here is that it is only an inner join and not a left join. I have found a way to do the left join but I cannot figure out how to pass in the IEqualityComparer into this syntax.
var result = from personA in listA
join personB in listB on new {personA.FirstName, personA.LastName } equals new { personB.FirstName, personB.LastName }
into buffer
from subPerson in buffer.DefaultIfEmpty()
select new Person
{
FirstName = personA.FirstName,
LastName = personA.LastName,
UpdateMe = (subPerson == null ? string.Empty : subPerson.UpdateMe)
};
The real catch is that once I am able to complete the left join while injecting the comparison criteria, I still need to detect duplicates.
I need to be able to identify if a person from listA matches to more than one person in listB. It is alright if listA had duplicates though.
End Goal
Left join 2 lists with dynamic matching criteria.
Detect multiple matches from the right list.
I need to update a static list of properties on the left list with the match from the right list.
Related
I'm presently trying to use a dictionary values to name optional parameters when invoking a method. I'm not sure this is possible with c# but I do something similar with queries using dynamic SQL.
string[] dobArrayKey = {"dob: "};
string[] dobArrayValue = {txtDob.Text};
string[] ptntNumArrayKey = { "PatientID: " };
string[] ptntNumArrayValue = { txtOfficeMR.Text};
string[] nameArrayKey = { "FirstName: ", "LastName: " };
string[] nameArrayValue = { txtFirstname.Text, txtLastname.Text };
List<List<string>> searchResults = new List<List<string>>();
Dictionary<string[], string[]> searchCriteria = new Dictionary<string[], string[]>
{
{dobArrayKey,dobArrayValue}
,{ptntNumArrayKey,ptntNumArrayValue}
,{nameArrayKey,nameArrayValue}
};
foreach (var item in searchCriteria)
{
if (item.Value[0] != "" && item.Value[0] != null)
{
searchResults.Add(new List<string>());
for (int x = 0; x <= item.Key.Count(); x++)
{
string strJSON = doPatientSearch(Convert.ToInt32(au.UserID)
, Convert.ToInt32(Session["PracticeID"]), au.SessionID, item.Key[x].ToString() : item.Value[x].ToString() );
PatientSearchResponse ptLi = JsonConvert.DeserializeObject<PatientSearchResponse>(json2);
foreach (PatientList3 patient in ptLi.PatientList)
{
searchResults[x].Add(patient.PatientNumber);
}
}
}
}
public static string doPatientSearch(int UserID, int PracticeID, string SessionID, string PatientID = null,
,string first = null, string last = null, string dob = null, string social = null)
{
//search
}
My colleague suggested I change the method itself by removing all of the optional parameters and instead passing through a dictionary that contains all of the parameters and handling them inside the method.
I think that would work, but for curiosities sake I wanted to get some feedback and find out whether or not something like I'm attempting to do in the above code is possible.
If it is impossible but there is another way of achieving the desired outcome I'd love to see your suggestions.
Thank you in advance.
Pass an expression
Since the criteria are used post-hoc (i.e. by filtering a complete resultset), you can use LINQ to filter the results. For maximum flexibility, the caller can pass in an Expression to be used as a callback on each item to determine if it should be included.
To get a filtered resultset:
public IEnumerable<Patient> FindPatients(Func<Patient,bool> criteria)
{
return sourceData
.Where (criteria);
}
To return a single result:
public Patient FindPatient(Func<Patient,bool> criteria)
{
return sourceData
.Single(criteria);
}
The criteria expression is just a function that accepts a patient and returns a Boolean. The caller can write this any way desired, or insert it as a lambda expression.
var results = patients.FindPatients( p => p.LastName == "Doe" );
Or
var results = patients.FindPatients
(
p =>
p.LastName.Contains("Doe") &&
p.PracticeID == 12
);
Or
var singleResult = patients.FindPatient( p => p.UserID == 1);
As you can see, the caller can provide literally any criteria desired, and has the benefit of type safety and early binding. This is far superior to using a Dictionary which has neither.
Full example code:
class Patient
{
public int UserID { get; set; }
public int PracticeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DOB { get; set; }
public string Social { get; set; }
public override string ToString()
{
return string.Format("{0} {1} {2}", UserID, FirstName, LastName);
}
}
class PatientRepository
{
static private readonly List<Patient> sourceData = new List<Patient>
{
new Patient
{
UserID = 1, PracticeID = 10, FirstName = "John", LastName = "Doe", DOB = DateTime.Parse("1/2/1968"), Social="123456789"
},
new Patient
{
UserID = 2, PracticeID = 10, FirstName = "Jane", LastName = "Doe", DOB = DateTime.Parse("1/2/1958"), Social="123456790"
},
new Patient
{
UserID = 3, PracticeID = 10, FirstName = "John", LastName = "Carson", DOB = DateTime.Parse("4/1/1938"), Social="123456791"
}
};
public IEnumerable<Patient> FindPatients(Func<Patient,bool> criteria)
{
return sourceData
.Where (criteria);
}
public Patient FindPatient(Func<Patient,bool> criteria)
{
return sourceData
.Single(criteria);
}
}
public class Program
{
public static void Main()
{
//Get a reference to the data store
var patients = new PatientRepository();
Console.WriteLine("Multiple record search");
var results = patients.FindPatients
(
p => p.LastName == "Doe"
);
foreach (var p in results)
{
Console.WriteLine(p);
}
Console.WriteLine("Single record search");
var singleResult = patients.FindPatient
(
p => p.UserID == 1
);
Console.WriteLine(singleResult);
}
}
Output:
Multiple record search
1 John Doe
2 Jane Doe
Single record search
1 John Doe
See the working code on DotNetFiddle
Is is possible to have a linq query that populates a class with List for any outer join subqueries?
I've tried various variations of this, but can't get it to work.
Another option would be to populate the class by having more queries, but that would be bad performance wise.
Here's an example, where I try to populate MyClass, using a single query
var result = from p in PersonTable
join cars in CarTable on p.id equals cars.id_person into carsGroup.DefaultIfEmpty()
select new MyClass
{
Person = new Person
{
Id = p.id,
Name = p.name
},
Cars = new List<Car>()
{
Id = carsGroup....??
}
}
public class MyClass
{
public Person Person { get; set; }
public List<PersonCar> Cars { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class PersonCar
{
public int Id { get; set; }
pubint int IdPerson {get; set;}
public string Description { get; set; }
}
The LINQ query you have provide is incorrect. The following is a Test that will demonstrate functionality that you're probably looking for:
[TestMethod]
public void TestMethod1()
{
var PersonTable = new List<Person>
{
new Person
{
Id = 1,
Name = "Test1"
},
new Person
{
Id = 2,
Name = "Test2"
},
};
var CarTable = new List<PersonCar>
{
new PersonCar
{
Id = 1,
IdPerson = 2
},
new PersonCar
{
Id = 2,
IdPerson = 3
}
};
var result = (from person in PersonTable
join cars in CarTable on person.Id equals cars.IdPerson into carsGroup
from args in carsGroup.DefaultIfEmpty()
select new MyClass
{
Person = person,
Cars = carsGroup.ToList()
}).ToList();
Assert.AreEqual(2, result.Count);
Assert.AreEqual(1, result.Count(res => res.Cars.Count == 0));
Assert.AreEqual(1, result.Count(res => res.Cars.Count == 1));
}
I would assume there's a simple LINQ query to do this, I'm just not exactly sure how. Please see code snippet below, the comment explains what I'd like to do:
class Program
{
static void Main(string[] args)
{
List<Person> peopleList1 = new List<Person>();
peopleList1.Add(new Person() { ID = 1 });
peopleList1.Add(new Person() { ID = 2 });
peopleList1.Add(new Person() { ID = 3 });
peopleList1.Add(new Person() { ID = 4});
peopleList1.Add(new Person() { ID = 5});
List<Person> peopleList2 = new List<Person>();
peopleList2.Add(new Person() { ID = 1 });
peopleList2.Add(new Person() { ID = 4});
//I would like to perform a LINQ query to give me only
//those people in 'peopleList1' that are in 'peopleList2'
//this example should give me two people (ID = 1& ID = 4)
}
}
class Person
{
public int ID { get; set; }
}
var result = peopleList2.Where(p => peopleList1.Any(p2 => p2.ID == p.ID));
You can do this using Where:
var result = peopleList1.Where(p => peopleList2.Any(p2 => p2.ID == p.ID));
You can also use Intersect (var result = peopleList1.Intersect(peopleList2);), but that would need you to implement an extra IEqualityComparer<Person> or override Person's Equals and GetHashCode methods in a way that two Person instances with the same ID are regarded equal. Intersect would otherwise perform reference equality.
I would join both lists on ID:
var inboth = from p1 in peopleList1
join p2 in peopleList2
on p1.ID equals p2.ID
select p1;
List<Person> joinedList = inboth.ToList();
Related: Why is LINQ JOIN so much faster than linking with WHERE?
If you would override Equals + GetHashCode you could use Intersect:
List<Person> joinedList = peopleList1.Intersect(peopleList2).ToList();
or you could provide a custom IEqualityComparer<Person> for Intersect:
public class PersonIdComparer: IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if(object.ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
return x.ID == y.ID;
}
public int GetHashCode(Person obj)
{
return obj == null ? int.MinValue : obj.ID;
}
}
Now you can use it in this way:
List<Person> joinedList = peopleList1
.Intersect(peopleList2, new PersonIdComparer())
.ToList();
Both, Enumerable.Join and Enumerable.Intersect are efficient since they are using a set.
Product[] fruits1 = { new Product { Name = "apple", Code = 9 },
new Product { Name = "orange", Code = 4 },
new Product { Name = "lemon", Code = 12 } };
Product[] fruits2 = { new Product { Name = "apple", Code = 9 } };
//Get all the elements from the first array
//except for the elements from the second array.
IEnumerable<Product> except =
fruits1.Except(fruits2);
foreach (var product in except)
Console.WriteLine(product.Name + " " + product.Code);
/*
This code produces the following output:
orange 4
lemon 12
*/
You can use Enumerable.Intersect
var common = peopleList1.Intersect(peopleList2);
You just can use the LINQ Intersect Extension Method.
http://msdn.microsoft.com/en-us/library/bb460136(v=VS.100).aspx
So you would do it like that:
class Program
{
static void Main(string[] args)
{
List<Person> peopleList1 = new List<Person>();
peopleList1.Add(new Person() { ID = 1 });
peopleList1.Add(new Person() { ID = 2 });
peopleList1.Add(new Person() { ID = 3 });
peopleList1.Add(new Person() { ID = 4});
peopleList1.Add(new Person() { ID = 5});
List<Person> peopleList2 = new List<Person>();
peopleList2.Add(new Person() { ID = 1 });
peopleList2.Add(new Person() { ID = 4});
var result = peopleList1.Intersect(peopleList2);
}
}
Just override the Equals Method in the Person-Class. I think you could compare the ids there.
Is there a way to progressively / conditionally add joins to a query? I am creating a custom reporting tool for a client, and the client is given a list of objects he/she can select to query on. There will always be a base object used in the query ("FWOBid").
So, for example, if the customer selects objects "FWOBid", "FWOItem", and "FWOSellingOption", I'd want to do this:
var query = from fb in fwoBids
// if "FWOSellingOption", add this join
join so in sellingOptions on fb.Id equals so.BidId
// if "FWOItem", add this join
join i in fwoItems on fb.Id equals i.FWOBidSection.BidId
// select "FWOBid", "FWOItem", and "FWOSellingOption" (everything user has selected)
select new { FWOBid = fb, FWOSellingOption = so, FWOItem = i };
The trick is the customer can select about 6 objects that are all related to each other, resulting in many different combinations of joins. I'd like to avoid hard coding those if possible.
One option is to do some custom join combined with left joins.
A decent TSQL backend should not get any drawbacks in terms of performance for always using all the joins, since the optimers would just remove the join if the condition is always false. But this should be checked out.
bool joinA = true;
bool joinB = false;
bool joinC = true;
var query = from fb in fwoBids
join so in sellingOptions on new { fb.Id, Select = true } equals new { Id = so.BidId, Select = joinA } into js
from so in js.DefaultIfEmpty()
join i in fwoItems on new { fb.Id, Select = true } equals new { Id = i.FWOBidSection.BidId, Select = joinB } into ji
from i in ji.DefaultIfEmpty()
join c in itemsC on new { fb.Id, Select = true } equals new { Id = c.BidId, Select = joinC }
select new
{
FWOBid = fb,
FWOSellingOption = so,
FWOItem = i,
ItemC = c
};
In the Linq query syntax this is not possible, or looking at the other answers hardly readable. Not much more readable but another possibility would be to use the extension methods (sort of pseudo code):
bool condition1;
bool condition2;
List<Bid> bids = new List<Bid>();
List<SellingOption> sellingOptions = new List<SellingOption>();
List<Item> items = new List<Item>();
var result = bids.Select(x => new {bid = x, sellingOption = (SellingOption)null, item = (Item)null});
if (condition1)
result = result.Join(
sellingOptions,
x => x.bid.Id,
x => x.BidId,
(x, sellingOption) => new { x.bid, sellingOption, item = (Item)null });
if (condition2)
result = result.Join(
items,
x => x.bid.Id,
x => x.BidId,
(x, item) => new { x.bid, x.sellingOption, item });
Just see this as a sort of a concept. It is essentially the same that Peter Duniho did.
The thing is, if you don't want to immediately join on all options if not necessary, then it won't look that nice. Perhaps you should try to join all now and don't worry about performance. Have you ever measured how slow or fast it might be? Think of it as "I don't need it now!". If performance is indeed a problem, then you can act on it. But if it is not, and you won't know if you never tried, then leave it as the six joins you mentioned.
It's hard to provide a really good example solution without a really good example problem. However, what I mean by "chain the queries" is something like this:
var query = from x in dba select new { A = x, B = (B)null, C = (C)null };
if ((joinType & JoinType.B) != 0)
{
query = from x in query
join y in dbb on x.A.Id equals y.Id
select new { A = x.A, B = y, C = x.C };
}
if ((joinType & JoinType.C) != 0)
{
query = from x in query
join y in dbc on x.A.Id equals y.Id
select new { A = x.A, B = x.B, C = y };
}
That is, based on the appropriate condition, query the previous result with another join. Note that to do this successfully, each query must produce the same type. Otherwise, it's not possible to assign a new query to the previous query result variable.
Note that while in the above, I simply have a separate property for each possible input type, I could have instead had the type simply have properties for the input columns, Id, Name, and then the Text properties from the B and C types (which would have to be named differently in the query result type, e.g. TextB and TextC). That would look like this:
var query = from x in dba select new { Id = x.Id, Name = x.Name,
TextB = (string)null, TextC = (string)null };
if ((joinType & JoinType.B) != 0)
{
query = from x in query
join y in dbb on x.Id equals y.Id
select new { Id = x.Id, Name = x.Name, TextB = y.Text, TextC = x.TextC };
}
if ((joinType & JoinType.C) != 0)
{
query = from x in query
join y in dbc on x.Id equals y.Id
select new { Id = x.Id, Name = x.Name, TextB = x.TextB, TextC = y.Text };
}
Here is a complete code example that includes the above logic in a runnable program:
class A
{
public string Name { get; private set; }
public int Id { get; private set; }
public A(string name, int id)
{
Name = name;
Id = id;
}
public override string ToString()
{
return "{" + Name + ", " + Id + "}";
}
}
class B
{
public int Id { get; private set; }
public string Text { get; private set; }
public B(int id, string text)
{
Id = id;
Text = text;
}
public override string ToString()
{
return "{" + Id + ", " + Text + "}";
}
}
class C
{
public int Id { get; private set; }
public string Text { get; private set; }
public C(int id, string text)
{
Id = id;
Text = text;
}
public override string ToString()
{
return "{" + Id + ", " + Text + "}";
}
}
[Flags]
enum JoinType
{
None = 0,
B = 1,
C = 2,
BC = 3
}
class Program
{
static void Main(string[] args)
{
A[] dba =
{
new A("A1", 1),
new A("A2", 2),
new A("A3", 3)
};
B[] dbb =
{
new B(1, "B1"),
new B(2, "B2"),
new B(3, "B3")
};
C[] dbc =
{
new C(1, "C1"),
new C(2, "C2"),
new C(3, "C3")
};
JoinType joinType;
while ((joinType = _PromptJoinType()) != JoinType.None)
{
var query = from x in dba select new { A = x, B = (B)null, C = (C)null };
if ((joinType & JoinType.B) != 0)
{
query = from x in query
join y in dbb on x.A.Id equals y.Id
select new { A = x.A, B = y, C = x.C };
}
if ((joinType & JoinType.C) != 0)
{
query = from x in query
join y in dbc on x.A.Id equals y.Id
select new { A = x.A, B = x.B, C = y };
}
foreach (var item in query)
{
Console.WriteLine(item);
}
Console.WriteLine();
}
}
private static JoinType _PromptJoinType()
{
JoinType? joinType = null;
do
{
Console.Write("Join type ['A' for all, 'B', 'C', or 'N' for none]");
ConsoleKeyInfo key = Console.ReadKey();
Console.WriteLine();
switch (key.Key)
{
case ConsoleKey.A:
joinType = JoinType.BC;
break;
case ConsoleKey.B:
joinType = JoinType.B;
break;
case ConsoleKey.C:
joinType = JoinType.C;
break;
case ConsoleKey.N:
joinType = JoinType.None;
break;
default:
break;
}
} while (joinType == null);
return joinType.Value;
}
}
I hope this is an improvement over previous answers.
public class Bids
{
public int Id { get; set; }
public double Price { get; set; }
}
public class BidSection
{
public int BidId { get; set; }
}
public class SellingOptions
{
public int BidId { get; set; }
public int Quantity { get; set; }
}
public class Item
{
public int ItemId { get; set; }
public BidSection FWOBidSection { get; set; }
}
public class ConditionalJoin
{
public bool jOpt1 { get; set; }
public bool jOpt2 { get; set; }
public ConditionalJoin(bool _joinOption1, bool _joinOption2)
{
jOpt1 = _joinOption1;
jOpt2 = _joinOption2;
}
public class FBandSo
{
public Bids FWOBids { get; set; }
public SellingOptions FWOSellingOptions { get; set; }
}
public class FBandI
{
public Bids FWOBids { get; set; }
public Item FWOItem { get; set; }
}
public void Run()
{
var fwoBids = new List<Bids>();
var sellingOptions = new List<SellingOptions>();
var fwoItems = new List<Item>();
fwoBids.Add(new Bids() { Id = 1, Price = 1.5 });
sellingOptions.Add(new SellingOptions() { BidId = 1, Quantity = 2 });
fwoItems.Add(new Item() { ItemId = 10, FWOBidSection = new BidSection() { BidId = 1 } });
IQueryable<Bids> fb = fwoBids.AsQueryable();
IQueryable<SellingOptions> so = sellingOptions.AsQueryable();
IQueryable<Item> i = fwoItems.AsQueryable();
IQueryable<FBandSo> FBandSo = null;
IQueryable<FBandI> FBandI = null;
if (jOpt1)
{
FBandSo = from f in fb
join s in so on f.Id equals s.BidId
select new FBandSo()
{
FWOBids = f,
FWOSellingOptions = s
};
}
if (jOpt2)
{
FBandI = from f in fb
join y in i on f.Id equals y.FWOBidSection.BidId
select new FBandI()
{
FWOBids = f,
FWOItem = y
};
}
if (jOpt1 && jOpt2)
{
var query = from j1 in FBandSo
join j2 in FBandI
on j1.FWOBids.Id equals j2.FWOItem.FWOBidSection.BidId
select new
{
FWOBids = j1.FWOBids,
FWOSellingOptions = j1.FWOSellingOptions,
FWOItems = j2.FWOItem
};
}
}
}
I'm starting to love Lambda expressions but I'm struggling to pass this wall:
public class CompanyWithEmployees {
public CompanyWithEmployees() { }
public Company CompanyInfo { get; set; }
public List<Person> Employees { get; set; }
}
My search:
List<CompanyWithEmployees> companiesWithEmployees = ws.GetCompaniesWithEmployees();
CompanyWithEmployees ces = companiesWithEmployees
.Find(x => x.Employees
.Find(y => y.PersonID == person.PersonID));
So, I want to get the Object "CompanyWithEmployees" that have that Person (Employee) that I'm looking for, but I'm getting "Cannot implicit convert 'Person' To 'bool')" which is correct, but if I'm not passing the Person object, how can the first Find executes?
Because you want to check for existance, perhaps try:
ces = companiesWithEmployees
.Find(x => x.Employees
.Find(y => y.ParID == person.ParID) != null);
This will check for any Person with the same ParID; if you mean the same Person instance (reference), then Contains should suffice:
ces = companiesWithEmployees
.Find(x => x.Employees.Contains(person));
Find() returns the found object. Use Any() to just check whether the expression is true for any element.
var ces = companiesWithEmployees
.Find(x => x.Employees
.Any(y => y.PersonID == person.PersonID));
ces = companiesWithEmployees
.First(x => x.Employees.Any(p=>p.PersonID == person.PersonID));
ces = companiesWithEmployees.Find( x => x.Employees.Find(...) );
.Find returns only one object, x.Employees.Find(..) returns Person.
.Find expects boolean parameter(i.e. the result of conditions), that's why there's a compiler error that says Cannot implicit convert 'Person' To 'bool'
.Where can return multiple objects, hence can iterate through all list.
use a combination of .Where and .Any in your case.
the following code will illustrate the difference between .Where, .Find, and .Any:
public partial class Form2 : Form {
public Form2() {
InitializeComponent();
var companiesWithEmployees = new List<CompanyWithEmployees>() {
new CompanyWithEmployees {
CompanyInfo = new Company { CompanyName = "Buen" },
Employees = new List<Person>() {
new Person { PersonID = 1976, PersonName = "Michael" },
new Person { PersonID = 1982, PersonName = "Mark" },
new Person { PersonID = 1985, PersonName = "Matthew" },
new Person { PersonID = 1988, PersonName = "Morris" }
}
},
new CompanyWithEmployees {
CompanyInfo = new Company { CompanyName = "Muhlach" },
Employees = new List<Person>() {
new Person { PersonID = 1969, PersonName = "Aga" },
new Person { PersonID = 1971, PersonName = "Nino" },
new Person { PersonID = 1996, PersonName = "Mark" }
}
},
new CompanyWithEmployees {
CompanyInfo = new Company { CompanyName = "Eigenmann" },
Employees = new List<Person>() {
new Person { PersonID = 1956, PersonName = "Michael" },
new Person { PersonID = 1999, PersonName = "Gabby" }
}
}
};
// just explicitly declared the types (instead of var) so the intent is more obvious
IEnumerable<CompanyWithEmployees> whereAreMichaels = companiesWithEmployees
.Where(cx => cx.Employees.Any(px => px.PersonName == "Michael"));
string michaelsCompanies = string.Join(", ", whereAreMichaels
.Select(cx => cx.CompanyInfo.CompanyName).ToArray());
MessageBox.Show("Company(s) with employee Michael : " + michaelsCompanies);
Person findAga = companiesWithEmployees
.Find(company => company.CompanyInfo.CompanyName == "Muhlach")
.Employees.Find(person => person.PersonName == "Aga");
if (findAga != null)
MessageBox.Show("Aga's ID : " + findAga.PersonID.ToString());
}
}
class CompanyWithEmployees {
public Company CompanyInfo { get; set; }
public List<Person> Employees { get; set; }
}
class Company {
public string CompanyName { get; set; }
}
class Person {
public int PersonID { get; set; }
public string PersonName { get; set; }
}
That's because you haven't specified a legitimate Find expression for your top level Find.
I'll show it here:
ces = companiesWithEmployees
.Find (x => x.Employees.Find(y => y.ParID == Person.ParID) /*condition is missing here*/);
So what is the condition for your initial find?
The easiest one would be
ces = companiesWithEmployees.FirstOrDefault(x =>
x.Employees.Any(y => y.PersonID == person.ParID));
without any null check