简体   繁体   中英

C# linq with two Lists

I'm stuck at the following Problem: I want to load one List, but I can pass more than one Parameter what could be the criteria to find the Lists I want. Now I have the following structure:

House{
  Name;
  ID;
  Alias;
}

Also I Have:

Person{
  Name;
  Alias;
}

This means, 1 house can hold multiple persons and multiple persons with the same name can be in multiple houses. So now I want to call my Function eG "GetHouses(string criteria)" as criteria could be:

  1. a Name of a house
  2. an ID of a house
  3. a part of the name of the house
  4. a Name of one of the Persons in the house

Now I just read every house and it's data and I select afterwards by the criteria.

IMPORTANT NOTE: I can not change the logic until here!

So now as I try to find the matching criteria I came up with using LINQ as it is very fast. It works as long as I don't compare to the Persons:

result = (from x in result
where (
  (string.Equals(x.Name, criteria))
  || (string.Equals(x.ID, criteria))
  || (x.Name.Contains(criteria))
  select x).ToList();

Now I want to load every Person to the houses I found and check if a Name of the Persons in the house would match the criteria. Is there a way to do this within the LINQ I have already? Or do I have to go though the result with:

result.ForEach(x => ...)

Would be nice if it would work with the LINQ. I did a similar Logic with the

result.FindAll(new Predicate<House>((x) => { ... LoadPersons(criteria) {... } }));

But that took to long.

Kind regards, Asat0r

Presuming that you have a PersonList in your House -class you could use Enumerable.Any :

var matchingHouses = from house in allHouses
    where string.Equals(house.Name, criteria) 
       || string.Equals(house.ID, criteria) 
       || house.Name.Contains(criteria)
       || house.PersonList.Any(resident => string.Equals(resident.Name, criteria))
    select house;

If you have a method to return the "residents" you can use this. If you later want to access these persons you could create an anonymous type to store them:

var housesAndResidents = from house in allHouses
    let matchingResidentList = LoadPersons(house.ID)
       .Where(resident => string.Equals(resident.Name, criteria))
       .ToList()
    where string.Equals(house.Name, criteria) 
       || string.Equals(house.ID, criteria) 
       || house.Name.Contains(criteria)
       || matchingResidentList.Any()
    select new { house, matchingResidentList };

You can access these properties in the following way:

var matchingHouseList = housesAndResidents.ToList(); 
// you don't need the list, you can use foreach directly,
// but whenever you access housesAndResidents you will execute that query
// ToList materializes this query into a collection, so you can enumerate it or use the Count property
foreach(var x in matchingHouseList )
{
    Console.WriteLine("House:{0} Matching-Resident(s):{1}"
        , x.house.Name
        , String.Join(", ", x.matchingResidentList.Select(r => r.Name)));
}

Here is a simple solution using three classes: Program , House and Person .

The class House "houses" (pun intended) a list of the class Person (and your own values: name, id and alias). In this way the residents are a part of the house and not stored somewhere else. Storing the residents outside the House class is the same as having your lunchbox's contents outside the box in your backpack.

The class Person stores the basic information about the house's residents. Having a list of residents inside the House class makes it easier to compare the search criteria with the people.

In the class Program you'll find the Main and the FindHouses classes. These kinda explains themselves

class Person
{
    public string name;
    public string alias;

    public Person(string _name, string _alias = "")
    {
        name = _name;
        alias = _alias;
    }
}

class House
{
    public string name;
    public string id;
    public string alias;
    public List<Person> people = new List<Person>();

    public House(string _name, string _id, string _alias = "")
    {
        name = _name;
        id = _id;
        alias = _alias;
    }
}

class Program
{
    static List<House> houses = new List<House>();

    static void Main(string[] args)
    {
        // Add houses here

        foreach (House house in FindHouses("criteria"))
        {
            // Do stuff
        }
    }

    // Find all the houses in which the criteria exists
    static List<House> FindHouses(string criteria)
    {
        // Return the list of all houses in which the criteria exists
        return houses.Where(h =>
            h.name.Contains(criteria) ||
            h.id == criteria ||
            h.alias.Contains(criteria) ||

            h.people.Any(p => 
            p.name.Contains(criteria) ||
            p.alias.Contains(criteria))).ToList();
    }
}

I dont know if this is too big of a change in your code, but anyways, I hope this helps.

I know you mentioned you didn't want to load the persons into the houses, but this makes the search for houses based on "all values" much easier

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM