0

I have a List within a List. I need two compare two properties of the sub_List with a third list.

Classes:

public class Human
{
    public string FirstName{ get; set; }
    public string LastName{ get; set; }
    public List<Clothing> clothings { get; set; }
}

public class Clothing
{
    public string ClothingID{ get; set; }
    public string Garment{ get; set; }
    public string Color{ get; set; }
}

public class CurrentClothes
{
    public string Garment{ get; set; }
    public string Color{ get; set; }
}

Pseudo Code:

public List<Human> humans = new List<Human>()
{
    new Human
    {
        FirstName="Karl",
        LastName="Karlson"
        clothings=new List<Clothing>()
        {
            new Clothing
            {
                ClothingID="1",
                Garment="T-Shirt",
                Color="pink"
            },
            new Clothing
            {
                ClothingID="11",
                Garment="Pant",
                Color="white"
            },
            new Clothing
            {
                ClothingID="111",
                Garment="Shoes",
                Color="black"
            }
        }
    },
    new Human
    {
        FirstName="Paula",
        LastName="Paulson"
        clothings=new List<Clothing>()
        {
            new Clothing
            {
                ClothingID="2",
                Garment="T-Shirt",
                Color="red"
            },
            new Clothing
            {
                ClothingID="22",
                Garment="Pant",
                Color="blue"
            },
            new Clothing
            {
                ClothingID="222"
                Garment="Shoes",
                Color="black"
            }
        }
    }
};
    
public List<CurrentClothes> currentclothes = new List<CurrentClothes>()
{
    new CurrentClothes
    {
        Garment="Pant",
        Color="blue"
    },
    new CurrentClothes
    {
        Garment="T-Shirt",
        Color="red"
    },
    new CurrentClothes
    {
        Garment="Shoes",
        Color="black"
    }
}

var human = humans.Where(x=>x.clothings.Equals(currentClothes));

The question is, how can I compare if the currentclothes matches some human clothes. Is there any Linq Option?

I have added a Example. In this Example there are two humans. Karl and Paula. The current clothes are defind. Now i want the human how matches the currentclothes. In this case Paula.

T0bi
  • 261
  • 1
  • 4
  • 13
  • Can you provide us sample data with expected output? To me, it's actually pretty unclear – Cid Aug 29 '21 at 12:21
  • 1
    is [`Enumerable.Any`](https://learn.microsoft.com/dotnet/api/system.linq.enumerable.any?view=net-5.0) what you are looking for? – Cid Aug 29 '21 at 12:22
  • What do you call "compare" how do you define if they are equal? Perhaps you should implement `IEquatable` – Charlieface Aug 29 '21 at 12:25
  • You should override the Equals method for your Clothing class. See the docs here and you will know how to compare it with CurrentClothes. https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=net-5.0 .In that way you can redefine your criteria on how this 2 classes should be compare all the time and define your equal criteria – Zinov Aug 29 '21 at 12:33
  • @Zinov can you provide me a code example along my example how to override the equal methode and use ist to get the correct human from the list? – T0bi Aug 29 '21 at 12:46
  • I will, I am not in front of the computer now, but I will try it later today – Zinov Aug 29 '21 at 21:49
  • @Zinov guess the answer by rand random catches your idea with override the equal methode but thank you too – T0bi Aug 29 '21 at 22:41

2 Answers2

2

I would do the following

public class Human
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public List<Clothing> clothings { get; set; }
}

public class Clothing : CurrentClothes
{
    public string ClothingID { get; set; }
}

public class CurrentClothes : IEquatable<CurrentClothes>
{
    public string Garment { get; set; }
    public string Color { get; set; }

    public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is CurrentClothes other && Equals(other);
    public bool Equals(CurrentClothes other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Garment == other.Garment && Color == other.Color;
    }
    public override int GetHashCode() => HashCode.Combine(Garment, Color);
}

And than use it like this:

var currentclothesAsHashSet = currentclothes.ToHashSet();
var human = humans.Where(x => x.clothings.OfType<CurrentClothes>().ToHashSet().SetEquals(currentclothesAsHashSet));
foreach (var human1 in human)
{
    Console.WriteLine(human1.FirstName);
}

This uses the method of

https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.iset-1.setequals

To look if Human.clothings is identical to currentclothes.

Here a demo: https://dotnetfiddle.net/tS72Wt

Edit:

As was pointed out in the comment, if currentClothes could only be a subset of data, you may want to change SetEquals to Except.

Like this demos demonstrates:

https://dotnetfiddle.net/FZg85Z

Rand Random
  • 7,300
  • 10
  • 40
  • 88
  • just a small comment for @T0bi, its not clear from the question of the current clothes are exactly the same as the Human is wearing, if not and the current cloths may specify more cloths than actual, you can use IsSupersetOf instead of SetEquals like this: ... var human = humans.Where(x =>currentClothesAsHashSet.IsSupersetOf(x.clothings.OfType().ToHashSet())); – Dor Lugasi-Gal Aug 29 '21 at 13:05
  • @DorLugasi-Gal - contains would be wrong, because "Shoes / black" are contained in both the human Frank and Paula, and only Paula should be a positive result. - The only thing that I could see is currentClothes only be a subset of data eg. only having 2 entries but the human has 3 entries of clothing than `SetEquals` would be wrong and it would need to change to `!subset.Except(superset).Any()` - like explained here: https://stackoverflow.com/questions/407729/determine-if-a-sequence-contains-all-elements-of-another-sequence-using-linq – Rand Random Aug 29 '21 at 13:12
  • @DorLugasi-Gal, yes there could be more e.g (example + new clothes "Head") , then it is not matching. – T0bi Aug 29 '21 at 20:16
  • Did I understand correctly that if I inherit something I can change the same method that only the inherited is compared? – T0bi Aug 29 '21 at 20:28
  • @RandRandom thank you for your answer and your example provided. I checked the https://dotnetfiddle.net/FZg85Z link and i found out when i removed a currentclothing object it also shows paula. I will give it a try now and hope i can find a solutio for my problem. – T0bi Aug 29 '21 at 20:31
  • @T0bi - well the examples provided are either or, either you want currentclothes to be exactly the same or you only want a subset of data to be the same, if you had removed an entry of currentclothes in the first example paula wouldn't have shown up, since it needs to be exactly the same, we weren't sure what you wanted, so we discussed and provided solutions for a subset to be the same – Rand Random Aug 30 '21 at 09:32
  • @RandRandom, i tried your solution and it worked like a charm. Tbh i'm at the edge of my programming skills. thanks for your help. – T0bi Aug 30 '21 at 10:39
0

You can use Any like this:

    humans.clothings.Where(x => 
       currentClothes.Any(s => 
        s.Color.Equals(x.Color) && s.Garment.Equals(x.Garment)
       )
    ).ToList();

Check this for .netFiddle demo

Batuhan
  • 1,521
  • 12
  • 29
  • Doesn't seem to meet the requirement since you are returning a list of clothing instead of the humans. and it doesn't seem like `Any` would be correct, more like only return return the human which matches `ALL` the clothes – Rand Random Aug 29 '21 at 12:41
  • I'm aware , I will update this @RandRandom – Batuhan Aug 29 '21 at 12:50